VanillaFront 튜토리얼 — 추가편 · 차트
지난 여덟 편으로 프레임워크의 기본 개념은 모두 다뤘습니다. 이번은 추가편 — 실무에서 가장 자주 요구받는 컴포넌트 중 하나인 차트를 정리합니다.
VanillaFront는 20종 이상의 차트를 기본 제공합니다. 별도 라이브러리 없이 Va.Chart 계열 컴포넌트 하나로 처리되며, 모든 차트가 동일한 데이터 규약을 따라서 한 번 익히면 나머지 차트로 확장이 수월합니다.
1. 시작 — 별도 라이브러리 import
차트는 크기가 커서 코어(va.js)와 분리되어 있습니다. 사용 전 반드시 로드하세요.
import Va from '../../lib/va.js';
import '../../lib/va_chart.js'; // ← 이 한 줄이 필수
이 import가 실행되면 프레임워크에 20종의 차트 컴포넌트가 등록됩니다. 이후 barChart / pieChart / verticalChart 등을 tagName으로 자유롭게 쓸 수 있어요.
2. 차트의 3대 구성
거의 모든 축형 차트(막대·라인·영역·콤보 등)는 세 가지 개념의 조합입니다.
구성역할
| 컨테이너 | 좌표계·축·범례를 담는 상자. verticalChart / horizontalChart |
| axes | X, Y 축 정의 배열 |
| charts | 실제 시각화 시리즈 배열 (bar, line 등) |
이걸 그림으로 그리면:
verticalChart (컨테이너)
├── axes: [
│ { position: 'left', labelTitle: '값', key: 'value' }, ← Y축
│ { position: 'bottom', labelTitle: '월', key: 'date' } ← X축
│ ]
└── charts: [
{ type: 'bar', key: 'value', text: 'value' } ← 시리즈
]
3. 가장 간단한 예 — 세로 막대 차트
시작은 언제나 세로 막대. 이 하나가 익숙해지면 나머지는 확장입니다.
import Va from '../../lib/va.js';
import '../../lib/va_chart.js';
class App extends Va.View {
mounted() {
this.getRef('chart1').setData([
{ date: '1월', value: 120 },
{ date: '2월', value: 200 },
{ date: '3월', value: 150 },
{ date: '4월', value: 320 },
{ date: '5월', value: 280 }
]);
}
config() {
return {
tagName: 'main',
tags: [{
tagName: 'verticalChart',
ref: 'chart1',
axes: [
{ position: 'left', labelTitle: '판정값', key: 'value' },
{ position: 'bottom', labelTitle: '월', key: 'date' }
],
charts: [
{ type: 'bar', key: 'value', text: 'value' }
],
style: { height: '400px' }
}]
};
}
}
흐름:
- verticalChart 컨테이너 배치
- 축 두 개 정의 (좌측 = 값, 하단 = 카테고리)
- bar 시리즈 하나 등록 (key: 'value'가 데이터의 어느 필드를 그릴지)
- mounted()에서 setData()로 데이터 주입
4. 데이터 규약 — 배열 하나면 끝
모든 차트가 동일한 규약을 따릅니다. 배열이고, 각 원소는 필드가 이름표(카테고리)와 값을 가진 객체.
[
{ date: '1월', value: 120, cost: 80 },
{ date: '2월', value: 200, cost: 150 },
// ...
]
- axes의 key가 카테고리 필드를 지목
- charts의 key가 값 필드를 지목
- 필드명은 자유. date 대신 month, value 대신 sales 등 원하는 대로
한 데이터로 여러 시리즈를 그릴 수 있다는 게 이 규약의 힘입니다.
5. 여러 시리즈 겹치기 — 콤보 차트
같은 데이터로 막대 + 라인을 겹쳐 그려봅시다.
{
tagName: 'comboChart',
ref: 'refChart',
style: { height: '400px', width: '100%' },
title: { text: '월별 매출 현황', fontSize: 16 },
axes: [
{ position: 'left', key: 'sales', labelTitle: '금액(만원)' },
{ position: 'bottom', key: 'month' }
],
charts: [
{ key: 'sales', fill: '#0f6cbd', label: '매출' },
{ key: 'cost', fill: '#da3b01', label: '원가', opacity: 0.7 },
{
key: 'target',
type: 'line', // ← 이 시리즈만 라인
stroke: '#00723b',
label: '목표',
lineWidth: 3,
dotRadius: 4,
smooth: true
}
],
legends: [{ position: 'bottom' }],
onBarClick: 'onBarClick',
onPointClick: 'onPointClick'
}
데이터는 이런 모양:
[
{ month: '1월', sales: 320, cost: 220, target: 300 },
{ month: '2월', sales: 280, cost: 200, target: 300 },
// ...
]
포인트:
- 시리즈에 type: 'line'을 안 넣으면 컨테이너 종류에 따라 기본 타입(bar)
- 각 시리즈에 fill / stroke / label / opacity 등 개별 시각 옵션
- bar는 onBarClick, line은 onPointClick
6. 원형 차트 — 축 없이 데이터 하나
파이·도넛은 축 개념 없이 카테고리 + 값 조합만 있습니다.
{
tagName: 'pieChart',
ref: 'chart1',
style: { height: '300px', width: '100%' },
title: { text: '브라우저 점유율', fontSize: 14 },
valueKey: 'value', // ← 값 필드
categoryKey: 'name', // ← 카테고리 필드
showLabel: true,
labelPosition: 'outside', // 'inside' / 'outside'
labelType: 'name+percent', // 'name' / 'percent' / 'value' / 조합
legends: [{ position: 'bottom' }],
onSliceClick: 'onSliceClick'
}
데이터:
[
{ name: 'Chrome', value: 65 },
{ name: 'Safari', value: 18 },
{ name: 'Firefox', value: 10 },
{ name: 'Edge', value: 5 },
{ name: 'Others', value: 2 }
]
도넛으로 만들려면 innerRadius만 지정하면 됩니다.
{
tagName: 'pieChart',
innerRadius: 60, // 픽셀. 0보다 크면 도넛
// ...
}
7. 이벤트 처리
차트마다 클릭·호버 이벤트를 받을 수 있습니다.
축형 차트 (bar / line 등)
onBarClick(chart, data, dataIndex, chartIndex, barElement, evt) {
console.log('클릭된 데이터:', data);
console.log('인덱스:', dataIndex);
}
onPointClick(chart, data, dataIndex, chartIndex, dotElement, evt) {
console.log('클릭된 데이터 포인트:', data);
}
원형 차트 (pie / donut)
onSliceClick(chart, data, dataIndex, sliceElement, evt) {
console.log('클릭된 조각:', data);
}
이벤트 파라미터 순서는 컴포넌트 → 데이터 → 인덱스 → DOM 요소 → 원본 이벤트 순으로 통일되어 있어 익히기 쉽습니다.
8. 20종+ 차트 카탈로그
한눈에 보는 전체 목록. 각각의 tagName으로 바로 사용 가능합니다.
세로 축 차트 (verticalChart 컨테이너 안에 배치)
tagName이름
| barChart | 세로 막대 (stacked 지원) |
| lineChart | 라인 |
| areaChart | 영역 (라인 아래 채움) |
| comboChart | 콤보 (막대 + 라인 혼합) |
| scatterChart | 산점도 |
| bubbleChart | 버블 (산점도 + 크기) |
가로 축 차트 (horizontalChart 컨테이너)
tagName이름
| hBarChart | 가로 막대 |
| divergingBarChart | 발산형 막대 (좌우) |
| hLineChart | 가로 라인 |
| hAreaChart | 가로 영역 |
| hComboChart | 가로 콤보 |
원형·방사형 차트
tagName이름
| pieChart | 파이 / 도넛 |
| semiPieChart | 반원 파이 |
| gaugeChart | 게이지 |
| radialBarChart | 방사형 막대 |
| nightingaleChart | 나이팅게일 (극좌표 막대) |
| radarChart | 레이더 (거미줄) |
특수 차트
tagName이름용도
| stockChart | 캔들스틱 | 주식·가격 |
| treeChart | 트리 | 계층 구조 |
| treeMapChart | 트리맵 | 사각형 분할 계층 |
| boxPlotChart | 박스플롯 | 통계 분포 |
9. 실전 예 — 서비스에서 받은 데이터로 그리기
이전에 배운 서비스(Service) 패턴과 조합하면 실제 업무 흐름이 됩니다.
service/ChartService.js
export default class ChartService extends Va.Service {
static async list(view, params) {
const options = { method: 'GET', headers: { 'Content-Type': 'application/json' } };
const response = await Va.HttpSync.request(view,
'./assets/json/samples/chart.json', params, options);
return {
result: response.result,
data: response.data,
message: response.error?.message || ''
};
}
}
view/dashboard/Sales.js
import Va from '../../lib/va.js';
import '../../lib/va_chart.js';
import ChartService from '../../service/ChartService.js';
class Sales extends Va.View {
async mounted() {
const res = await ChartService.list(this, {});
if (res.result) {
this.getRef('chart1').setData(res.data.list);
} else {
new Va.Alert({ title: '오류', message: res.message }).show(this);
}
}
onBarClick(chart, data, i, ci, el, evt) {
new Va.Alert({
title: '상세',
message: `${data.date} 판매액: ${data.value}만원`
}).show(this);
}
config() {
return {
tagName: 'page',
tags: [{
tagName: 'panel',
tags: [
{ tagName: 'h2', innerHTML: '월별 판매 현황' },
{
tagName: 'verticalChart',
ref: 'chart1',
style: { height: '400px' },
axes: [
{ position: 'left', labelTitle: '판매액', key: 'value' },
{ position: 'bottom', labelTitle: '월', key: 'date' }
],
charts: [
{ type: 'bar', key: 'value', fill: '#0f6cbd', label: '판매액' }
],
legends: [{ position: 'bottom' }],
onBarClick: 'onBarClick'
}
]
}]
};
}
}
흐름:
- mounted()에서 서비스로 데이터 조회
- 응답이 오면 setData()로 차트에 주입
- 사용자가 막대 클릭 → 상세 알림
이 패턴이 대시보드·리포트 화면의 표준 골격입니다.
10. 자주 쓰는 옵션 치트시트
프로젝트에서 반복적으로 마주치는 옵션만 정리했습니다.
컨테이너 공통
옵션설명
| title | { text, height, fontSize } 형태의 차트 제목 |
| legends | [{ position: 'bottom' }] 등 범례 위치 |
| area | { top, bottom, left, right } 여백 |
| gridColor | 격자선 색 |
| barGap | 막대 간격 (0~1) |
| referenceLines | 기준선 ({ axis, value, color, dash, label }) |
시리즈 옵션 (charts 배열 각 원소)
옵션설명
| type | bar / line / area |
| key | 데이터의 값 필드명 |
| label | 범례에 표시될 이름 |
| fill | 채우기 색 |
| stroke | 선 색 (라인·영역용) |
| opacity | 투명도 (0~1) |
| text / textPosition / textFontSize | 데이터 라벨 표시 |
| smooth | 라인을 부드럽게 (곡선) |
| lineWidth / dotRadius | 라인 두께 / 점 크기 |
| dashArray | 점선 패턴 ('6,3' 등) |
| areaFill / areaOpacity | 라인 아래 영역 채움 |
파이 전용
옵션설명
| valueKey / categoryKey | 필드 매핑 |
| innerRadius | 도넛 안쪽 반지름 (0 = 파이) |
| showLabel / labelPosition / labelType | 라벨 표시 |
11. 알아두면 좋을 팁
1) 반드시 import '../../lib/va_chart.js'
차트가 안 뜬다면 대부분 이 import 누락입니다. 콘솔에 컴포넌트 미등록 관련 경고가 뜹니다.
2) 차트에 명시적 height 필수
verticalChart의 컨테이너 style에 height를 안 주면 0으로 렌더되어 안 보입니다. 데이터가 있는데 화면이 비어 있다면 이걸 먼저 의심하세요.
3) setData() 시점은 mounted() 이후
init()에서 호출하면 아직 컴포넌트가 없어 조회 실패. mounted() 안에서 호출.
4) 다크 테마 대응
색을 하드코딩하지 말고 테마 변수 활용:
fill: 'var(--colorBrandStroke1)'
5) 대량 데이터
차트당 데이터가 수만 건 넘어가면 브라우저가 느려질 수 있습니다. 서버에서 미리 집계해 내려주거나, 스크롤 뷰포트 안에서만 그리는 스크롤 차트를 검토하세요.
6) 차트 재조회
이미 그려진 차트를 새 데이터로 갈아치우려면 그냥 setData()를 다시 호출:
onSearch() {
ChartService.list(this, params, (view, ok, res) => {
if (ok) view.getRef('chart1').setData(res.data.list);
});
}
내부에서 알아서 재렌더링합니다.
요약 — 차트 5대 규칙
- import '../../lib/va_chart.js' 필수 — 코어와 분리된 별도 파일
- 컨테이너(verticalChart/horizontalChart) + axes + charts 3단 구조
- 모든 차트가 동일한 데이터 규약 — 배열 하나로 여러 시리즈 그리기 가능
- 이벤트 파라미터 순서 통일 — (chart, data, index, ..., element, evt)
- 파이·게이지 같은 원형 차트는 축 없이 valueKey/categoryKey 로 필드 매핑
'기본사용법' 카테고리의 다른 글
| VanillaFront 튜토리얼 — 8. 테마 (0) | 2026.09.11 |
|---|---|
| VanillaFront 튜토리얼 — 7. 다국어 (0) | 2026.09.11 |
| VanillaFront 튜토리얼 — 6. 서비스 (0) | 2026.09.11 |
| VanillaFront 튜토리얼 — 5. MVP 패턴 (0) | 2026.09.11 |
| VanillaFront 튜토리얼 — 4. 라우터 (0) | 2026.09.11 |