서론
@mui/x-data-grid
를 사용하여 프로젝트를 진행하는 도중, 데이터 양이 방대하여 클라이언트가 hang이 되는 이슈로 Server-side Pagination가 필요한 경우가 생겼다. 이 과정에서 Filter, Sort까지 같이 해결해보았다.MUI
문서
Type | Link |
Server Side Data | |
Pagination | |
Sorting | |
Filter |
각 문서들을 참고해보는 것이 좋다.
Example
import { useState } from 'react'; import { DataGrid, koKR } from "@mui/x-data-grid"; const columns = [ { field: "id", headerName: "ID" }, { field: "text", headerName: "Text" }, { field: "number", headerName: "Number" }, ]; function DataGrid() { const [state, setState] = useState({ rows: [], total: 0, }); const [loading, setLoading] = useState(true); const [rows, setRows] = React.useState([]); const [paginationModel, setPaginationModel] = React.useState({ page: 0, pageSize: 10, }); const [filterModel, setFilterModel] = React.useState({ items: [], }); const [sortModel, setSortModel] = React.useState([]); useEffect(() => { const fetchData = async () => { setLoading(true); const data = await fetch(`http://localhost:4096/get`, { method: "POST", body: { page: paginationModel.page, pageSize: paginationModel.pageSize, sortModel, filterModel, }, }).then((response) => response.json()); setState({ rows: data.rows, total: data.total, }); setLoading(false); }; fetchData(); }, [paginationModel, sortModel, filterModel]); return ( <DataGrid // Default columns={columns} rows={state.rows} rowCount={state.total} loading={loading} // Pagination paginationMode='server' onPaginationModelChange={setPaginationModel} // Filter filterMode='server' onFilterModelChange={setFilterModel} // Sort sortingMode='server' onSortModelChange={setSortModel} // Etc rowsPerPageOptions={[25, 50, 100]} pagination localeText={koKR.components.MuiDataGrid.defaultProps.localeText} /> ); } export default DataGrid;
주의
- Server-Side로 처리할 경우, rowsPerPage가 100 이상의 값이 오는 경우 Error가 표출된다.
- 해당 사항은 원래 Pro 이상만 가능하다고 되어있으나, Client Side로만 처리할 경우 표출되지 않는 오류가 있는 것으로 보인다.
Server
DTO
만약 위의 형식대로 구현했다면 아래 DTO 형식으로 데이터가 구성된다.
export class QueryDto { page: number; pageSize: number; filter?: FilterDto; sort?: SortDto[]; } export class FilterDto { items?: FilterItemDto[]; linkOperator?: 'and' | 'or'; quickFilterValues?: FilterItemDto[]; quickFilterLogicOperator?: 'and' | 'or'; } export class FilterItemDto { columnField: string; operatorValue: | '=' // Number | '!=' | '>' | '>=' | '<' | '<=' | 'contains' // Common | 'equals' | 'startsWith' | 'endsWith' | 'isEmpty' | 'isNotEmpty' | 'isAnyOf' | 'is' // Date, Selectable | 'not' | 'after' | 'onOrAfter' | 'before' | 'onOrBefore'; id: number; value: string; } export class SortDto { field: string; sort: 'asc' | 'desc'; }
문서
Type | Link |
pagenationModel | |
GridFilterModel | |
GridFilterItem | |
logicOperator |
해당 데이터 중 전체
operatorValue
는 공식 문서에서 목록을 찾지 못하여 github.com/SpaceInvaderTech/typeorm-mui-query
를 일부 참조했다. (Source)Query
해당 코드의 길이가 너무 길어져 본문에서는 생략했다. 외부 라이브러리(
github.com/SpaceInvaderTech/typeorm-mui-query
) 코드를 참조바란다.추가적으로 해당 라이브러리는 PostgreSQL에 맞춰져 있으므로, MySQL이나 MariaDB에서 사용 시 일부 쿼리 문의 변경이 필요하다.
findByQuery(queryDto) { const query = this.repository.createQueryBuilder("entity"); // Filter 내용 스킵. // Order queryDto.sort?.forEach((sortItem) => { const field = sortItem.field; const sort: "ASC" | "DESC" = sortItem.sort.toUpperCase() as | "ASC" | "DESC"; const key = `entity.${field}`; query.addOrderBy(key, sort); }); // Pagination query.limit(queryDto.pageSize).offset(queryDto.pageSize * queryDto.page); return query.getManyAndCount(); }