Analytics
DataTable
Dark ModeDataTable, 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의 필터 · 검색 · 다중 정렬 · 필드 가시성과 헤더 팝오버, 페이지네이션이 모두 연동되는 전체 조합 예시입니다.
포트폴리오 회사
| 포트폴리오 회사 | 현재 가치 | 회수액 | MOIC | IRR | 등급 | 섹터 | Date | |
|---|---|---|---|---|---|---|---|---|
| (주)테크스타트 | 12억 | 3억 | 2.4x | 35% | In Progress | 딥테크 | 2018 | |
| (주)헬스랩 | 4.5억 | 1억 | 1.5x | 18% | Done | 바이오 | 2021 | |
| (주)그린에너지 | 8억 | 0 | 0.8x | -5% | Pending | 클린테크 | 2023 | |
| (주)핀테크솔루션 | 15억 | 5억 | 2.9x | 42% | Cancelled | 핀테크 | 2020 | |
| (주)푸드커넥트 | 2.5억 | 0.5억 | 1.3x | 12% | In Review | F&B | 2022 | |
| (주)에듀플러스 | 6억 | 2억 | 2.0x | 28% | Done | 에듀테크 | 2021 | |
| (주)로보틱스AI | 20억 | 0 | 2.5x | 38% | In Progress | 딥테크 | 2023 | |
| (주)클라우드넷 | 3억 | 1억 | 0.7x | -8% | Cancelled | SaaS | 2019 |
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>기업명
| 투자 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 기업명 | 현재 가치 | 회수액 | MOIC | IRR | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
건물: A동(4건) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (주)테크스타트 | 12억 | 3억 | 2.4x | 35% | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (주)그린에너지 | 8억 | 0 | 0.8x | -5% | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (주)푸드커넥트 | 2.5억 | 0.5억 | 1.3x | 12% | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (주)로보틱스AI | 20억 | 0 | 2.5x | 38% | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
건물: B동(4건) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (주)헬스랩 | 4.5억 | 1억 | 1.5x | 18% | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (주)핀테크솔루션 | 15억 | 5억 | 2.9x | 42% | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (주)에듀플러스 | 6억 | 2억 | 2.0x | 28% | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (주)클라우드넷 | 3억 | 1억 | 0.7x | -8% | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
TableCell Variants
| Variant | 미리보기 | 설명 |
|---|---|---|
text (기본) | 일반 텍스트 내용 | variant 생략 시 기본값 |
date | 2024-03-15 | 캘린더 아이콘 + 날짜 텍스트 |
status | DoneIn ProgressPending | 컬러 dot + 상태 텍스트 |
tag | 딥테크 | pill 배경의 태그 형태 |
Props
DataTable
| Prop | Type | Required | Default | 설명 |
|---|---|---|---|---|
columns | TableColumn[] | ✓ | — | 컬럼 정의 목록. key, label, width, sortable, align, primary 속성을 가집니다. |
children | ReactNode | ✓ | — | 테이블 바디 영역. TableRow + TableCell 조합을 전달합니다. |
sortConfigs | SortConfig[] | — | [] | 다중 정렬 설정 목록. 순서대로 1차·2차·N차 정렬이 적용됩니다. |
onColumnUpdate | (key: string, updates: ColumnUpdate) => void | — | — | 헤더 팝오버에서 컬럼명/정렬/너비 변경 시 호출. 설정 시 헤더 클릭으로 팝오버가 열립니다. |
sourceGroups | SourceGroup[] | — | — | 데이터 소스 그룹 목록. 설정 시 컬럼 헤더 위에 소스별 그룹 헤더가 표시됩니다. |
className | string | — | — | 루트 요소 추가 클래스. |
TableRow
| Prop | Type | Required | Default | 설명 |
|---|---|---|---|---|
children | ReactNode | ✓ | — | TableCell 목록. |
selected | boolean | — | — | 행 선택 하이라이트 상태. |
onClick | () => void | — | — | 행 클릭 핸들러. |
className | string | — | — | 추가 클래스. |
TableCell
| Prop | Type | Required | Default | 설명 |
|---|---|---|---|---|
children | ReactNode | — | — | 셀 콘텐츠. |
variant | CellVariant | — | 'text' | 셀 프리셋 유형. date(캘린더 아이콘), status(컬러 Dot), tag(pill 배경). |
statusColor | StatusColor | — | 'grey' | variant='status' 전용. dot과 텍스트 색상을 결정합니다. |
align | 'left' | 'center' | 'right' | — | 'left' | 텍스트 정렬. |
primary | boolean | — | — | 마스터 키 컬럼 여부. true이면 좌측 sticky 고정됩니다. |
className | string | — | — | 추가 클래스. |