headless-chart란?
@meursyphus/headless-chart
는 UI가 없는 차트 라이브러리입니다. 데이터와 로직만 제공하고, 실제 UI는 개발자가 Flitter 위젯으로 직접 구현합니다. 이를 통해 완전히 커스터마이즈 가능한 차트를 만들 수 있습니다.
- GitHub: https://github.com/meursyphus/headless-chart
- 설치:
npm install @meursyphus/headless-chart
기본 개념
headless-chart의 핵심은 데이터 구조와 커스텀 렌더링 함수입니다:
- 데이터 구조: labels와 datasets로 구성
- 커스텀 함수: 차트의 각 부분(축, 눈금, 막대, 선 등)을 Flitter 위젯으로 렌더링
간단한 Bar Chart 예제
아래는 headless-chart로 만든 커스텀 바 차트입니다. Highcharts 스타일을 Flitter로 재현했습니다:
주요 커스터마이징 포인트
-
레이아웃 커스터마이징
custom.layout
함수로 전체 차트 구조 정의- Stack과 Positioned로 자유로운 요소 배치
-
축 라벨 스타일링
custom.xAxisLabel
,custom.yAxisLabel
로 텍스트 스타일 적용- 폰트, 크기, 색상을 원하는 대로 설정
-
막대 디자인
custom.bar
함수에서 Container와 BoxDecoration 활용- 색상, 모서리 둥글기, 그림자 등 자유롭게 디자인
-
범례 커스터마이징
- 범례 위치를 차트 내부에 떠있는 박스로 표현
- BoxShadow로 입체감 추가
Bar Chart 구현 코드 예시
const Bar = (...[{ legend, value }, { data }]) => {
const index = data.datasets.findIndex((d) => d.legend === legend);
const backgroundColor = backgroundColors[index];
return Container({
width: Infinity,
height: 14,
alignment: Alignment.centerRight,
decoration: new BoxDecoration({
color: backgroundColor,
borderRadius: BorderRadius.only({
topRight: Radius.circular(7),
bottomRight: Radius.circular(7),
}),
}),
child: FractionalTranslation({
translation: { x: 1, y: 0 },
child: Padding({
padding: EdgeInsets.only({ left: 6 }),
child: Text(value.toLocaleString("ko-KR").replace(",", " "), {
style: new TextStyle({
fontFamily: "Noto Sans JP",
fontSize: 12,
fontWeight: "bold",
color: "#111827",
}),
}),
}),
}),
});
};
위 코드에서는:
- Flitter의 Container와 BoxDecoration으로 막대를 그립니다
- FractionalTranslation으로 값 텍스트를 막대 끝에 배치합니다
- 각 데이터셋별로 다른 색상을 적용합니다
Line Chart 예제
다음은 Nivo 스타일의 라인 차트입니다:
Line Chart 커스터마이징
-
CustomPaint로 선 그리기
custom.line: (...[{ values, index }, { scale }]) => { return CustomPaint({ painter: { svg: { paint({ line, point }, { height, width }) { const linePath = new Path(); const pointPath = new Path(); // 데이터 포인트 계산 const points = values.map((value, index) => { const y = height - (height * (value - scale.min)) / (scale.max - scale.min); const x = (index * width) / (values.length - 1); return { x, y }; }); // 선 그리기 linePath.moveTo(points[0]); points.slice(1).forEach((point) => { linePath.lineTo(point); }); // 점 그리기 points.forEach((point) => { pointPath.addOval( Rect.fromCircle({ center: new Offset(point), radius: 5 }) ); }); // SVG 속성 설정 line.setAttribute("fill", "none"); line.setAttribute("stroke", colors[index]); line.setAttribute("stroke-width", "2"); line.setAttribute("d", linePath.getD()); point.setAttribute("fill", "#1f2937"); point.setAttribute("stroke", colors[index]); point.setAttribute("stroke-width", "1"); point.setAttribute("d", pointPath.getD()); } } } }); }
-
색상 팔레트 활용
- 데이터셋별로 다른 색상 적용
- 선과 점의 색상 조화
-
레이아웃 유연성
- Row로 차트와 범례 나란히 배치
- Flexible로 반응형 레이아웃 구현
headless-chart의 장점
- 완전한 커스터마이징: 모든 시각적 요소를 Flitter 위젯으로 제어
- 일관된 개발 경험: Flitter의 위젯 시스템 그대로 사용
- 성능 최적화: 필요한 부분만 렌더링
- 재사용성: 커스텀 컴포넌트를 쉽게 재사용
headless-chart 사용법
1. 설치
npm install @meursyphus/headless-chart
2. 기본 사용법
import { BarChart, LineChart } from "@meursyphus/headless-chart";
// 데이터 정의
const data = {
labels: ["A", "B", "C", "D"],
datasets: [
{
legend: "Dataset 1",
values: [10, 20, 30, 40],
},
],
};
// 차트 생성
const chart = BarChart({
data,
custom: {
// 각 요소를 Flitter 위젯으로 커스터마이징
bar: (...args) => Container({ /* ... */ }),
xAxisLabel: (...args) => Text(/* ... */),
// ... 기타 커스터마이징
},
});
3. 지원하는 차트 타입
- BarChart: 막대 차트 (수직/수평)
- LineChart: 라인 차트
- PieChart: 파이 차트
- ScatterChart: 산점도 차트
다음 단계
- 더 복잡한 차트 타입 구현하기 (Pie, Scatter, Area 등)
- 애니메이션 추가하기
- 인터랙티브 기능 구현하기 (툴팁, 줌, 팬 등)
- 실시간 데이터 업데이트