DataGrid
편집형 데이터 그리드 — 정렬·행 선택·인라인 편집·클라이언트 페이징을 갖춘 표. 정적 표는 Table, 인터랙션이 필요하면 DataGrid. 비제어 defaultData + onDataChange(불변) (DK WizDataTable 사양의 절제된 1차 승격).
마지막 업데이트 2026-06-11
한눈에#
정렬·행 선택·인라인 편집·클라이언트 페이징을 갖춘 편집형 데이터 그리드입니다 — 정적 표는 Table, 인터랙션이 필요하면 DataGrid.
| 담당 | 완료 | ||
|---|---|---|---|
| 토큰 SSOT 정리 | planner | 6 | |
| 달력 컴포넌트 구현 | coder | 14 | |
| 접근성 키보드 경로 검수 | reviewer | 4 | |
| 문서 포털 데모 작성 | coder | 9 | |
| 회귀 테스트 보강 | tester | 7 |
- 정렬·선택·인라인 편집·페이징
- APG Data Grid(role=grid)
- 비제어 defaultData
- pageSize로 Pagination 합성
정렬 헤더 클릭(asc→desc→해제)·셀 더블클릭/Enter로 편집·완료 체크박스 즉시 토글 — 직접 조작해 보세요
함수 prop(rowKey)이 필수라 서버 MDX에서 직접 전달할 수 없어 데모 래퍼로
컬럼·rowKey를 주입합니다(DatePickerDemo 패턴). 샘플 5행, 정렬 가능 컬럼(작업·공수)과
편집기(text·select·number·checkbox)가 섞여 있습니다.
사용 시점#
인라인 편집·정렬·선택·페이징이 필요한 운영 표면 DataGrid — 정적 표시나 페이지 컨트롤만이면 다른 컴포넌트입니다.
| 담당 | 완료 | ||
|---|---|---|---|
| 토큰 SSOT 정리 | planner | 6 | |
| 달력 컴포넌트 구현 | coder | 14 |
운영 표에서 인라인 편집·헤더 정렬·행 선택·클라이언트 페이징 중 하나라도 필요할 때
아래 ## 변형의 비교표가 Table과의 선택 기준을 상세히 정리합니다.
플레이그라운드#
컨트롤로 props를 조작하면 미리보기와 코드가 실시간 갱신됩니다.
| 이름 | 역할 |
|---|---|
| 김위즈 | 플래너 |
| 이코드 | 코더 |
| 박리뷰 | 리뷰어 |
import { DataGrid } from '@wds/ui-web';
<DataGrid columns={columns} defaultData={members} rowKey={(row) => row.id} />변형#
| 담당 | 완료 | |||
|---|---|---|---|---|
| 토큰 SSOT 정리 | planner | 6 | ||
| 달력 컴포넌트 구현 | coder | 14 | ||
| 접근성 키보드 경로 검수 | reviewer | 4 | ||
| 문서 포털 데모 작성 | coder | 9 | ||
| 회귀 테스트 보강 | tester | 7 |
| 담당 | 완료 | ||
|---|---|---|---|
| 토큰 SSOT 정리 | planner | 6 | |
| 달력 컴포넌트 구현 | coder | 14 |
pageSize를 주면 하단에 Pagination이 합성되고
페이지 단위로 행을 잘라 보여줍니다. 정렬은 전체 데이터 기준이라 페이지가
바뀌어도 정렬 순서가 유지됩니다. stickyHeader를 켜면 세로 스크롤 시 헤더가
고정됩니다.
Table과의 선택 기준#
| 기준 | Table | DataGrid |
|---|---|---|
| 용도 | 정적 표시 — 읽기 전용 데이터 | 인라인 편집·정렬·선택이 필요한 운영 표 |
| 정렬 | 소비자 책임(YAGNI) | 헤더 클릭 sortable 내장 |
| 행 선택 | 없음 | selectable 체크박스 + 전체 선택 |
| 편집 | 없음 | editor(text/number/select/checkbox) 인라인 |
| 페이징 | 소비자 책임 | pageSize로 Pagination 합성 |
읽기 전용 표는 가벼운 Table을 쓰고, 편집·정렬·선택 중 하나라도 필요하면 DataGrid를 씁니다.
범위 제외(백로그) — rowspan 셀 병합 · 하위행(트리/확장) · 서버 페이징은
이번 1차 승격 범위 밖입니다. 서버 페이징이 필요하면 현재는 소비자가
defaultData를 페이지 단위로 갈아끼우는 방식으로 대응합니다.
크기#
폭은 부모 100%, 좁은 화면에서는 래퍼가 가로 스크롤로 보호합니다(caption이
있으면 그 스크롤 래퍼가 키보드 region이 됩니다). 셀 패딩은 space.3×space.4
단일 밀도이고, stickyHeader의 기본 컨테이너 높이는 24rem(약 10행)으로 소비자가
재정의할 수 있습니다.
상태#
| 담당 | 완료 | ||
|---|---|---|---|
| 집계된 작업이 없습니다 | |||
- 행 hover —
color.surface-hover배경 - 선택 행 —
color.surface-selected(hover보다 우선) - 편집 셀 포커스 —
color.primary아웃라인, 편집 모드 입력은input.border-focus보더 - 정렬 활성 헤더 —
aria-sort+color.primary화살표(asc는 180° 회전) - 빈 데이터 —
colSpan전체 폭에emptyContent
Props#
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
columns | ReadonlyArray<DataGridColumn<T>> | — | 컬럼 정의 배열 (아래 표) |
defaultData | ReadonlyArray<T> | [] | 초기 행 데이터 (비제어) — 편집 확정 시 내부 상태만 갱신 |
onDataChange | (rows: T[]) => void | — | 셀 편집 확정 통지 — 항상 새 배열·새 행 객체(불변) |
rowKey | (row: T, index: number) => string | — | 행 고유 키 — index 기반 키는 정렬 시 위험해 명시를 강제 (필수) |
selectable | boolean | false | 첫 열 체크박스 + 헤더 전체 선택(indeterminate) |
onSelectionChange | (keys: string[]) => void | — | 선택 행 키 통지 — 전체 데이터 기준(페이지 무관) |
pageSize | number | — | 지정 시 하단 Pagination 합성 + 클라이언트 페이징 |
stickyHeader | boolean | false | 세로 스크롤 시 헤더 고정 |
caption | ReactNode | — | 표 이름(접근성) — 제공 시 스크롤 래퍼도 키보드 region이 됨 |
emptyContent | ReactNode | '데이터가 없습니다' | 데이터 0건 표시 내용 |
selectAllLabel | string | '전체 선택' | 헤더 전체 선택 체크박스 aria-label |
selectRowLabel | (row: T, index: number) => string | '{rowKey} 행 선택' | 행 선택 체크박스 aria-label |
DataGridColumn<T>:
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
key | string | — | 데이터 키 또는 컬럼 id — render 미지정 시 row[key] 원시값 표시 |
header | ReactNode | — | 헤더 셀 내용 |
align | 'left' | 'center' | 'right' | — | 셀 정렬 — 숫자는 right 권장 |
width | string | — | 컬럼 폭 (CSS 값) |
sortable | boolean | false | 헤더 버튼 클릭으로 asc → desc → 해제 순환 정렬 |
editor | 'text' | 'number' | 'select' | 'checkbox' | — | checkbox는 즉시 토글, 나머지는 편집 모드 진입 |
options | ReadonlyArray<DataGridSelectOption> | — | editor: 'select' 전용 옵션 목록 ({ value, label }) |
render | (row: T, rowIndex: number) => ReactNode | — | 커스텀 셀 렌더 (편집기 없는 컬럼) |
접근성#
APG Data Grid 패턴입니다 — role="grid" + 셀 roving tabindex로 그리드 전체가
단일 탭 스톱이 됩니다. 내부 위젯(정렬 버튼·체크박스)은 탭 순서에서 빠지고(tabIndex=-1)
포커스는 셀이 가지며 Enter/Space가 위임됩니다. 헤더는 <th scope="col"> + aria-sort,
페이징 시 DOM에 없는 행 수를 aria-rowcount/aria-rowindex로 보고합니다.
| 키 | 동작 |
|---|---|
Tab | 그리드 진입/이탈 — 마지막 포커스 셀(없으면 헤더 첫 셀) 하나만 탭 순서에 포함 |
↑ ↓ ← → | 셀 단위 포커스 이동(헤더 행 포함) — 경계에서 클램프, 순환 없음 |
Home / End | 행의 첫/마지막 셀 |
Ctrl+Home / Ctrl+End | 그리드 첫 셀(헤더)/마지막 셀 |
PageUp / PageDown | 이전/다음 페이지로 이동(pageSize 지정 시) — 셀 좌표 유지·클램프 |
정렬 헤더 셀 Enter / Space | asc → desc → 해제 순환 |
선택 셀 Space / Enter | 행 선택 토글 · 헤더 선택 셀은 전체 선택 토글 |
checkbox 편집 셀 Space / Enter | 값 즉시 토글(편집 모드 없음) |
편집 셀 Enter | 편집 모드 진입 — 입력으로 포커스 이동 (text/number/select) |
| 편집 셀 더블클릭 | 편집 모드 진입 |
편집 중 Enter | 확정(commit) + 셀로 포커스 복귀 |
편집 중 Escape | 취소 — 원값 유지 + 셀로 포커스 복귀 (편집 재진입 차단) |
| 편집 중 blur | 확정 (Enter 확정 직후 blur 중복은 1회 가드) |
- 편집 중에는 그리드 내비가 꺼집니다 — 화살표는 입력 캐럿 이동에 쓰이고, Escape/Enter로 셀에 복귀한 뒤 다시 내비합니다
- 클릭한 셀이 roving 탭 스톱이 됩니다 — Tab으로 나갔다 돌아와도 마지막 위치가 유지됩니다
- number 편집은 빈 값·숫자 아님이면 조용히 취소(원값 유지)
- 좁은 화면 가로 스크롤 래퍼는
caption이 있으면role="region"+tabIndex=0으로 키보드 조작 가능
토큰#
component 토큰 없이 Table·Input·Checkbox 어휘와 semantic을 직접 소비합니다(신설 기준 §4 미충족).
| 속성 | 토큰 |
|---|---|
| 헤더 | color.surface-muted(배경) · color.text-muted · border-strong(하단) |
| 셀 | color.text · 구분선 color.border · 패딩 space.3 × space.4 |
| 행 hover | color.surface-hover |
| 선택 행 | color.surface-selected |
| 정렬 활성 | color.primary(화살표) |
| 인라인 에디터 | input.bg/fg/border-focus · radius.sm |
| 편집 셀 포커스 | color.focus-ring(아웃라인) |
| 캡션 | color.text-muted · font-size.caption |
| 모션 | duration.fast + ease.standard |