Skip to Content

Grid

TanStack Table 기반의 선언적 데이터 그리드 컴포넌트입니다.


개요

Grid 컴포넌트는 <TableColumn> 자식 컴포넌트로 열을 선언적으로 정의하고, TanStack Table을 내부 엔진으로 사용하여 정렬, 페이지네이션, 행 선택 등을 지원합니다.

주요 특징

  • 선언적 API: <TableColumn> children으로 열 구성
  • 페이지네이션: Foundation Pagination 기반, 페이지 크기 선택 및 직접 이동 지원
  • 행 선택: 체크박스(다중) / 라디오(단일) 모드
  • 정렬: 클라이언트/서버 사이드 정렬 지원
  • 셀 유형: link, button, range(progress/rate), 조건부 서식
  • 커스텀 렌더링: renderCell prop으로 자유로운 셀 커스터마이징
  • 그룹 컬럼: <TableColumn> 중첩으로 멀티행 헤더 지원
  • 컨럼 표시/숨김: columnVisibility로 컨럼별 표시 여부 제어
  • 개인화 (Personalization): 컨럼 순서/크기/표시 상태 저장 및 복원 (비동기 저장소 지원)
  • 합계 행 (showSummary): 컨럼별 sum/avg/count/max/min 집계 표시
  • 컨럼 설정 (GridColumnSettings): 컨럼 표시/숨김 및 순서 변경 다이얼로그
  • 디자인 토큰: Foundation 토큰 시스템 호환

기본 사용

<Grid>datagetRowId를 전달하고, <TableColumn>으로 열을 정의합니다.


공통 —list-* 토큰 오버라이드

Grid의 최상위 컨테이너는 data-token="list"를 사용하며 List와 기존 32개 --list-* 토큰을 공유합니다. 컴포넌트 이름 Grid.grid-container 클래스는 그대로 유지됩니다. 테마는 별도의 --grid-*가 아닌 공통 --list-* 네임스페이스로 설정하세요.

Grid에는 style prop이 없습니다. 인라인 토큰을 적용하려면 아래처럼 data-token="list"인 HTML 래퍼의 style에 CSS 사용자 정의 속성을 지정하세요. 중첩된 Grid는 토큰 기본값을 다시 선언하지 않고 래퍼의 값을 상속합니다. List 내부에서도 같은 방식으로 List의 토큰을 상속합니다.

토큰적용 대상예제 값
--list-head-background헤더 배경#ede9fe
--list-cell-foreground셀 글자색#4c1d95
--list-cell-padding-x셀 좌우 여백18px
--list-row-background-selected선택 행 배경#dcfce7
--list-footer-background합계 행 배경#dbeafe

체크박스 선택과 상품명 검색·정렬을 조작해 토큰이 적용된 헤더, 셀, 선택 행, 합계 행을 확인하세요.

Foundation Table 내부 브리지

Grid는 내부 Foundation Table의 색상·글꼴·셀 여백을 --list-* 토큰에 연결합니다. --table-*는 내부 브리지로 사용되므로 Grid 테마를 변경하기 위해 직접 재정의할 필요가 없습니다. 높이와 모서리 반경 같은 구조적 Foundation Table 토큰은 그대로 유지합니다.

backgroundColor를 전달하면 --list-row-background와 자동 파생된 --list-row-background-hover, --list-row-background-selected 값이 내부 Grid에 우선 적용됩니다. 위 예제처럼 선택 행 색을 토큰으로 직접 제어하려면 이 prop을 생략하세요.

스타일시트에서는 화면 범위를 지정한 .orders [data-token="list"] 같은 선택자로도 동일한 토큰을 재정의할 수 있습니다.


행 선택

체크박스 (다중 선택)

selectable + selectionMode="checkbox"로 다중 행 선택을 활성화합니다.

라디오 (단일 선택)

selectionMode="radio"로 단일 행 선택을 활성화합니다.


페이지네이션

pageSize, pageSizeOptions, useGoToPage으로 페이지네이션을 구성합니다.


셀 유형 (uiType)

TableColumn의 uiType prop으로 셀 표시 유형을 지정합니다.


조건부 서식

conditionalFormatting으로 조건에 따라 셀 스타일을 적용합니다.


조건부 서식 — 확장 연산자

between(범위), in(목록 포함), isMatch(정규식) 연산자를 지원합니다. 여러 규칙이 동시에 매칭되면 스타일이 누적(병합)됩니다.


조건부 서식 — 행 스타일

applyTo: "row"를 지정하면 조건 통과 시 행 전체에 스타일이 적용됩니다.


그룹 컬럼 (멀티행 헤더)

<TableColumn> 안에 <TableColumn>을 중첩하면 그룹 컬럼(멀티행 헤더)을 구성할 수 있습니다. 그룹 컬럼의 field는 생략하고 title만 지정합니다.


합계 행 (showSummary)

showSummaryTableColumnsummary prop으로 컨럼별 집계를 표시합니다.

TableColumnSummary

속성타입설명
type"sum" | "avg" | "count" | "max" | "min" | "custom"집계 유형 (생략 시 label만 표시)
labelstring레이블 텍스트 (집계 대신 표시)
formatter(value: number) => string결과 포맷터

커스텀 셀 렌더링

renderCell prop으로 셀을 자유롭게 커스터마이징합니다.


API Reference

PropTypeDefaultDescription
dataT[]-테이블 데이터 배열
getRowId(row: T) => string-행 고유 ID 추출 함수 (필수)
childrenReactNode-<TableColumn> 자식 컴포넌트들
loadingbooleanfalse로딩 상태 표시
heightnumber | string-그리드 높이
rowHeightnumber-행 높이 (가상 스크롤 시)
classNamestring-그리드 컨테이너 CSS 클래스
selectablebooleanfalse행 선택 활성화
selectionMode"checkbox" | "radio""checkbox"선택 방식
selectedRowsstring[]-선택된 행 ID 배열 (controlled)
paginationbooleantrue페이지네이션 표시 여부
pageIndexnumber-현재 페이지 번호 (1-based, controlled)
pageSizenumber20페이지당 행 수
pageCountnumber-전체 페이지 수 (서버 페이징 시)
rowCountnumber-전체 행 수
pageSizeOptionsnumber[][10, 20, 50, 100]페이지 크기 선택 목록
useGoToPagebooleanfalse페이지 직접 이동 입력란 표시
paginationDisabledbooleanfalse로딩 시 페이지 탐색 비활성화
manualPaginationbooleanfalse서버 사이드 페이징 여부
manualSortingbooleanfalse서버 사이드 정렬 여부
showRowNumberbooleanfalse행 번호 열 표시
stripedbooleanfalse줄무늬 행 표시
fullSizebooleanfalse화면 가득 채움
enableColumnResizebooleantrue열 너비 조절 활성화
enableDragCopybooleanfalse행 텍스트 드래그 복사 활성화
virtualScrollbooleanfalse가상 스크롤 활성화
columnSearchGridColumnSearchState-컬럼 헤더 검색 조건 (controlled)
clientSearchbooleanfalse클라이언트 사이드 검색 필터링 활성화
columnVisibilityRecord<string, boolean>-컨럼 표시/숨김 상태 (controlled)
onColumnVisibilityChange(visibility: Record<string, boolean>) => void-컨럼 표시 변경 콜백
columnOrderstring[]-컬럼 순서 (controlled, 필드명 배열)
onColumnOrderChange(order: string[]) => void-컬럼 순서 변경 콜백
personalizationGridPersonalizationConfig-개인화 저장/복원 설정
showSummarybooleanfalse합계 행 표시
overlayGridOverlayConfig[]-오버레이 목록
backgroundColorstring-그리드 배경색

Grid Events

EventTypeDescription
onRowClick(row: T, rowIndex: number) => void행 클릭
onRowDoubleClick(row: T, rowIndex: number) => void행 더블클릭
onRowContextMenu(row: T, rowIndex: number, event: React.MouseEvent) => void행 우클릭 컨텍스트 메뉴
onLoad(params: GridLoadParams) => void데이터 로드 요청
onSortChange(sorting: SortingState) => void정렬 변경
onPageChange(pageIndex: number) => void페이지 변경
onPageSizeChange(pageSize: number) => void페이지 크기 변경
onSelectionChange(selectedIds: string[], selectedRows: T[]) => void선택 변경
onColumnResize(columnId: string, width: number) => void열 너비 변경
onColumnSearch(field: string, value: GridColumnSearchValue | undefined, search: GridColumnSearchState) => void컬럼 검색 실행
onScrollEnd() => void스크롤 하단 도달 (무한스크롤)

TableColumn Props

주요 props만 요약합니다. 전체 명세는 TableColumn 문서를 참조하세요.

PropTypeDefaultDescription
fieldstring-데이터 필드명 (그룹 컬럼은 생략 가능)
titlestring-열 헤더 텍스트 (필수)
widthnumber-열 너비 (px)
minWidthnumber-최소 열 너비
sortablebooleanfalse정렬 가능 여부
searchablebooleanfalse검색 가능 여부 (헤더 검색 영역 표시)
searchType"text" | "date" | "select" | "popup""text"검색 입력 유형
enabledbooleantrue열 활성화 여부
hiddenbooleanfalse시각적 숨김
fixedboolean | "left" | "right"-열 고정 방향
uiType"text" | "link" | "button" | "range" | …"text"셀 표시 유형
formatterTableColumnFormatter-값 포맷터 (천단위 구분 등)
conditionalFormattingTableColumnConditionalRule[]-조건부 서식 규칙
childrenReactNode-하위 <TableColumn> (그룹 컬럼용)
renderCell(value, row, rowIndex) => ReactNode-커스텀 셀 렌더 함수
onLinkClick(row: T) => void-uiType=“link” 클릭 콜백
onButtonClick(row: T) => void-uiType=“button” 클릭 콜백
summaryTableColumnSummary-합계 행 설정 (type, label, formatter)

서버 사이드 페이징 / 정렬

manualPaginationmanualSorting을 활성화하면 Grid는 클라이언트에서 데이터를 자르지 않고, onLoad/onPageChange/onSortChange 콜백을 통해 서버에 요청합니다.

onLoad vs onPageChange: onLoad는 Grid 마운트 시 1회 호출됩니다. 이후 페이지 변경은 onPageChange, 정렬 변경은 onSortChange가 호출됩니다.


컬럼 검색

searchable TableColumn과 함께 사용합니다. clientSearch={true}이면 Grid가 직접 필터링하고, false(기본)이면 onColumnSearch 콜백으로 조건만 전달합니다.


인라인 편집 모드

inlineEditMode로 Grid의 편집 상태를 제어합니다. "edit" 모드에서는 editable로 지정한 셀이 편집 가능해집니다.

모드설명
"none"읽기 전용 (기본)
"add"새 행 추가 모드
"edit"기존 행 편집 모드
"delete"삭제 모드

Tip: List 컴포넌트를 사용하면 InlineEditToolbar와 함께 추가/수정/삭제/저장/취소 워크플로우를 쉽게 구성할 수 있습니다.


ScrollArea 내부 스크롤

Grid의 스크롤 영역은 Foundation ScrollArea / ScrollBar를 사용합니다. height, fullSize, virtualScroll, onScrollEnd 등 기존 공개 API는 그대로이며, Grid에 viewportRefviewportProps를 추가할 필요가 없습니다.

가상 행 계산, 무한 스크롤의 하단 감지, 열 리사이즈는 내부 Viewport의 실제 스크롤 위치와 크기를 기준으로 동작합니다. 가로·세로 스크롤바를 제공하며, 스크롤바 너비와 thumb 색상은 List의 스크롤바 스타일 예제처럼 중첩된 data-token="scroll-area" 루트에 Foundation 토큰을 지정해 변경합니다. 기존 --list-* 토큰과는 독립적입니다.

가상 스크롤

virtualScroll을 활성화하면 화면에 보이는 행만 렌더링하여 대용량 데이터(수천~수만 행)에서도 성능을 유지합니다. 이 예제는 기존 페이지 모델에서 전체 데이터를 한 페이지로 표시하도록 pageSize={bigData.length}를 지정합니다. pagination={false}는 페이지 탐색 UI를 숨기는 설정입니다.

주의: virtualScroll 사용 시 height 지정이 필수입니다. rowHeight는 각 행의 예상 높이로, 정확한 값을 지정할수록 스크롤이 부드럽습니다.


오버레이

overlay prop으로 Grid 위에 메시지/아이콘 오버레이를 표시합니다. 권한 잠금, 안내 메시지 등에 사용합니다.


드래그 복사

enableDragCopy를 활성화하면 셀 텍스트를 드래그로 선택·복사할 수 있습니다. 기본적으로 Grid는 텍스트 선택이 비활성화되어 있습니다.


컬럼 표시/숨김 (columnVisibility)

columnVisibilityonColumnVisibilityChange로 컬럼별 표시 여부를 제어합니다.

import { useState } from "react"; import { Grid, TableColumn } from "@vortex/ui-cals"; function ColumnVisibilityExample() { const [visibility, setVisibility] = useState<Record<string, boolean>>({ name: true, price: true, category: false, // 초기에 숨김 }); return ( <> {/* 토글 버튼 예시 */} <button onClick={() => setVisibility((v) => ({ ...v, category: !v.category }))}> 카테고리 {visibility.category ? "숨기기" : "보이기"} </button> <Grid data={data} getRowId={(row) => row.id} columnVisibility={visibility} onColumnVisibilityChange={setVisibility} > <TableColumn field="name" title="이름" /> <TableColumn field="price" title="가격" /> <TableColumn field="category" title="카테고리" /> </Grid> </> ); }

개인화 (Personalization)

personalization prop으로 컬럼 순서, 크기, 표시 상태를 저장/복원합니다. 비동기 저장소(IndexedDB 등)도 지원합니다.

import { Grid, TableColumn } from "@vortex/ui-cals" // 동기 저장소 (localStorage) const syncPersonalization = { getItem: () => { const saved = localStorage.getItem("grid-state") return saved ? JSON.parse(saved) : null }, setItem: (state) => localStorage.setItem("grid-state", JSON.stringify(state)), } // 비동기 저장소 (IndexedDB 등) const asyncPersonalization = { getItem: async () => { const db = await openMyDB() return await db.get("grid-state", "main-grid") }, setItem: async (state) => { const db = await openMyDB() await db.put("grid-state", state, "main-grid") }, } <Grid data={data} getRowId={(row) => row.id} personalization={asyncPersonalization} // Promise 반환도 OK > <TableColumn field="name" title="이름" /> <TableColumn field="price" title="가격" /> </Grid>

getItem()Promise를 반환하면 내부적으로 .then()으로 처리합니다.


접근성

권장 사항

  • <table> 시맨틱 요소를 사용하여 스크린 리더 호환
  • ✅ 로딩 시 aria-busy="true" 자동 적용
  • ✅ 체크박스/라디오는 키보드(Space)로 토글 가능
  • ✅ 페이지 탐색 버튼은 키보드로 접근 가능

관련 컴포넌트

Last updated on