본문으로 건너뛰기

예제 — @topgrid/grid 사용 패턴

자주 쓰는 Grid 패턴을 실제 동작하는 코드로 정리했다. 그대로 복사해 React 프로젝트에 붙여 실행할 수 있다. 인터랙티브 데모는 Storybook 데모 에서 실행해 볼 수 있다(전 패키지 ~30개 스토리).

pnpm add @topgrid/grid-core @tanstack/react-table
# 변경 추적 예제는 추가로:
pnpm add @topgrid/grid-pro-tracking @topgrid/grid-license
# 서버사이드 / 스프레드시트 예제는 추가로:
pnpm add @topgrid/grid-pro-serverside @topgrid/grid-pro-sheet @tanstack/react-virtual

예제 1: 기본 Grid

클라이언트 페이지네이션 + 다중 행 선택 + 정렬.

import { Grid } from '@topgrid/grid-core';
import { type ColumnDef } from '@tanstack/react-table';

type Employee = {
id: string;
name: string;
department: string;
salary: number;
};

const data: Employee[] = [
{ id: 'E001', name: '김철수', department: '개발팀', salary: 5500000 },
{ id: 'E002', name: '이영희', department: '인사팀', salary: 4800000 },
{ id: 'E003', name: '박민준', department: '회계팀', salary: 5200000 },
{ id: 'E004', name: '정수연', department: '개발팀', salary: 6000000 },
{ id: 'E005', name: '최동현', department: '영업팀', salary: 4500000 },
];

const columns: ColumnDef<Employee>[] = [
{ accessorKey: 'id', header: '사원번호', size: 100 },
{ accessorKey: 'name', header: '성명', size: 120 },
{ accessorKey: 'department', header: '부서', size: 120 },
{
accessorKey: 'salary',
header: '급여',
size: 150,
cell: ({ getValue }) =>
new Intl.NumberFormat('ko-KR', { style: 'currency', currency: 'KRW' })
.format(getValue() as number),
},
];

export default function App() {
return (
<Grid
data={data}
columns={columns}
enableSort
enablePagination
pagination={{ pageSize: 3 }}
rowSelection="multi"
emptyText="조회 결과가 없습니다."
/>
);
}

예제 2: 가상화 Grid (대용량)

enableVirtualization 하나로 대용량 행을 가상화 렌더링한다(보이는 행만 DOM 유지).

import { Grid } from '@topgrid/grid-core';
import { type ColumnDef } from '@tanstack/react-table';

type Row = { id: number; item: string; category: string; value: number };

// 1,000행 생성
const data: Row[] = Array.from({ length: 1000 }, (_, i) => ({
id: i + 1,
item: `항목_${i + 1}`,
category: `분류_${(i % 10) + 1}`,
value: (i * 137) % 100000,
}));

const columns: ColumnDef<Row>[] = [
{ accessorKey: 'id', header: '#', size: 60 },
{ accessorKey: 'item', header: '항목', size: 200 },
{ accessorKey: 'category', header: '분류', size: 150 },
{
accessorKey: 'value',
header: '값',
size: 120,
cell: ({ getValue }) =>
new Intl.NumberFormat('ko-KR').format(getValue() as number),
},
];

export default function App() {
return (
<Grid
data={data}
columns={columns}
enableVirtualization
enableSort
emptyText="데이터가 없습니다."
/>
);
}

예제 3: 변경 추적 (useChangeTracking)

행 단위 추가/수정/삭제를 추적하고, 서버 전송용 변경 집합(getChangeSet)을 만든다. Pro 패키지이므로 라이선스 키 설정이 필요하다(미설정 시 워터마크 표시).

import { Grid } from '@topgrid/grid-core';
import { useChangeTracking } from '@topgrid/grid-pro-tracking';
import { setLicenseKey } from '@topgrid/grid-license';
import { type ColumnDef } from '@tanstack/react-table';

setLicenseKey('YOUR-LICENSE-KEY'); // 앱 시작 시 1회

type Product = { id: string; name: string; price: number; stock: number };

const initialData: Product[] = [
{ id: 'P001', name: '노트북', price: 1200000, stock: 50 },
{ id: 'P002', name: '마우스', price: 35000, stock: 200 },
{ id: 'P003', name: '키보드', price: 120000, stock: 150 },
];

const columns: ColumnDef<Product>[] = [
{ accessorKey: 'id', header: '상품코드', size: 100 },
{ accessorKey: 'name', header: '상품명', size: 180 },
{ accessorKey: 'price', header: '단가', size: 120 },
{ accessorKey: 'stock', header: '재고', size: 100 },
];

export default function App() {
// data + rowKey(필수) 로 추적 시작
const tracking = useChangeTracking<Product>({
data: initialData,
rowKey: 'id',
});

return (
<div>
<div style={{ display: 'flex', gap: 8, marginBottom: 12 }}>
<button
onClick={() =>
tracking.addRow({ id: `P${Date.now()}`, name: '신규상품', price: 0, stock: 0 })
}
>
행 추가
</button>
<button onClick={() => tracking.updateRow('P001', { price: 1100000 })}>
P001 단가 수정
</button>
<button onClick={() => tracking.deleteRow('P002')}>P002 삭제</button>
<button onClick={() => console.log(tracking.getChangeSet())}>
변경 집합 출력
</button>
<button onClick={() => tracking.resetChanges()}>초기화</button>
</div>

<Grid data={tracking.rows} columns={columns} />

<div style={{ marginTop: 12, fontSize: 14, color: '#555' }}>
추가 {tracking.added.length}건 · 수정 {tracking.edited.length}건 · 삭제 {tracking.deleted.length}
</div>
</div>
);
}

useChangeTracking 반환값:

멤버설명
rows추적이 반영된 현재 행 배열 (<Grid data>에 전달)
added / edited / deleted변경된 행 목록
addRow(seed)행 추가 — 할당된 키를 동기 반환
updateRow(key, patch)부분 수정
deleteRow(key)삭제
getChangeSet()서버 전송용 변경 집합 생성
resetChanges()추적 초기화

예제 4: 컬럼 가상화 (대량 컬럼)

enableColumnVirtualization — 화면 밖 컬럼은 렌더하지 않아 100+ 컬럼 그리드의 렌더 비용을 줄인다. 핀 컬럼은 가로 스크롤과 무관하게 항상 렌더된다. 세로(enableVirtualization)와 함께 쓰면 행·열 동시 가상화.

import { Grid, createColumns } from '@topgrid/grid-core';

type Row = Record<string, string>;

// 50개 컬럼
const columns = createColumns<Row>(
Array.from({ length: 50 }, (_, i) => ({
id: `c${i}`, name: `컬럼 ${i}`, type: 'text', width: '140',
})),
);

const data: Row[] = Array.from({ length: 200 }, (_, r) => {
const row: Row = {};
for (let i = 0; i < 50; i++) row[`c${i}`] = `${r}-${i}`;
return row;
});

export default function App() {
return (
<Grid
data={data}
columns={columns}
enableColumnVirtualization // 가로 가상화(opt-in)
enableColumnPinning
defaultColumnPinning={{ left: ['c0'], right: ['c49'] }} // 핀 컬럼은 항상 렌더
enableVirtualization // 세로도 함께
virtualScrollHeight={400}
/>
);
}

예제 5: 서버사이드 무한 스크롤 (SSRM)

@topgrid/grid-pro-serverside — 대용량 서버 데이터를 블록 단위로 lazy 로드한다. 스크롤하면 보이는 블록만 요청(1회/블록), 정렬/필터는 서버 파라미터로 전달된다. Pro 패키지(라이선스 필요).

import { Grid } from '@topgrid/grid-core';
import { useServerSideData } from '@topgrid/grid-pro-serverside';
import type { ServerSideDatasource } from '@topgrid/grid-pro-serverside';
import { setLicenseKey } from '@topgrid/grid-license';
import { type ColumnDef } from '@tanstack/react-table';

setLicenseKey('YOUR-LICENSE-KEY');

type Row = { id: number; name: string; amount: number };

// datasource — 실제로는 서버 호출. getRows 가 블록 요청을 받는다.
const datasource: ServerSideDatasource<Row> = {
async getRows({ startRow, endRow, sortModel, filterModel }) {
const res = await fetch('/api/rows', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ startRow, endRow, sortModel, filterModel }),
});
const { rows, lastRow } = await res.json();
// lastRow = 끝에 도달했을 때의 전체 건수, 더 있으면 undefined
return { rows, lastRow };
},
};

const columns: ColumnDef<Row>[] = [
{ accessorKey: 'id', header: '#', size: 80 },
{ accessorKey: 'name', header: '이름', size: 200 },
{ accessorKey: 'amount', header: '금액', size: 150 },
];

export default function App() {
const { gridProps } = useServerSideData<Row>(datasource, {
blockSize: 100, // 100행 단위 블록 lazy 로드
rowCount: 100_000, // 초기 추정 총건수(lastRow 로 보정)
});
// gridProps = data(placeholder 배열) + enableVirtualization + manualSorting/Filtering
// + onSortingChange/onColumnFiltersChange + virtualizerOptions
return <Grid columns={columns} {...gridProps} virtualScrollHeight={500} />;
}

정렬/필터가 바뀌면 진행 중이던 이전 쿼리 응답은 자동 폐기된다(epoch 불변식 — 빠른 정렬 토글 시 행 순서 손상 방지). lazy 그룹은 useServerSideTree 참고.


예제 6: 스프레드시트 (수식 셀)

@topgrid/grid-pro-sheet — A1 참조 + 사칙연산 + SUM/AVERAGE/MIN/MAX/COUNT 수식. 셀은 수식을 저장하고 값을 표시하며, 의존 셀이 바뀌면 자동 재계산된다(순환 참조 → #CYCLE!). Pro 패키지(라이선스 필요).

import { SheetGrid } from '@topgrid/grid-pro-sheet';
import { setLicenseKey } from '@topgrid/grid-license';

setLicenseKey('YOUR-LICENSE-KEY');

export default function App() {
// 셀을 더블클릭해 편집: =A1+A2, =SUM(A1:A3)*2, =1/0(→#DIV/0!) 등
return <SheetGrid rows={12} cols={6} />;
}

순수 수식 엔진만 따로 쓸 수도 있다(그리드 없이):

import { compileCell, evaluate, formatValue } from '@topgrid/grid-pro-sheet';

const cells = { A1: 10, A2: 20, A3: 30 } as const;
const compiled = compileCell('=SUM(A1:A3)*2'); // { kind:'formula', ast, refs:['A1','A2','A3'] }
const value = evaluate(compiled.ast, (ref) => cells[ref] ?? ''); // 120
formatValue(value); // "120"

예제 7: 차트 — 경량 (스파크라인 · 범위)

topgrid 차트는 모두 Pro(EULA) 입니다 — 무료 차트 계층은 없습니다. 대신 경량(zero-dep SVG)엔터프라이즈(ECharts 17종) 두 계층으로 나뉩니다. 아래는 실제 렌더되는 라이브 데모입니다. 데이터 흐름·전체 카탈로그는 차트 가이드를 참고하세요.

스파크라인 셀 (SparklineCell)

셀 안 인라인 미니 차트(line/bar/area/win-loss). 차트 라이브러리 의존 0(순수 SVG).

import { SparklineCell } from '@topgrid/grid-pro-chart';

// 컬럼 셀 렌더러에서 — 숫자 배열을 셀 안 미니 차트로
{
accessorKey: 'trend',
header: '추이',
cell: ({ getValue }) => <SparklineCell type="line" values={getValue() as number[]} />,
}

경량 범위 차트 (RangeChart)

zero-dep SVG 차트(bar/line/area) — 셀 범위 선택 → 즉석 시각화.

import { RangeChart } from '@topgrid/grid-pro-chart';

const categories = ['1월', '2월', '3월', '4월', '5월', '6월'];
const series = [{ name: '출하량', data: [120, 200, 150, 80, 170, 210] }];

export default function App() {
return <RangeChart type="bar" categories={categories} series={series} />;
}

차트 카드 — 타입 스위처 (ChartCard)

툴바로 막대/선/영역 전환. 셀 범위·피벗 결과를 카드 하나로 시각화.


예제 8: 차트 — 엔터프라이즈 (ECharts 17종)

@topgrid/grid-pro-chart-enterprise — Apache ECharts 어댑터(17 타입). echarts(5.x/6.x)를 peer로 설치하며, 앱 전체가 단일 ECharts 인스턴스를 공유합니다.

툴바로 17종을 전환할 수 있습니다 — 예: 레이더·히트맵.

import { EnterpriseChartPanel } from '@topgrid/grid-pro-chart-enterprise';
import { setLicenseKey } from '@topgrid/grid-license';

setLicenseKey('YOUR-LICENSE-KEY'); // 앱 entry 1회 (미설정 시 워터마크)

const data = {
categories: ['1월', '2월', '3월', '4월'],
series: [
{ name: '서울', values: [120, 200, 150, 180] },
{ name: '부산', values: [90, 130, 110, 160] },
],
};

export default function App() {
return (
<EnterpriseChartPanel
data={data}
initialType="bar"
toolbarTypes={['bar', 'line', 'radar', 'heatmap', 'pie']} // 17 중 노출할 타입
enableExport
onCrossFilter={(sel) => applyGridFilter(sel.name)} // 차트 클릭 → 그리드 필터
/>
);
}

17 타입: line·bar·area·stacked-bar·stacked-area·100-stacked-bar·scatter·bubble·pie·doughnut· funnel·treemap·radar·heatmap·candlestick·boxplot·sankey.

Vue 3 (동일 엔진)

Nuxt/Vue 앱은 같은 엔진의 Vue 패키지를 씁니다 — React 의존 0, SSR 안전. 아래는 실제 Vue 3 EnterpriseChartPanel 라이브 데모입니다(툴바로 타입 전환).

import { EnterpriseChartPanel, setLicenseKey } from '@topgrid/grid-pro-chart-enterprise-vue';

setLicenseKey('YOUR-LICENSE-KEY');
// <EnterpriseChartPanel :data="data" initial-type="bar"
// :toolbar-types="['bar','radar','heatmap']" @cross-filter="onSelect" />

Bring-your-own (Highcharts / AG Charts)

ECharts 대신 본인이 라이선스한 라이브러리를 주입할 수 있습니다 — renderChart 시임은 라이브러리 무관(의존 0).

import { RangeChartPanel } from '@topgrid/grid-pro-chart';

<RangeChartPanel
series={selectedSeries}
renderChart={(s) => <MyOwnChart series={s} />} // Highcharts / AG Charts 등
/>;

관련 문서