Intro
์๋
ํ์ธ์. ์ด๋ฒ ์๊ฐ์๋ react-virtuoso ๊ฐ ๋ฌด์์ด๋ฉฐ, react virtuoso ๋ฅผ ์ฌ์ฉํ์ฌ react table ๋ ๋๋ง ์ต์ ํํ๋ ๋ฐฉ๋ฒ์ ๋ํด ์์๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
Issue
mui react table์ ์ฌ์ฉํ๋ ๋์ค ๋ฐ์ดํฐ ์์ด ๋ง์์ง๋ฉด์ ํ
์ด๋ธ์ด ๊ทธ๋ ค์ง๋ ์๋๊ฐ ๋๋ ค์ ธ ์๋น์ค ์ด์ฉ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์์ต๋๋ค. ๋ฐ๋ผ์ react table์ ์ฑ๋ฅ์ ๊ฐ์ ํ๊ธฐ ์ํด ๊ฐ์ ๋ฐฉ์์ ์ฐพ์์ผ ํ์ต๋๋ค.
๊ด๋ จ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์์นํ๋ ๋์ค TanStack ์, mui ๊ณต์ ํํ์ด์ง์ Virtualized table ๋ถ๋ถ์ ์๊ฐ๋์ด ์๋ react-virtuoso ๋๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ฐ๊ฒฌํ์์ต๋๋ค. star์ ์ ์์ฒด๋ TanStack์ด ๋ ๋์์ง๋ง ์ฌ์ฉ๋ฐฉ๋ฒ์ด ๋ ์ฝ๊ณ , ๊ธฐ์กด ํ๋ก์ ํธ์ ์ฉ์ดํ๊ฒ ๋ถ์ผ ์ ์๋ react-virtuoso ๋ฅผ ์ฌ์ฉํ๊ธฐ๋ก ๊ฒฐ์ ํ์์ต๋๋ค.
react-virtuoso
react virtuoso๋ react ์ ํ๋ฆฌ์ผ์ด์ ์์ ๋์ฉ๋ list, table, grid ๋ฑ์ ํจ์จ์ ์ผ๋ก ๋ ๋๋งํ๊ธฐ ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค. ์ฃผ๋ก ๊ฐ์ํ (virtualization) ๋๋ ์๋์(windowing) ๊ธฐ์ ์ ์ฌ์ฉํ์ฌ ํ๋ฉด์ ๋ณด์ด๋ ํญ๋ชฉ๋ง ๋ ๋๋งํ์ฌ ์ฑ๋ฅ์ ์ต์ ํํฉ๋๋ค.
react-virtuoso example
react virtuoso ์์๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
import * as React from 'react';
import Table from '@mui/material/Table';
import TableBody from '@mui/material/TableBody';
import TableCell from '@mui/material/TableCell';
import TableContainer from '@mui/material/TableContainer';
import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import Paper from '@mui/material/Paper';
import { TableVirtuoso, TableComponents } from 'react-virtuoso';
interface Data {
calories: number;
carbs: number;
dessert: string;
fat: number;
id: number;
protein: number;
}
interface ColumnData {
dataKey: keyof Data;
label: string;
numeric?: boolean;
width: number;
}
type Sample = [string, number, number, number, number];
const sample: readonly Sample[] = [
['Frozen yoghurt', 159, 6.0, 24, 4.0],
['Ice cream sandwich', 237, 9.0, 37, 4.3],
['Eclair', 262, 16.0, 24, 6.0],
['Cupcake', 305, 3.7, 67, 4.3],
['Gingerbread', 356, 16.0, 49, 3.9],
];
function createData(
id: number,
dessert: string,
calories: number,
fat: number,
carbs: number,
protein: number,
): Data {
return { id, dessert, calories, fat, carbs, protein };
}
const columns: ColumnData[] = [
{
width: 200,
label: 'Dessert',
dataKey: 'dessert',
},
{
width: 120,
label: 'Calories\u00A0(g)',
dataKey: 'calories',
numeric: true,
},
{
width: 120,
label: 'Fat\u00A0(g)',
dataKey: 'fat',
numeric: true,
},
{
width: 120,
label: 'Carbs\u00A0(g)',
dataKey: 'carbs',
numeric: true,
},
{
width: 120,
label: 'Protein\u00A0(g)',
dataKey: 'protein',
numeric: true,
},
];
const rows: Data[] = Array.from({ length: 200 }, (_, index) => {
const randomSelection = sample[Math.floor(Math.random() * sample.length)];
return createData(index, ...randomSelection);
});
const VirtuosoTableComponents: TableComponents<Data> = {
Scroller: React.forwardRef<HTMLDivElement>((props, ref) => (
<TableContainer component={Paper} {...props} ref={ref} />
)),
Table: (props) => (
<Table {...props} sx={{ borderCollapse: 'separate', tableLayout: 'fixed' }} />
),
TableHead: React.forwardRef<HTMLTableSectionElement>((props, ref) => (
<TableHead {...props} ref={ref} />
)),
TableRow,
TableBody: React.forwardRef<HTMLTableSectionElement>((props, ref) => (
<TableBody {...props} ref={ref} />
)),
};
function fixedHeaderContent() {
return (
<TableRow>
{columns.map((column) => (
<TableCell
key={column.dataKey}
variant="head"
align={column.numeric || false ? 'right' : 'left'}
style={{ width: column.width }}
sx={{
backgroundColor: 'background.paper',
}}
>
{column.label}
</TableCell>
))}
</TableRow>
);
}
function rowContent(_index: number, row: Data) {
return (
<React.Fragment>
{columns.map((column) => (
<TableCell
key={column.dataKey}
align={column.numeric || false ? 'right' : 'left'}
>
{row[column.dataKey]}
</TableCell>
))}
</React.Fragment>
);
}
export default function ReactVirtualizedTable() {
return (
<Paper style={{ height: 400, width: '100%' }}>
<TableVirtuoso
data={rows}
components={VirtuosoTableComponents}
fixedHeaderContent={fixedHeaderContent}
itemContent={rowContent}
/>
</Paper>
);
}
TableVirtuoso ์ปดํฌ๋ํธ ์์๋ก Paper ์ปดํฌ๋ํธ๋ก ๊ฐ์ผ ํ, ๊ธฐ๋ณธ์ ์ผ๋ก height๋ฅผ ์ง์ ํด์ค์ผ ํฉ๋๋ค. height๋ฅผ ์ง์ ํ์ง ์์ ๊ฒฝ์ฐ ํ๋ฉด์ ๋ํ๋์ง ์๋๋ผ๊ณ ์. ๊ผญ Paper ์ปดํฌ๋ํธ๊ฐ ์๋๋๋ผ๋ Box ์ปดํฌ๋ํธ๋ div ํ๊ทธ๋ ๊ฐ๋ฅํฉ๋๋ค. ๊ฐ์ธ์ค ํ๊ทธ๋ฅผ ์ง์ ํด์ฃผ๋ฉด ๋ฉ๋๋ค.
์ดํ data prop์๋ rows ๋ฐ์ดํฐ๋ฅผ ๋ฃ์ด์ฃผ๊ณ , components props์๋ ์ ์๋ VirtuosoTablecomponents๋ฅผ ๋ฃ์ด์ค๋๋ค. ์ดํ fixedHeaderContent, itemContent props๋ฅผ ํ์ฉํ์ฌ headers์ rowContent ๋ฅผ ์ง์ ํด์ค๋๋ค.
๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ๊ณ , ๋ ๋ํ
์ผํ ๊ธฐ๋ฅ๋ค์ ์ฟ์ฉํ๊ธฐ ์ํด์๋ ๊ณต์ ํํ์ด์ง๋ฅผ ์ฐธ๊ณ ํ์๊ธธ ๋ฐ๋์!
https://tanstack.com/
https://mui.com/material-ui/react-table/#virtualized-table