[React/TS] MUI DataGrid로 수정 가능한 테이블 만들기
안녕하세요. 황진성입니다.
이번 글에서는 React.js와 TypeScript를 활용해서 수정 가능한 테이블을 만들어보려 합니다. 구글의 Material UI Design을 구현한 라이브러리인 MUI를 활용해 볼 예정이며, MUI의 DataGrid 컴포넌트로 테이블을 생성할 수 있습니다.
이번에 다룰 내용이 간단하지만 블로깅 하는 이유는 공식 문서에 제가 원하는 방식이 설명되어 있지 않아서 많이 애먹었기 때문입니다... MUI DataGrid를 사용하시는 분들 중 저와 비슷한 기능을 만드시는 분들은 시간 낭비를 하시지 않았으면 좋겠습니다.
목표
React 프로젝트 생성부터 DataGrid로 테이블을 생성하고 변경된 값으로 상태를 변경하는 것까지 해보겠습니다.
프로젝트 생성 스크립트
$ npx create-react-app mui-datagrid --template typescript
적당한 위치에 프로젝트를 생성합니다. 저는 프로젝트의 이름을 `mui-datagrid` 로 설정했습니다.
의존성 추가
"dependencies": {
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@mui/x-data-grid": "^6.18.1",
...
},
package.json 파일에 MUI DataGrid, Styled compoent 관련 의존성을 추가해 줍니다. 작성일 기준 DataGrid 최신 버전은 6.18.1 입니다.
DataGrid 컴포넌트 생성
const App = () => {
return (
<div className="App">
<DataGrid columns={} rows={}/>
</div>
);
}
간단한 실습 용도이니 App.tsx의 App component에 DataGrid를 만들어 보겠습니다.
아무 값이나 넣어보자
const initialData = [
{"name": "jinseong", "age": 20},
{"name": "gildong", "age": 30},
{"name": "cheolsu", "age": 40},
]
const App = () => {
const columns: GridColDef[] = [
{field: 'name', headerName: 'Name', width: 150},
{field: 'age', headerName: 'Age', editable: true},
];
const rows: GridRowsProp = initialData.map((data, idx) => ({
id: idx, ...data
}));
return (
<div className="App">
<DataGrid
sx={{"width": "500px", "margin": "auto"}}
columns={columns}
rows={rows}
/>
</div>
);
}
필수적으로 columns와 rows에 값을 설정해 줘야 합니다.
columns
- 컬럼에 대한 설정을 합니다.
- `field`는 데이터 접근 시 이름이고, `headerName`은 테이블에 노출되는 컬럼명입니다.
- `editable`의 기본값은 false인데, true로 설정해 주면 테이블 셀을 더블클릭해서 수정할 수 있습니다.
rows
- 표에 채워지는 데이터입니다.
- `id` 필드가 꼭 필요해서 map index 값을 넣어줬습니다.
State 연결 및 수정하기
type TableRow = {
"name": string,
"age": number,
}
const initialData: TableRow[] = [
{"name": "jinseong", "age": 20},
{"name": "gildong", "age": 30},
{"name": "cheolsu", "age": 40},
]
const App = () => {
const [data, setData] = useState<TableRow[]>(initialData)
const rows: GridRowsProp = data.map((row, idx) => ({
id: idx, ...row
})); // rows를 기반으로 테이블이 채워짐
// ...
}
State를 관리 편의를 위해 TableRow라는 type을 하나 만들어 줬습니다.
Data라는 state를 하나 만들었습니다. 그리고 data를 기반으로 테이블이 채워집니다. 아직 렌더링 결과는 동일합니다.
return (
<div className="App">
<DataGrid
sx={{"width": "500px", "margin": "auto"}}
columns={columns}
rows={rows}
onStateChange={e => console.log(e)}
/>
</div>
);
그리고 DataGrid 컴포넌트에 onStateChange 프롭 값을 채워줍니다. 일단 console logging을 해봅시다.
셀을 선택하고 조금 수정을 해봤는데 뭔가 촤르륵 많이 나옵니다! Click, Cell edit start, Cell edit end, Cell focus out 등 여러 이벤트들이 모두 로깅되는 것 같았습니다. 이제 변경된 값으로 Data State 변경을 해봅시다.
로깅된 이벤트 메시지를 잘 살펴보니 이벤트마다 editRows에 들어가는 값이 다르다는 것을 알게 됐습니다.
- editRows가 {} 라면,
- 셀이 수정되지 않았다. 즉, 값의 수정과 관련이 없는 클릭 이벤트 등이 발생한 것이다.
- editRows가 { ... } 라면,
- 셀이 수정됐다.
type CellEditProps = {
"rowId": string,
"colName": string,
"value": string,
}
const App = () => {
// ...
const parseCellEditProps = (e: any): CellEditProps | null => {
const editingRowId = Object.keys(e.editRows)[0]
if (editingRowId === undefined) return null;
const editingColName = Object.keys(e.editRows[editingRowId])[0]
const editingValue = e.editRows[editingRowId][editingColName].value
return {
"rowId": editingRowId,
"colName": editingColName,
"value": editingValue,
}
}
const handleEditEvent = (e: any) => {
const editProps = parseCellEditProps(e);
if (editProps === null) return;
setData(prevState => {
return prevState.map((row, idx) => {
if (idx.toString() === editProps.rowId) {
return {...row, [editProps.colName]: editProps.value};
}
return row;
});
});
};
return (
<div className="App">
<DataGrid
sx={{"width": "500px", "margin": "auto"}}
columns={columns}
rows={rows}
onStateChange={handleEditEvent} // 여기 추가!
/>
</div>
);
}
Cell 수정에 필요한 값 관리 편의를 위해 CellEditProps라는 type을 하나 만들어 줬습니다.
이벤트 객체에서 수정에 필요한 데이터를 뽑는 parseCellEditProps 함수를 만들고, 핸들러 함수를 만들었습니다.
이제 정상적으로 값이 수정되며, state 값도 변경되는 것을 확인할 수 있습니다!
2023-11-20 추가
Maximum update depth exceeded 에러가 발생한다면 다음 글을 참고해 주세요!
[React] Maximum update depth exceeded 에러를 해결한 방법
맺음말
제가 프론트엔드 개발 경험이 적어서 삽질을 많이 했는데 아직도 많이 부족합니다. 피드백 언제나 환영합니다! 감사합니다.
프로젝트에 사용된 전체 코드는 아래에서 확인하실 수 있습니다.
https://github.com/JinseongHwang/react-playground/tree/main/mui-datagrid