Row Selection
TanStack 네이티브 rowSelection(행 id → boolean) 위의 체크박스 선택 — 셀 선택과는 독립적인 별개 기능입니다.
개요
행 선택은 TanStack Table의 네이티브 rowSelection 상태(Record<string, boolean>, 행 id → boolean 맵)를 기반으로 동작하는 체크박스 선택 모델입니다.
셀 선택과의 차이
이 페이지의 행 선택과 셀 선택(./selection)은 완전히 별개 기능입니다.
| 행 선택 (이 페이지) | 셀 선택 (./selection) | |
|---|---|---|
| 상태 모델 | RowSelectionState — 행 id → boolean | DataGridSelectionState — anchor/focused/ranges 사각형 |
| 진입점 | enableRowSelection + createSelectColumn | useDataGridSelection |
| UI | 체크박스 컬럼 | Excel식 사각형 하이라이트 |
| 조합 | 독립적으로 함께 켤 수 있음 | 독립적으로 함께 켤 수 있음 |
언제 어느 것을? 행 단위 선택 후 벌크 액션(삭제·그룹 저장·내보내기)에는 행 선택을, 셀 데이터 복사·편집 진입·스프레드시트식 상호작용에는 셀 선택을 사용합니다. 둘을 동시에 켜도 됩니다(KitchenSink 참고).
기본 사용법
enableRowSelection: true를 useDataGrid에 주고, createSelectColumn을 컬럼 목록 맨 앞에 끼웁니다.
const data = [ { id: '1', name: '김민준', followers: 128000 }, { id: '2', name: '이서연', followers: 84500 }, { id: '3', name: '박도윤', followers: 233000 }, { id: '4', name: '최하은', followers: 45200 }, { id: '5', name: '정시우', followers: 67800 }, ]; // 행 선택 체크박스 컬럼 — 아래는 createSelectColumn이 하는 일을 인라인으로 풀어쓴 최소 버전입니다. // (실제 프로젝트에서는 공용 createSelectColumn 팩토리로 한 곳에서 소유하세요.) const columns = [ { id: '_select', size: 44, enableSorting: false, meta: { columnRole: 'control' }, header: ({ header, table }) => ( <DataGrid.HeaderCell header={header} $css={{ cursor: 'pointer' }} onClick={() => table.toggleAllRowsSelected()} > <Center $css={{ width: '100%', height: '100%' }}> <Checkbox aria-label="전체 선택" size="sm" checked={table.getIsAllRowsSelected()} indeterminate={table.getIsSomeRowsSelected() && !table.getIsAllRowsSelected()} onChange={table.getToggleAllRowsSelectedHandler()} onClick={(e) => e.stopPropagation()} /> </Center> </DataGrid.HeaderCell> ), cell: (info) => ( <DataGrid.Cell cell={info.cell} $css={{ cursor: 'pointer' }} onClick={() => info.row.getCanSelect() && info.row.toggleSelected()} > <Center $css={{ width: '100%', height: '100%' }}> <Checkbox aria-label={info.row.original.name + ' 선택'} size="sm" disabled={!info.row.getCanSelect()} checked={info.row.getIsSelected()} onChange={info.row.getToggleSelectedHandler()} onClick={(e) => e.stopPropagation()} /> </Center> </DataGrid.Cell> ), }, { accessorKey: 'name', header: ({ header }) => <DataGrid.HeaderCell header={header}>채널</DataGrid.HeaderCell>, cell: (info) => <DataGrid.Cell cell={info.cell}>{info.getValue()}</DataGrid.Cell>, }, { accessorKey: 'followers', header: ({ header }) => <DataGrid.HeaderCell header={header}>팔로워</DataGrid.HeaderCell>, cell: (info) => <DataGrid.Cell cell={info.cell}>{info.getValue().toLocaleString()}</DataGrid.Cell>, }, ]; function RowSelectGrid() { const [rowSelection, setRowSelection] = useState({}); const table = useDataGrid({ data, columns, getCoreRowModel: getCoreRowModel(), getRowId: (row) => row.id, state: { rowSelection }, onRowSelectionChange: setRowSelection, enableRowSelection: true, }); const selectedCount = table.getSelectedRowModel().rows.length; return ( <VStack $css={{ gap: '$spacing-200' }}> <Typo variant="$caption-1" $css={{ color: '$text-3' }}> 선택됨: {selectedCount} / {data.length} </Typo> <DataGrid.Root table={table} size="md"> <DataGrid.Header /> <DataGrid.Body /> </DataGrid.Root> </VStack> ); } render(<RowSelectGrid />);
createSelectColumn은 헤더에 전체선택 체크박스, 각 행 셀에 행별 체크박스를 렌더합니다. 체크박스뿐 아니라 셀 전체가 토글 히트영역이므로, 셀 어디를 눌러도 선택이 토글됩니다.
getRowId필수 —rowSelection의 키가 행 id입니다.getRowId를 지정하지 않으면 TanStack이 행 인덱스를 id로 쓰고, 정렬·필터 후 행 순서가 바뀌면 선택이 엉뚱한 행을 가리킵니다. 항상(r) => r.id처럼 안정적인 고유 키를 주세요.
전체선택 — 페이지 vs 전 행
페이지네이션(getPaginationRowModel)을 사용할 때 헤더 전체선택의 의미가 두 가지로 갈립니다.
selectAllScope | 선택 범위 | 내부 핸들러 |
|---|---|---|
'all' (기본) | 모든 행 | getToggleAllRowsSelectedHandler |
'page' | 현재 페이지만 | getToggleAllPageRowsSelectedHandler |
createSelectColumn의 selectAllScope 옵션으로 고릅니다.
// 클라이언트 페이지네이션 — 현재 페이지만 전체선택
createSelectColumn<Row>({ selectAllScope: 'page' })
// 서버 페이지네이션 또는 단순 데이터 — 전 행 전체선택 (기본값)
createSelectColumn<Row>({ selectAllScope: 'all' })헤더 indeterminate
헤더 체크박스는 자동으로 indeterminate 상태를 관리합니다. createSelectColumn 내부 로직이 다음을 계산합니다.
- 모두 선택 →
checked - 일부 선택 →
indeterminate(isSomeSelected && !isAllSelected) - 없음 → unchecked
selectAllScope에 따라 전 행 버전(getIsAllRowsSelected / getIsSomeRowsSelected) 또는 페이지 버전(getIsAllPageRowsSelected / getIsSomePageRowsSelected)을 사용합니다. 별도로 계산할 필요가 없습니다.
Controlled
state.rowSelection + onRowSelectionChange를 소비자가 직접 소유하면 외부 UI와 그리드가 단일 상태를 공유합니다.
const [rowSelection, setRowSelection] = useState<RowSelectionState>({
'1': true, // 행 id '1'이 초기 선택 상태
'3': true,
});
const table = useDataGrid<Row>({
data,
columns,
getRowId: (r) => r.id,
state: { rowSelection },
onRowSelectionChange: setRowSelection,
enableRowSelection: true,
});
// 선택된 행 id 읽기
const selectedIds = Object.keys(rowSelection).filter((id) => rowSelection[id]);
// 선택된 행 모델 읽기
const selectedRows = table.getSelectedRowModel().rows;
// 외부에서 전체 선택 해제
const clearAll = () => setRowSelection({});setRowSelection({})만으로 그리드 체크박스가 즉시 동기화됩니다. state가 유일한 source of truth입니다.
조건부 선택 가능 — enableRowSelection 술어
enableRowSelection에 함수를 주면 행마다 선택 가능 여부를 결정합니다.
const table = useDataGrid<Row>({
// ...
enableRowSelection: (row) => row.original.status !== 'inactive',
});row.getCanSelect()가 false인 행은 createSelectColumn이 체크박스를 disabled로 렌더하고, 헤더 전체선택도 선택 가능한 행만 대상으로 동작합니다.
행 상태 스타일링
패키지는 선택된 행에 강조 색을 강요하지 않습니다. <DataGrid.Row>가 DataGridRowState를 style / className state-callback으로 노출하므로, 소비자가 원하는 스타일을 자유롭게 입힙니다.
<DataGrid.Body>
{(row) => (
<DataGrid.Row
row={row}
onClick={() => row.toggleSelected()}
// style state-callback — isSelected 에 따라 동적 스타일
style={(state) => ({
cursor: 'pointer',
backgroundColor: state.isSelected
? 'var(--global-colors-primary-10)'
: 'transparent',
borderLeft: state.isSelected
? '3px solid var(--global-colors-primary-60)'
: '3px solid transparent',
})}
>
{row.getVisibleCells().map((cell) => (
<Fragment key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</Fragment>
))}
</DataGrid.Row>
)}
</DataGrid.Body>
$css와style의 차이 — rainbow-sprinkles 기반$css는 atomic CSS라 콜백을 받지 않습니다. 선택 상태처럼 런타임 boolean으로 분기하는 동적 스타일은stylestate-callback을 사용하세요.
DOM에는 선택 상태가 다음 속성으로도 노출됩니다.
data-selected— 행이 선택된 경우 빈 문자열 속성 부착data-some-selected— 하위 행 일부가 선택된 경우 (트리 데이터)aria-selected— 선택 시"true", 미선택 시 속성 없음
CSS만으로 선택 행을 스타일링할 수도 있습니다.
[data-row][data-selected] {
background-color: var(--global-colors-primary-10);
}createSelectColumn 옵션
createSelectColumn은 _shared 공용 팩토리로 ColumnDef<T>를 반환합니다. 컬럼 설정은 다음 옵션으로 조정합니다.
Prop
Type
DataGridRowState
<DataGrid.Row>의 style/className state-callback에 전달되는 상태 객체입니다.
Prop
Type
다음 단계
- 셀 선택(Selection model) — anchor/focused/ranges 사각형 모델. 행 선택과 별개이며 함께 사용할 수 있습니다.
- Columns — 컬럼 정의, control 컬럼 패턴(
meta.columnRole: 'control'). - Pagination — 페이지 전환과
selectAllScope의 관계. - 라이브 스토리 — Storybook에서 Overview · Controlled · 조건부 선택 · 페이지 vs 전 행 · 행 상태 스타일링 예제를 직접 실행할 수 있습니다.