핵심 개념
컬럼 정의, 셀 편집 파이프라인, 가상화 모델, 선택/클립보드 아키텍처
reopt design업데이트
1. 컬럼 정의
DataGridColumn<Row, Value>는 하나의 컬럼을 정의하는 핵심 인터페이스입니다. 표시, 편집, 레이아웃을 모두 이 객체 안에서 선언합니다.
| 속성 | 필수 | 설명 |
|---|---|---|
id | O | 유니크 식별자. 정렬/클립보드/캐시에서 키로 사용 |
title | O | 헤더에 표시되는 텍스트 |
getValue | O | (row: Row) => Value — 셀 표시 값 추출 |
setValue | 편집 시 | (row: Row, value: Value) => Row — 불변 업데이트 반환 |
width / minWidth / maxWidth | 픽셀 단위 너비. 기본값 150px, 리사이즈 범위 제한 | |
resizable | 드래그 리사이즈 허용 여부 (기본 true) | |
align | "left" | "center" | "right" | |
group | 같은 group 값을 가진 컬럼끼리 헤더를 합침 |
const columns: DataGridColumn<Product>[] = [
{
id: "name",
title: "상품명",
getValue: (row) => row.name,
width: 200,
minWidth: 120,
resizable: true,
},
{
id: "price",
title: "가격",
getValue: (row) => row.price,
align: "right",
width: 100,
group: "금액",
},
{
id: "tax",
title: "세금",
getValue: (row) => row.price * 0.1,
align: "right",
width: 100,
group: "금액",
},
];2. 셀 편집 파이프라인
사용자 입력이 최종 데이터에 반영되기까지 5단계 파이프라인을 거칩니다. 각 단계에서 변환, 검증, 거부가 가능합니다.
User Input
→ parseInput(raw) // 문자열 → 타입 변환 (예: "42" → 42)
→ validateValue(parsed) // 유효성 검증, 에러 메시지 반환
→ setValue(row, validated) // 불변 Row 업데이트 반환
→ onCellsEdited(changes) // 배치 편집 인터셉트 (paste/delete 포함)
→ onRowsChange(newRows) // 최종 상태 업데이트빌트인 에디터 종류
| kind | 용도 | 주요 옵션 |
|---|---|---|
text | 단일 행 텍스트 | maxLength, placeholder |
textarea | 여러 줄 텍스트 | rows, maxLength |
number | 숫자 입력 | min, max, step, decimals |
select | 드롭다운 선택 | options, allowEmpty |
checkbox | 불리언 토글 | indeterminate |
date | 날짜 선택 | format, minDate, maxDate |
async-combobox | 비동기 검색 + 선택 | loadOptions, debounceMs |
custom | 커스텀 편집기 컴포넌트 | component, provideEditor |
{
id: "price",
title: "가격",
getValue: (row) => row.price,
setValue: (row, value) => ({ ...row, price: value as number }),
editable: true,
editor: { kind: "number", min: 0, max: 999999, step: 100, decimals: 0 },
// 문자열 입력을 숫자로 변환
parseInput: (raw) => Number(raw.replace(/,/g, "")),
// 음수 값 거부
validateValue: (val) =>
val < 0 ? { valid: false, message: "음수 불가" } : { valid: true },
}3. 가상화 모델
DataGrid는 행(row)과 열(column) 모두 가상화하여 수만 행 이상의 데이터에서도 일정한 렌더링 성능을 유지합니다.
| 옵션 | 기본값 | 설명 |
|---|---|---|
rowBufferPx | 300 | 뷰포트 위/아래에 미리 렌더링할 픽셀 버퍼. 스크롤 시 빈 영역 방지 |
columnOverscan | 2 | 뷰포트 좌/우에 추가 렌더링할 컬럼 수 |
maxRenderedRows | 500 | 한 번에 렌더링할 최대 행 수 상한. 비정상 스크롤 방지용 |
scrollUpdateMode | raf | sync — 매 스크롤 이벤트마다 동기 업데이트. raf — requestAnimationFrame으로 배치 처리 |
valueCache | false | true 시 getValue 결과를 캐시. 스크롤/선택 중 재계산 방지 |
valueCacheStrategy | row-ref | row-ref — 객체 레퍼런스로 invalidation. row-id — getRowId 기반 (sort/reorder에 안정적) |
rendererRefreshMode | auto | auto — 값 변경 시 자동 리렌더. manual — 컬럼별 refreshCellRenderer 콜백으로 제어 |
<DataGrid
columns={columns}
rows={rows}
height={600}
getRowId={(row) => row.id}
// 가상화 튜닝
rowBufferPx={400}
columnOverscan={3}
maxRenderedRows={300}
scrollUpdateMode="raf"
// 값 캐시
valueCache
valueCacheStrategy="row-id"
/>4. 선택 모델
DataGridSelection은 시작 셀(start)과 끝 셀(end)로 정의되는 직사각형 영역입니다. 행 선택과 컬럼 선택은 별도 모드로 제어합니다.
| 옵션 | 값 | 설명 |
|---|---|---|
rowSelect | none | single | multi | 행 선택 모드. multi에서 Shift+Click 범위 선택, Ctrl+Click 토글 |
columnSelect | boolean | 헤더 클릭으로 전체 컬럼 선택 허용 |
rowMarkers | none | number | checkbox | both | 왼쪽 행 마커 표시 유형. checkbox는 multi-select 전용 |
const [selection, setSelection] = useState<DataGridSelection>();
<DataGrid
columns={columns}
rows={rows}
height={400}
rowSelect="multi"
rowMarkers="checkbox"
columnSelect
selection={selection}
onSelectionChange={setSelection}
/>5. 클립보드
복사(Copy)와 붙여넣기(Paste)는 TSV(Tab-Separated Values) 형식을 기본으로 사용합니다. 스프레드시트 앱과의 호환이 자연스럽게 동작합니다.
- Copy: serializeValue(value) → 클립보드 TSV 텍스트
- Paste: 클립보드 TSV → deserializeValue(text) → validateValue → setValue
- Cross-column paste: coercePasteValue로 다른 타입의 컬럼에 붙여넣기 변환 가능
const columns: DataGridColumn<Row>[] = [
{
id: "status",
title: "상태",
getValue: (row) => row.status,
setValue: (row, val) => ({ ...row, status: val as string }),
editable: true,
// 복사 시 한글 라벨로 직렬화
serializeValue: (val) =>
val === "active" ? "활성" : val === "paused" ? "일시정지" : "비활성",
// 붙여넣기 시 역변환
deserializeValue: (text) =>
text === "활성" ? "active" : text === "일시정지" ? "paused" : "inactive",
},
];
<DataGrid
columns={columns}
rows={rows}
height={400}
copyHeaders // 복사 시 헤더 행 포함
maxPasteCells={5000} // 붙여넣기 최대 셀 수 제한
onCopy={(ctx) => {
console.log("복사:", ctx.selection, ctx.cells);
}}
onPaste={(ctx) => {
// false 반환 시 붙여넣기 취소
if (ctx.cells.length > 1000) return false;
return true;
}}
/>6. Row Identity
getRowId는 각 행의 안정적인 식별자를 반환합니다. 지정하지 않으면 배열 인덱스가 사용되지만, 정렬이나 필터링 시 캐시 무효화와 키 충돌이 발생합니다.
권장: 실무 프로젝트에서는 반드시 getRowId를 지정하세요. 정렬, Undo/Redo, Remote Sync, Value Cache 모두 getRowId에 의존합니다.
| valueCacheStrategy | invalidation 기준 | 적합한 경우 |
|---|---|---|
row-ref | 객체 레퍼런스 (===) | 정렬/필터 없이 배열 순서가 고정인 경우 |
row-id | getRowId 반환값 | 정렬, 필터, 재정렬이 빈번한 경우 (권장) |
// DB의 PK나 UUID를 안정적인 식별자로 사용
<DataGrid
columns={columns}
rows={rows}
getRowId={(row) => row.uuid}
valueCache
valueCacheStrategy="row-id"
height={400}
/>