- new workspace package packages/chart (shadcn/ui Chart + Recharts, HM Theme tokens), examples entry with sample data - Storybook Chart group (src/chart), consumer example (examples/chart), build:chart / build:chart-example scripts - README rewritten as the AI usage guide (install, CSS wiring, chart selection, data rules, verification) - docs: package count/list, Submodule guide (chart workspace, React dedupe), shared AI_GUIDE row, MAINTAINING, story inventory
13 KiB
@hm-design/chart
공식 shadcn/ui Chart(recharts@3.8.0) 기반의 HM 차트 패키지입니다. Bar(세로·가로·부호)·Line·Donut·Pie·Radar·Area·Radial 차트와 shadcn Chart 기본 부품을 제공하며, 색상·카드·글꼴은 @hm-design/ui의 HM Theme 토큰을 그대로 사용합니다. 색상값(HEX)은 이 패키지에 정의하지 않습니다.
이 패키지에는 별도
ai/폴더가 없고 이 README가 AI 코딩 도구용 사용 지침을 겸합니다(공통AI_GUIDE.md의 "기타" 유형). 아래 순서대로 따릅니다: ① 설치·연결 → ② 차트 고르기 → ③ 데이터 구성 → ④ 렌더링 → ⑤ 검증.
1. 설치·연결
- 이 저장소를 Git Submodule로 포함한 프로젝트는 외부 설치 가이드의 절차를 따릅니다. HMDesign 안에서
npm ci후npm run build:ui와npm run build:chart(또는npm run build:packages)를 먼저 실행해야dist/가 생깁니다.dist/는 Git에 없습니다. - 소비 프로젝트의 npm Workspace에는
HMDesign/packages/ui와HMDesign/packages/chart를 함께 추가합니다.@hm-design/chart는@hm-design/ui(0.1.0),react·react-dom(^19),tailwindcss(^4)를 peer로 요구하며, 차트가 직접 쓰는 것은@hm-design/ui의 Theme CSS 토큰(theme.css)뿐입니다(@hm-design/ui의 JS는 import하지 않음). - 이 저장소 안에서는 루트 workspace(
packages/*)로 이미 연결되어 있습니다.
CSS 연결 (Tailwind v4 앱)
앱의 CSS 진입점(예: src/index.css)에서 @import 순서대로 연결합니다.
@import "tailwindcss" source("./");
@import "@hm-design/ui/tailwind.css";
@import "@hm-design/chart/tailwind.css";
@import "@hm-design/ui/theme.css";
@hm-design/chart/tailwind.css는 차트 마크업의 Tailwind 유틸리티를 소비 앱 빌드가 만들도록 하는@source한 줄입니다. 연결하지 않으면 그 유틸리티 class가 소비 앱 CSS에 생성되지 않습니다.@hm-design/ui/theme.css는 차트 색 별칭(--hm-chart-*)이 가리키는--hm-sem-*토큰과 Noto Sans KR 글꼴을 제공합니다.- 차트 스타일(
@hm-design/chart/style.css)은 JS 진입점(@hm-design/chart,@hm-design/chart/examples)이 자동으로 불러오므로 따로 import하지 않습니다. <html>·<body>에 배경·글꼴을 정하는 것은 소비 앱의 몫입니다(차트는 전역 규칙을 넣지 않습니다).
2. 진입점과 차트 고르기
| Entry | 내용 |
|---|---|
@hm-design/chart |
차트 컴포넌트(아래 표), shadcn Chart 부품(ChartContainer, ChartTooltip(Content), ChartLegend(Content), ChartStyle, ChartConfig), chartColors, 타입 |
@hm-design/chart/examples |
예시 데이터와 예제 카드(ChartExamples 및 *Example 컴포넌트, sample* 데이터, SampleDataBadge). 실제 업무 데이터가 아니며 운영 화면에 그대로 쓰지 않습니다. |
@hm-design/chart/tailwind.css |
Tailwind 유틸리티 스캔용 @source |
@hm-design/chart/style.css |
차트 토큰·스타일(JS 진입점이 자동으로 불러옴) |
| 보여 줄 것 | 컴포넌트 | 데이터 모양 |
|---|---|---|
| 카테고리별 값 비교(그룹·누적) | HMBarChart |
data 행 + categoryKey + series[] |
| 항목명이 긴 순위·값 비교 | HMHorizontalBarChart |
data 행 + categoryKey + valueKey + valueLabel |
| 증감(양수·음수) | HMSignedBarChart |
data 행 + categoryKey + valueKey + valueLabel |
| 추이 | HMLineChart |
data 행 + categoryKey + series[] |
| 시간 흐름의 양·구성비 | HMAreaChart |
data 행 + categoryKey + series[] |
| 전체 대비 비중 | HMDonutChart, HMPieChart |
data: { id, label, value, color? }[] |
| 두 구성의 동심원 비교 | HMStackedPieChart |
rings: [내부, 외부] |
| 여러 지표의 균형 | HMRadarChart |
data 행 + categoryKey + series[] |
| 목표 대비 진행률·항목별 막대 | HMRadialBarChart, HMRadialMeterChart, HMRadialStackedChart |
위 Donut 데이터 / item / series[]+row |
3. 데이터 구성 규칙
type ChartSeries = { key: string; label: string; color?: string; lightColor?: string }
type HMDonutDatum = { id: string; label: string; value: number; color?: string }
- 실제 데이터는 소비 앱이 props로 전달합니다. 이 패키지는 API·DB에 연결하지 않습니다.
series.key와HMDonutDatum.id는 CSS 변수 이름(--color-<key>)으로 쓰이므로 영문·숫자·-·_만 사용합니다(한글·공백 금지). 화면에 보이는 이름은label에 씁니다.- 색상을 지정하지 않으면
--hm-chart-1~5(HM 그린·브라운·웜 뉴트럴 팔레트 별칭)를 순서대로 사용합니다. 다른 색은color에 CSS 색상/토큰을 전달합니다. - 범례 라벨은 짧게 유지합니다. Bar·Line·Radar·Area의 범례는 한 줄로 두며, 직접
flex-wrap으로 줄바꿈시키면 Recharts가 범례 높이를 과대 측정해 차트 아래에 빈 공간이 생길 수 있습니다. Donut·Pie·Stacked Pie는 컴포넌트 내부에서 줄바꿈 범례를 쓰도록 구성되어 있고, Radial 계열은 범례를 차트 박스 밖 아래에 따로 놓습니다. - 모든 차트의
label은 필수 접근성 이름(role="group"의aria-label)입니다. - 차트는 부모 폭을 따라 줄어듭니다(
w-full, 높이h-64→sm:h-80).className으로 높이를 바꿀 수 있습니다. 부모에min-width: 0이 없으면 flex·grid 안에서 넘칠 수 있습니다.
4. 렌더링
import { HMBarChart, HMLineChart, HMDonutChart } from "@hm-design/chart"
const received = [{ month: "1월", received: 186, completed: 142 }]
<HMBarChart
label="월별 접수·완료 건수"
data={received}
categoryKey="month"
series={[{ key: "received", label: "접수" }, { key: "completed", label: "완료" }]}
/>
<HMLineChart label="처리 시간" data={rows} categoryKey="month" series={[{ key: "average", label: "평균" }]} />
<HMDonutChart label="상태 비중" totalLabel="전체(건)" data={[{ id: "done", label: "완료", value: 118 }]} />
가로형 라벨 막대(HMHorizontalBarChart)는 항목명을 막대 안쪽 왼쪽에, 값을 막대 끝 바깥에 표시합니다. 그라데이션은 왼쪽 30% → 오른쪽 100%이고(세로형은 상단 100% → 하단 30%), 막대에 들어가지 않는 긴 항목명은 …로 줄이며 막대가 너무 짧거나 0이면 값 뒤에 항목명을 보여 줍니다. 전체 항목명은 툴팁에서 확인합니다. 선택 props는 color, valueFormatter입니다. HMSignedBarChart는 양수는 0선 위로, 음수는 아래로 그립니다. Stacked 세로형 막대(HMBarChart stacked)는 눈금선이 비치지 않도록 각 세그먼트를 불투명한 두 색(시리즈 색 → 같은 계열의 한 단계 밝은 HM 팔레트 색, --hm-chart-N-light)으로 칠합니다. 사용자 지정 color는 lightColor를 함께 주지 않으면 단색 세그먼트가 됩니다.
<HMHorizontalBarChart
label="항목별 처리 건수"
data={rows}
categoryKey="item"
valueKey="count"
valueLabel="처리 건수"
/>
선 차트(HMLineChart) 옵션: curve("monotone" 기본 | "linear" | "step", 모든 시리즈에 적용), dots(기본 true; false면 기본 Dot 없이 선만 그리고 호버 시 활성 포인트·툴팁은 유지), pointLabels(큰 Dot + 값 라벨), pointColorKey(포인트별 색상 필드 — 문자열은 첫 시리즈, 객체는 { 시리즈키: 필드명 }; 선·범례는 시리즈 색, Dot·라벨·툴팁 표시는 포인트 색). 시리즈가 둘 이상이면 같은 카테고리에서 값이 큰 쪽 라벨은 위, 작은 쪽은 아래에 두고, 선·Dot·다른 라벨·축 라벨과 닿으면 가까운 빈 자리(더 띄우기, 반대편, 좌우)로 옮깁니다. 점 간격이 라벨 폭보다 좁으면 라벨을 n개마다 표시하고 툴팁에는 모든 값이 나옵니다.
도넛 차트(HMDonutChart) 옵션: totalLabel(가운데에 전체 합계와 캡션), activeId(선택한 조각을 바깥쪽으로 확장하고 가운데에 그 이름·값 표시), onActiveChange(조각 클릭 시 id 전달; 상태는 소비 앱이 보관하며 호버로는 바뀌지 않음). 가운데 텍스트는 도넛 안쪽을 넘지 않도록 숫자를 줄이고(1234.6억 같은 축약) 이름은 …로 줄이며, 합계가 0이면 빈 링과 0을 표시합니다. 전체 이름은 범례와 툴팁에서 확인합니다.
파이(HMPieChart, HMStackedPieChart): labels("outside" 조각 바깥 "이름 값"과 연결선, "inside" 조각 안 값)는 놓을 자리가 없으면 해당 Label만 생략하고 툴팁에는 전체 정보가 나옵니다. HMStackedPieChart는 rings(안쪽·바깥쪽 두 시리즈, 같은 id는 같은 색)로 동심원 링을 그립니다. 방사형(HMRadarChart): filled(기본 영역, false면 선만), dots, customLabels(축 이름을 두 줄로 줄이거나 …로 줄임), maxValue(공통 축 상한, 기본 100). 영역(HMAreaChart): curve(monotone·linear·step), dots, pointLabels·pointColorKey(Line과 같은 Dot·Label·충돌 처리), stacked(값 누적), expanded(각 시점 합계를 100%로 정규화, Y축 %, 툴팁에 원래 값과 비율; 합계 0이면 0%). 영역 채움은 시리즈 색의 세로 그라데이션(위 진하게, 아래 옅게)입니다.
래디얼(HMRadialBarChart, HMRadialMeterChart, HMRadialStackedChart)은 공식 shadcn/ui radial 예제(simple·label·grid·text·shape·stacked)의 각도·반지름·트랙·중앙 Text를 따릅니다(labels, grid, startAngle/endAngle, innerRadius/outerRadius, trackRadii, cornerRadius). 중앙 숫자는 CenterText가 구멍 폭에 맞춰 줄이거나 1234.6억처럼 축약합니다. 도넛·래디얼의 링·막대 두께는 공식 예제의 1.5배입니다(2026-11-06): 단일 막대(text·shape·stacked)는 바깥 반지름을 두고 안쪽 반지름을 줄였고, 여러 막대(simple·label·grid)는 barSize(18, grid 16.5)·ringGap(4, grid 3, 기존 간격)과 기존 중앙 구멍(innerRadius 30)을 유지해 outerRadius를 innerRadius + 항목 수 × (barSize + ringGap)(5개: 140, grid 127.5)로 계산합니다. 래디얼 차트 영역 높이는 300px로 같고 범례는 차트 영역 아래에 따로 놓이며, 폭이 모자라면(320px) simple·label·grid의 반지름·두께·간격이 비례해 줄어듭니다. HMRadialMeterChart의 trackRadii는 막대와 같은 두께로 맞춥니다. labels(항목명 Label)는 막대 안쪽 시작 부분에 호를 따라 그려지며(insideStart) 막대 호 길이에 들어가지 않는 긴 이름은 …로 줄입니다(전체 이름은 범례와 툴팁).
위 설명은 코드(
src/components/*.tsx의 Props 타입·기본값)와 Storybook(Chart그룹)에서 확인된 동작만 적었습니다. 아래 "예제 컴포넌트"의*Example이름은@hm-design/chart/examples에서 확인합니다.
5. 검증
npm run build:ui && npm run build:chart # HMDesign 안: packages/ui/dist, packages/chart/dist
npm run typecheck -w @hm-design/chart
npm run build:chart-example # examples/chart (소비 예제 빌드)
npm run dev:chart-example # 소비 예제 실행
npm run storybook # Chart 그룹: Bar / Line / Donut / Pie / Radar / Area / Radial
- 소비 프로젝트에서는 앱의
tsc --noEmit·vite build후 브라우저에서 차트가 렌더링되고(SVG 표시) 툴팁·범례가 보이는지, 320px에서 가로 스크롤이 없는지 확인합니다. - 애니메이션이 끝난 뒤(약 1.5초) 화면을 확인합니다. 애니메이션 중에는 막대·Label이 일부만 그려집니다.
예제 컴포넌트
소비 예제(examples/chart/main.tsx)와 Storybook은 @hm-design/chart/examples의 예제 카드를 사용합니다: Bar(BarChartExample, StackedBarChartExample, HorizontalBarChartExample, SignedBarChartExample), Line(LineChartExample 외 Curve·Straight·Step 변형), Donut(DonutChartExample 외 Plain·Center·Active·Interactive), Pie(PiePlainExample, PieLabelExample, PieLabelListExample, PieStackedExample), Radar(RadarPlainExample, RadarDotExample, RadarLinesOnlyExample, RadarCustomLabelExample), Area(AreaCurveExample 외 Straight·Step·Stacked·Expanded), Radial(RadialSimpleExample, RadialLabelExample, RadialGridExample, RadialTextExample, RadialShapeExample, RadialStackedExample), 묶음 ChartExamples. 예시 데이터(sampleBar, samplePie, sampleRadial 등)는 같은 진입점에서 export됩니다. 예제 카드는 ExampleCard(내부 컴포넌트)로 감싸며 실제 업무 데이터가 아님을 표시합니다.
공식 shadcn Chart와의 차이
src/components/chart.tsx는 npx shadcn view chart(radix-nova)의 공식 소스입니다. 차이는 세 가지뿐입니다: Dark 테마 항목 제거(HM은 Dark 없음), 툴팁·범례에 data-slot 추가, 툴팁 값의 font-mono 제거.
제한
- 차트 전용 HM 디자인 규격은 아직 없습니다. 색은 기존 HM 팔레트 Semantic 토큰의 별칭이며(
src/chart.css), 차트 Token은 이 패키지 안에서만 정의합니다. @hm-design/chart/examples의ExampleCard는 공개 Export가 아닙니다(예제 카드를 통해서만 사용).- Dark 테마 없음. Runtime Theme(
@hm-design/ui/theme)의 팔레트 override는 차트 별칭을 통해 반영됩니다.