[MUI/X-Data-Grid] Server-Side Data
[MUI/X-Data-Grid] Server-Side Data

[MUI/X-Data-Grid] Server-Side Data

Published
November 17, 2023
Author
WonJung Kim

서론

@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(); }