D·Camp UI

Analytics

DataTable

Dark Mode

DataTable, TableToolbar, TablePagination 등 하위 컴포넌트를 조합하여 다양한 형태의 데이터 테이블을 구성할 수 있는 합성 컴포넌트입니다.

import { DataTable } from "@/shared/ui/widget/analytics/table"

합성 컴포넌트 구조

DataTable은 단독으로 사용하지 않고, 하위 컴포넌트를 직접 조합하여 구성합니다. useDataTable hook이 모든 상태를 통합 관리하며, 각 컴포넌트에 toolbarProps, tableProps, paginationProps를 스프레드하면 자동으로 연동됩니다.

요소역할
useDataTable검색 · 필터 · 정렬 · 컬럼 가시성 · 페이지네이션 · 그룹화 · config 직렬화를 통합 관리하는 hook
useJoinedDataTable마스터 키 기준으로 여러 DataSource를 가로 병합하는 hook. 내부적으로 useDataTable을 확장
TableToolbar필터 · 검색 · 다중 정렬 · 필드 가시성 + 커스텀 액션 슬롯
DataTable컬럼 헤더 렌더링 (클릭 → 컬럼명/정렬/너비 설정 팝오버)
TableRow / TableCell행과 셀. Cell의 variant로 date · status · tag 프리셋 지원
GroupHeaderRow그룹 토글 헤더. 가로 스크롤 시 라벨 고정, 접기/펴기 지원
TablePagination페이지 이동 · 페이지 크기 선택 (그룹화 시 자동 비활성화)
기본 사용법 — useDataTable
// 1. Hook으로 상태 생성
const table = useDataTable({
  data, columns,
  pageSize: 10,
  rowKey: "id",
});

// 2. 각 영역에 props 스프레드
<TableToolbar {...table.toolbarProps} />

<DataTable {...table.tableProps}>
  {table.rows.map(row => (
    <TableRow key={row.id}>
      <TableCell>{row.name}</TableCell>
      <TableCell variant="status" statusColor={row.color}>
        {row.grade}
      </TableCell>
    </TableRow>
  ))}
</DataTable>

<TablePagination {...table.paginationProps} />

포트폴리오 테이블

Toolbar의 필터 · 검색 · 다중 정렬 · 필드 가시성과 헤더 팝오버, 페이지네이션이 모두 연동되는 전체 조합 예시입니다.

포트폴리오 회사
포트폴리오 회사현재 가치회수액MOICIRR등급섹터Date
(주)테크스타트12억3억2.4x35%In Progress딥테크2018
(주)헬스랩4.5억1억1.5x18%Done바이오2021
(주)그린에너지8억00.8x-5%Pending클린테크2023
(주)핀테크솔루션15억5억2.9x42%Cancelled핀테크2020
(주)푸드커넥트2.5억0.5억1.3x12%In ReviewF&B2022
(주)에듀플러스6억2억2.0x28%Done에듀테크2021
(주)로보틱스AI20억02.5x38%In Progress딥테크2023
(주)클라우드넷3억1억0.7x-8%CancelledSaaS2019

TableViewConfig — 테이블 상태 직렬화

table.config는 현재 테이블의 필터 · 정렬 · 컬럼 너비 · 가시성 등을 JSON으로 직렬화한 객체입니다. DB에 저장했다가 applyConfig()로 복원하면 어디서 열어도 동일한 테이블 뷰를 재현할 수 있습니다.

config 사용법
// table.config — 현재 테이블 상태를 JSON으로 직렬화
const { config, applyConfig } = useDataTable({ ... });

// config 값 예시
{
  "filters": [{ "columnKey": "sector", "values": ["딥테크"] }],
  "sortConfigs": [{ "key": "irr", "direction": "desc" }],
  "visibleKeys": ["company", "irr", "grade"],
  "columns": [{ "key": "company", "width": 200 }],
  "searchKey": "company",
  "pageSize": 10,
  "collapsedGroups": []
}

// DB에서 불러온 설정 복원
applyConfig(savedConfig);

table.config — 현재 포트폴리오 테이블 설정 (실시간)

{
  "filters": [],
  "sortConfigs": [],
  "visibleKeys": [
    "company",
    "currentValue",
    "recovery",
    "moic",
    "irr",
    "grade",
    "sector",
    "date"
  ],
  "columns": [
    {
      "key": "company",
      "label": "포트폴리오 회사",
      "width": 180
    },
    {
      "key": "currentValue",
      "label": "현재 가치",
      "width": 90
    },
    {
      "key": "recovery",
      "label": "회수액",
      "width": 80
    },
    {
      "key": "moic",
      "label": "MOIC",
      "width": 80
    },
    {
      "key": "irr",
      "label": "IRR",
      "width": 80
    },
    {
      "key": "grade",
      "label": "등급",
      "width": 110
    },
    {
      "key": "sector",
      "label": "섹터",
      "width": 120
    },
    {
      "key": "date",
      "label": "Date",
      "width": 100
    }
  ],
  "searchKey": "company",
  "pageSize": 10,
  "collapsedGroups": []
}

데이터 소스 병합 (Horizontal JOIN)

useJoinedDataTable hook을 사용하면 마스터 키를 기준으로 여러 데이터 소스의 컬럼을 가로로 병합할 수 있습니다. 아래 칩으로 활성 소스를 토글해 보세요. 마스터 키 컬럼은 가로 스크롤 시에도 왼쪽에 고정됩니다.

데이터 소스 병합 — useJoinedDataTable
// 데이터 소스 정의
const sources: DataSource[] = [
  {
    id: "investment",
    label: "투자",
    color: "primary",
    columns: [
      { key: "value", label: "현재 가치", sortable: true },
      { key: "irr",   label: "IRR",       sortable: true },
    ],
    data: investmentData,
  },
  // ...다른 소스
];

// Hook 생성
const joined = useJoinedDataTable({
  masterKey: "companyName",
  masterColumn: { key: "companyName", label: "기업명", primary: true },
  sources,
  initialActiveSources: ["investment"],
});

// 소스 토글 칩
<SourceChips
  sources={joined.availableSources}
  activeSources={joined.activeSources}
  onToggle={joined.toggleSource}
/>

// 테이블 렌더링 (masterKey 컬럼은 자동 고정)
<DataTable {...joined.tableProps}>
  {joined.rows.map(row => (
    <TableRow key={String(row.companyName)}>
      {joined.visibleKeys.map(key => (
        <TableCell key={key} primary={key === "companyName"}>
          {String(row[key] ?? "")}
        </TableCell>
      ))}
    </TableRow>
  ))}
</DataTable>
행 그룹화 — groupBy
// groupBy로 행 그룹화 (그룹화 시 페이지네이션 자동 비활성화)
const joined = useJoinedDataTable({
  masterKey: "companyName",
  masterColumn: masterCol,
  sources,
  groupBy: {
    key: "building",
    label: "건물",
    resolve: { "(주)테크스타트": "A동", "(주)헬스랩": "B동", ... },
  },
});

// groups가 있으면 그룹 렌더링, 없으면 flat 렌더링
<DataTable {...joined.tableProps}>
  {joined.groups?.map(group => (
    <Fragment key={group.key}>
      <GroupHeaderRow
        label={`${joined.groupByLabel}: ${group.key}`}
        count={group.count}
        collapsed={group.collapsed}
        onToggle={() => joined.toggleGroup(group.key)}
      />
      {!group.collapsed && group.rows.map(row => (
        <TableRow key={...}>
          {/* cells */}
        </TableRow>
      ))}
    </Fragment>
  ))}
</DataTable>
기업명
투자
기업명현재 가치회수액MOICIRR
건물: A동(4건)
(주)테크스타트12억3억2.4x35%
(주)그린에너지8억00.8x-5%
(주)푸드커넥트2.5억0.5억1.3x12%
(주)로보틱스AI20억02.5x38%
건물: B동(4건)
(주)헬스랩4.5억1억1.5x18%
(주)핀테크솔루션15억5억2.9x42%
(주)에듀플러스6억2억2.0x28%
(주)클라우드넷3억1억0.7x-8%

TableCell Variants

Variant미리보기설명
text (기본)일반 텍스트 내용variant 생략 시 기본값
date2024-03-15캘린더 아이콘 + 날짜 텍스트
status
DoneIn ProgressPending
컬러 dot + 상태 텍스트
tag딥테크pill 배경의 태그 형태

Props

DataTable

PropTypeRequiredDefault설명
columnsTableColumn[]컬럼 정의 목록. key, label, width, sortable, align, primary 속성을 가집니다.
childrenReactNode테이블 바디 영역. TableRow + TableCell 조합을 전달합니다.
sortConfigsSortConfig[][]다중 정렬 설정 목록. 순서대로 1차·2차·N차 정렬이 적용됩니다.
onColumnUpdate(key: string, updates: ColumnUpdate) => void헤더 팝오버에서 컬럼명/정렬/너비 변경 시 호출. 설정 시 헤더 클릭으로 팝오버가 열립니다.
sourceGroupsSourceGroup[]데이터 소스 그룹 목록. 설정 시 컬럼 헤더 위에 소스별 그룹 헤더가 표시됩니다.
classNamestring루트 요소 추가 클래스.

TableRow

PropTypeRequiredDefault설명
childrenReactNodeTableCell 목록.
selectedboolean행 선택 하이라이트 상태.
onClick() => void행 클릭 핸들러.
classNamestring추가 클래스.

TableCell

PropTypeRequiredDefault설명
childrenReactNode셀 콘텐츠.
variantCellVariant'text'셀 프리셋 유형. date(캘린더 아이콘), status(컬러 Dot), tag(pill 배경).
statusColorStatusColor'grey'variant='status' 전용. dot과 텍스트 색상을 결정합니다.
align'left' | 'center' | 'right''left'텍스트 정렬.
primaryboolean마스터 키 컬럼 여부. true이면 좌측 sticky 고정됩니다.
classNamestring추가 클래스.