Skip to Content

DataTable

정렬, 필터링, 페이지네이션을 지원하는 고급 데이터 테이블


개요

DataTable은 TanStack Table과 @dnd-kit 기반의 고급 데이터 테이블 컴포넌트입니다. 정렬, 필터링, 페이지네이션, 행 선택, 인라인 편집 등 다양한 기능을 통합 제공합니다.

주요 특징

  • 정렬/필터링: 컬럼별 정렬 및 필터 (텍스트, 셀렉트, 범위, 날짜 등)
  • 페이지네이션: 클라이언트/서버 사이드
  • 행 선택: 단일/다중 선택
  • 인라인 편집: 셀 단위 편집
  • 컬럼 관리: 리사이즈, 리오더, 피닝, 가시성 토글
  • 행 피닝: 특정 행 상단/하단 고정
  • 서버 사이드: 수동 페이지네이션/정렬/필터링
  • 접근성: 키보드 네비게이션
  • 푸터 합계 행: 컨럼 정의의 footer로 하단 합계 행 자동 렌더링
  • 디자인 토큰: 테마 커스터마이징 지원

기본 테이블

이름
이메일
역할
홍길동
hong@example.com
관리자
김철수
kim@example.com
분석가
이영희
lee@example.com
뷰어
전체 건수 3

Features

행 선택

이름
이메일
역할
홍길동
hong@example.com
관리자
김철수
kim@example.com
분석가
이영희
lee@example.com
뷰어
전체 건수 3

최대 높이 (스크롤)

지표
현재 값
이전 값
변화율
페이지뷰
125,430
118,200
+6.1%
세션
45,200
42,800
+5.6%
순 방문자
32,100
30,500
+5.2%
이탈률
42.3%
44.1%
-4.1%
평균 체류시간
3m 24s
3m 10s
+7.4%
전체 건수 5

필터링 + 정렬 + 편집

컨럼 meta에 필터 옵션을 설정하고, editable로 인라인 편집을 활성화합니다. editType: "lookup"을 지정하면 검색 가능한 Combobox로 편집합니다. tableRef를 사용하면 외부에서 newRow(), cancelNewRow(), saveNewRow()를 제어할 수 있습니다.

Name
Category
Price
₩1,200,000
₩89,000
₩35,000
₩450,000
₩65,000
전체 건수 5

인라인 편집 타입

meta.editType으로 셀 편집 UI 타입을 지정합니다. lookup은 검색 가능한 Combobox로 렌더링됩니다.

이름 (text)
부서 (lookup)
상태 (select)
동의 (switch)
Engineering
동의
Design
미동의
Marketing
동의

필터 순서 제어 + 컬럼 선택기 숨김

meta.filterOrder로 컬럼 정의 순서와 별개로 필터 UI 표시 순서를 지정할 수 있습니다. filterOrder 값이 있는 필터가 우선 배치되고, 없는 필터는 추가된 순서를 유지합니다. showColumnSelector={false}로 컬럼 선택기를 숨길 수 있으며, Reset 버튼은 활성 필터가 있을 때만 표시됩니다.

Name
Category
Status
Price
노트북
Electronics
active
₩1,200,000
키보드
Electronics
active
₩89,000
티셔츠
Clothing
inactive
₩35,000
모니터
Electronics
active
₩450,000
후드집업
Clothing
active
₩65,000
전체 건수 5

컬럼/행 피닝

ID
이름
이메일
부서
역할
상태
1
홍길동
hong@example.com
마케팅
매니저
비활성
2
김철수
kim@example.com
개발
시니어
활성
3
이영희
lee@example.com
디자인
주니어
활성
4
박지민
park@example.com
마케팅
시니어
비활성
5
최수진
choi@example.com
개발
주니어
활성
6
정민호
jung@example.com
디자인
매니저
활성
7
강서연
kang@example.com
마케팅
주니어
비활성
8
윤태호
yoon@example.com
개발
시니어
활성
9
임지현
lim@example.com
디자인
매니저
활성
10
한승우
han@example.com
마케팅
주니어
비활성
전체 건수 10

설정 저장 (Storage)

storage prop에 ConfigStorage 구현체를 전달하고 autoSave를 켜면, 컬럼 순서(order), 보이기/숨기기(visibility), 너비(sizing) 설정이 자동 저장됩니다. 새로고침 후에도 설정이 유지됩니다.

이름
이메일
역할
부서
홍길동
hong@example.com
관리자
개발
김철수
kim@example.com
분석가
마케팅
이영희
lee@example.com
뷰어
디자인
박지민
park@example.com
매니저
개발
최수진
choi@example.com
시니어
마케팅

고정 높이

height로 테이블 영역의 고정 높이를 지정합니다. 데이터가 적어도 높이가 줄어들지 않고, 데이터가 많으면 스크롤됩니다. maxHeight는 최대 높이만 제한하므로 데이터가 적으면 줄어드는 반면, height는 항상 고정됩니다.

이름
이메일
역할
홍길동
hong@example.com
관리자
김철수
kim@example.com
분석가
이영희
lee@example.com
뷰어

컬럼 정렬 (Column Align)

meta.align으로 헤더와 셀의 텍스트 정렬을 지정합니다. "left" (기본), "center", "right"를 지원합니다. 헤더와 셀을 개별 지정하려면 headerAlign, cellAlign을 사용합니다. 개별 값이 align보다 우선합니다. selectable 사용 시 selection 컬럼은 자동으로 center 정렬됩니다.

ID
상품명
카테고리
가격
재고
1
무선 키보드
전자기기
₩89,000
45
2
게이밍 마우스
전자기기
₩65,000
8
3
후드집업
의류
₩42,000
120
4
모니터 암
전자기기
₩55,000
23
5
요리책
도서
₩22,000
200

확장 가능 행 (Detail Panel)

expandablerenderExpandedRow로 행을 펼쳐 상세 콘텐츠를 표시합니다. chevron 아이콘으로 토글합니다.

주문번호
고객
날짜
상태
합계
ORD-001
김철수
2025-01-15
배송완료
₩125,000
ORD-002
이영희
2025-01-16
배송중
₩89,000
ORD-003
박민수
2025-01-17
준비중
₩234,000

컬럼 정의의 footer 속성으로 테이블 하단에 합계 행을 표시합니다. footer가 하나라도 정의되면 자동으로 <tfoot>이 렌더링됩니다.

상품명
카테고리
수량
단가
금액
노트북 Pro 15
전자기기
12
₩1,500,000
₩18,000,000
무선 키보드
주변기기
45
₩89,000
₩4,005,000
모니터 27" 4K
전자기기
8
₩650,000
₩5,200,000
USB-C 허브
주변기기
100
₩35,000
₩3,500,000
웹캠 HD Pro
주변기기
30
₩120,000
₩3,600,000

enableSearch / enableSorting (전체 기본값)

enableSearchenableSorting props로 전체 컬럼의 검색/정렬 기본값을 설정합니다. 컬럼에 개별 선언된 값이 항상 우선합니다.

이름 (search 개별 활성)
이메일
역할 (sort 개별 활성)
부서
홍길동
hong@example.com
관리자
개발
김철수
kim@example.com
분석가
마케팅
이영희
lee@example.com
뷰어
디자인
박지민
park@example.com
매니저
개발
최수진
choi@example.com
시니어
마케팅

날짜 범위 필터 (date-range)

meta.filterVariant"date-range"로 설정하면 DatePicker의 range 모드로 날짜 범위 필터링을 할 수 있습니다. from~to 범위 내의 날짜 데이터만 표시됩니다.

필터 태그의 날짜 포맷은 기본 yyyy-MM-dd이며, meta.filterDateFormat으로 커스터마이징 가능합니다. (지원 토큰: yyyy, yy, MM, dd)

이벤트명
카테고리
시작일
종료일
상태
신제품 출시
마케팅
2025-01-10
2025-01-20
완료
분기 보고서
경영
2025-02-01
2025-02-15
완료
UI 리디자인
개발
2025-03-05
2025-04-10
진행중
여름 캠페인
마케팅
2025-06-01
2025-07-31
예정
보안 감사
개발
2025-04-15
2025-04-30
진행중

Controlled Row Selection

rowSelection prop으로 외부에서 선택 상태를 제어할 수 있습니다. onRowSelectionChange 콜백과 함께 사용하면 양방향 바인딩이 가능합니다. rowSelection을 전달하지 않으면 기존처럼 내부 상태로 동작합니다 (uncontrolled).

선택: 1, 3

이름
이메일
역할
홍길동
hong@example.com
관리자
김철수
kim@example.com
분석가
이영희
lee@example.com
뷰어
박지민
park@example.com
매니저
최수진
choi@example.com
시니어

Border Variant

variant="border"로 셀마다 좌우 border를 추가합니다.

이름
이메일
역할
부서
홍길동
hong@example.com
관리자
개발
김철수
kim@example.com
분석가
마케팅
이영희
lee@example.com
뷰어
디자인

서버 사이드 페이지네이션

<DataTable columns={columns} data={data} getRowId={(row) => row.id} manualPagination pageCount={totalPages} pageSizeOptions={[10, 20, 50]} onPaginationChange={({ pageIndex, pageSize }) => { fetchData(pageIndex, pageSize) }} />

사용 예시

예시 1: 분석 지표 테이블

지표
현재 값
이전 값
변화율
페이지뷰
125,430
118,200
+6.1%
세션
45,200
42,800
+5.6%
순 방문자
32,100
30,500
+5.2%
이탈률
42.3%
44.1%
-4.1%
평균 체류시간
3m 24s
3m 10s
+7.4%
전체 건수 5

예시 2: 커스텀 셀 렌더링

상품명
가격
상태
재고
무선 키보드
₩89,000
판매중
45
게이밍 마우스
₩65,000
판매중
8
USB-C 허브
₩42,000
품절
0
모니터 암
₩55,000
판매중
23
웹캠 HD
₩78,000
품절
3
전체 건수 5

API Reference

Props

PropTypeDefaultDescription
columnsDataTableColumnDef<T>[][]컬럼 정의
dataT[][]데이터 배열
getRowId(row: T) => string-행 고유 ID 함수 (필수)
titleReact.ReactNode-테이블 제목
actionsReact.ReactNode-액션 영역
selectablebooleanfalse행 선택 활성화
enableMultiRowSelectionbooleanfalse다중 선택 허용
enableRowSelection(row: Row) => boolean() => true행별 선택 가능 여부
editablebooleanfalse인라인 편집 활성화
manualPaginationbooleanfalse서버 사이드 페이지네이션
manualSortingbooleanfalse서버 사이드 정렬
manualFilteringbooleanfalse서버 사이드 필터링
heightstring | number-고정 높이 (데이터 적어도 유지)
maxHeightstring | number-최대 높이 (스크롤)
loadingbooleanfalse로딩 상태
loadingDimstring-로딩 시 dim 배경색
loadingContentReact.ReactNode-로딩 시 표시할 커스텀 콘텐츠
pageCountnumber-전체 페이지 수 (서버)
rowCountnumber-전체 행 수 (서버)
defaultPageSizenumber10페이지 크기 초기값
pageSizeOptionsnumber[][10,20,50,100]페이지 크기 옵션
hidePaginationbooleanfalse페이지네이션 숨김
tableLayout"auto" | "fixed"-테이블 레이아웃
showColumnSelectorbooleantrue컬럼 선택기 표시 여부
storageConfigStorage<TableConfig>-설정 저장소 구현체
autoSavebooleanfalse설정 자동 저장 활성화
enableRowPinningbooleanfalse행 피닝 활성화
enableSearchboolean-전체 컬럼 검색 기본값
enableSortingboolean-전체 컬럼 정렬 기본값
variant"default" | "border"-스타일 변형 (border: 셀 좌우 border)
tableRefReact.RefObject<DataTableRef>-임퍼러티브 핸들 ref (새 행 제어)
expandablebooleanfalse확장 가능 행 활성화
getSubRows(row: T) => T[] | undefined-하위 행 접근 함수 (트리 구조)
getRowCanExpand(row: Row) => boolean-행 확장 가능 여부 판별 함수
renderExpandedRow(row: Row) => React.ReactNode-확장 시 렌더링할 콘텐츠
rowSelectionRecord<string, boolean>-행 선택 상태 (controlled)
columnFiltersColumnFiltersState-컬럼 필터 상태 (controlled)
columnOrderstring[]-컬럼 순서 (controlled)
columnVisibilityVisibilityState-컬럼 표시 상태 (controlled)
columnPinningColumnPinningState-컬럼 피닝 상태 (controlled)
rowPinningRowPinningState-행 피닝 상태 (controlled)
expandedExpandedState-확장 상태 (controlled)
classNamestring-추가 CSS 클래스

Events

EventTypeDescription
onRowSelectionChange(rows: T[]) => void행 선택 변경
onPaginationChange(state: { pageIndex, pageSize }) => void페이지 변경
onSortingChange(sorting: SortingState) => void정렬 변경
onCellUpdate(rowIndex, columnId, value) => void셀 값 업데이트 (onBlur)
onCellEditStart(rowIndex, columnId, value) => void셀 편집 시작 (onFocus)
onCellEditEnd(rowIndex, columnId, value) => void셀 편집 종료 (onBlur)
onRowClick(row: T) => void행 클릭
onRowDoubleClick(row: T) => void행 더블클릭
onSaveNewRow(newRowData: Partial<T>) => void새 행 저장
onColumnFiltersChange(filters: ColumnFiltersState) => void컬럼 필터 변경
onColumnOrderChange(order: string[]) => void컬럼 순서 변경
onColumnVisibilityChange(visibility: VisibilityState) => void컬럼 표시 변경
onColumnPinningChange(pinning: ColumnPinningState) => void컬럼 피닝 변경
onRowPinningChange(pinning: RowPinningState) => void행 피닝 변경
onExpandedChange(expanded: ExpandedState) => void확장 상태 변경

DataTableColumnDef

TanStack Table의 ColumnDef를 확장합니다.

PropTypeDefaultDescription
idstring-컬럼 고유 ID (필수)
headerstring | Function-헤더 텍스트/렌더 함수
accessorKeystring-데이터 접근 키
sizenumber-컬럼 너비 (px)
maxSizenumber-컬럼 최대 너비 (px), 리사이즈 상한 제한
cellFunction-셀 커스텀 렌더 함수
footerstring | Function-푸터 렌더 함수 (합계 등)
enableSearchboolean-검색 활성화 (props 기본값 오버라이드)
enableSortingboolean-정렬 활성화 (props 기본값 오버라이드)
metaobject-필터/편집/피닝 설정

DataTableRef

tableRef prop을 통해 외부에서 새 행 추가/취소/저장을 제어할 수 있습니다.

MethodTypeDescription
newRow()() => void새 행 추가 모드 활성화
cancelNewRow()() => void새 행 추가 취소
saveNewRow()() => void새 행 저장 (onSaveNewRow 콜백 호출)
import { useRef } from "react" import { DataTable, type DataTableRef } from "@vortex/ui-icignal" const tableRef = useRef<DataTableRef>(null) <DataTable tableRef={tableRef} editable onSaveNewRow={(newRow) => console.log(newRow)} // ... /> // 외부에서 호출 tableRef.current?.newRow() tableRef.current?.cancelNewRow() tableRef.current?.saveNewRow()

ConfigStorage<T>

MethodTypeDescription
getItem()() => T | null저장된 설정 가져오기
setItem()(value: T) => void설정 저장하기
removeItem()() => void저장된 설정 삭제하기

TableConfig

PropertyTypeDescription
columnVisibilityRecord<string, boolean>컬럼 보이기/숨기기 상태
columnOrderstring[]컬럼 순서
columnSizingRecord<string, number>컬럼 너비 (px)

Column Meta

PropTypeDescription
filterVariant"text" | "select" | "range" | "switch" | "checkbox" | "date" | "date-range"필터 유형
filterPlaceholderstring필터 플레이스홀더
filterAlwaysShowboolean필터 항상 표시
filterOptions{ label, value }[]select 필터 옵션
filterOrdernumber필터 표시 순서 (값이 있는 필터가 우선, 오름차순)
filterFormat(value: FilterValue) => string | React.ReactNode필터 값 포맷 함수
filterDateFormatstring날짜 필터 포맷 (기본: yyyy-MM-dd)
editableboolean셀 편집 가능 여부
editType"text" | "select" | "lookup" | "switch"셀 편집 UI 타입
editOptions{ label, value }[]select/lookup 편집 옵션
editPlaceholderstring편집 UI 플레이스홀더
align"left" | "center" | "right"헤더/셀 텍스트 정렬 (기본: left)
headerAlign"left" | "center" | "right"헤더만 정렬 (align보다 우선)
cellAlign"left" | "center" | "right"셀만 정렬 (align보다 우선)
pinned"left" | "right"컬럼 고정 위치

기본 사용법

import { DataTable, type DataTableColumnDef } from "@vortex/ui-icignal" const columns: DataTableColumnDef<User>[] = [ { id: "name", header: "이름", accessorKey: "name", size: 150 }, { id: "email", header: "이메일", accessorKey: "email", size: 200 }, ] const data = [ { id: "1", name: "홍길동", email: "hong@example.com" }, ] <DataTable columns={columns} data={data} getRowId={(row) => row.id} />

필터/정렬/편집 통합 예시

import { DataTable, type DataTableColumnDef } from "@vortex/ui-icignal" import { useState } from "react" interface Metric { id: string name: string category: string value: number status: "active" | "inactive" } const [data, setData] = useState<Metric[]>([ { id: "1", name: "페이지뷰", category: "트래픽", value: 12500, status: "active" }, { id: "2", name: "세션", category: "트래픽", value: 3200, status: "active" }, { id: "3", name: "이탈률", category: "트래픽", value: 42, status: "inactive" }, { id: "4", name: "매출", category: "매출", value: 98000, status: "active" }, { id: "5", name: "ARPU", category: "매출", value: 1250, status: "active" }, ]) const columns: DataTableColumnDef<Metric>[] = [ { id: "name", header: "지표명", accessorKey: "name", size: 150, enableSearch: true, }, { id: "category", header: "카테고리", accessorKey: "category", size: 120, meta: { filterVariant: "select", filterOptions: [ { label: "트래픽", value: "트래픽" }, { label: "매출", value: "매출" }, ], }, }, { id: "value", header: "값", accessorKey: "value", size: 100, meta: { editable: true }, }, { id: "status", header: "상태", accessorKey: "status", size: 100, meta: { filterVariant: "select", filterOptions: [ { label: "활성", value: "active" }, { label: "비활성", value: "inactive" }, ], }, }, ] <DataTable columns={columns} data={data} getRowId={(row) => row.id} title="분석 지표" selectable editable onCellUpdate={(rowIndex, columnId, value) => { setData((prev) => prev.map((row, i) => (i === rowIndex ? { ...row, [columnId]: value } : row)) ) }} onRowSelectionChange={(rows) => console.log("선택:", rows)} />

접근성

ARIA 속성

<DataTable columns={columns} data={data} getRowId={(row) => row.id} /> // 자동: role="table", role="row", role="cell" 등

권장 사항

  • ✅ 각 컬럼에 명확한 header 텍스트 제공
  • ✅ 컬럼에 항상 idsize 명시
  • ✅ 키보드: Tab으로 셀 이동, 정렬/선택 조작
  • ✅ 로딩 상태에서 loading prop 활용
  • ❌ 과도한 컬럼 수로 가독성 저하 지양

관련 컴포넌트

Last updated on