Chart.js 사용법: 웹에서 차트 그리기
웹 개발을 하다 보면 데이터를 차트로 보여줘야 할 때가 있죠? 대시보드에 매출 추이를 꺾은선 그래프로 표시하거나, 설문 결과를 막대 차트로 보여주거나, 카테고리별 비율을 도넛 차트로 나타내야 하는 상황 말이에요.
이런 경우에 가장 많이 쓰이는 라이브러리가 바로 Chart.js인데요. GitHub 스타가 6만 개가 넘고, npm 주간 다운로드가 240만 건에 달할 정도로 자바스크립트 차트 라이브러리 중에서는 압도적인 인기를 자랑합니다. D3.js처럼 자유도가 높은 라이브러리에 비하면 차트 종류가 제한적이긴 하지만, 그만큼 배우기 쉽고 몇 줄의 코드만으로 꽤 그럴듯한 차트를 만들 수 있다는 게 큰 장점입니다.
이번 포스팅에서는 Chart.js를 처음 접하는 분들을 위해 설치부터 다양한 차트 그리기, 세부 설정, 데이터 동적 업데이트까지 하나씩 다뤄보겠습니다.
설치
Chart.js를 사용하는 방법은 크게 두 가지입니다.
패키지 매니저로 설치하려면 다음 명령어를 실행하면 됩니다.
bun add chart.js
npm install chart.js
CDN을 통해 바로 불러오는 것도 가능합니다.
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
CDN 방식은 별도의 빌드 도구 없이 HTML 파일 하나로 바로 실습해 볼 수 있어서 처음 배울 때 편리합니다. 반면 프로덕션 프로젝트에서는 패키지 매니저로 설치한 뒤 번들러를 통해 트리 쉐이킹(tree-shaking)하는 게 번들 크기를 줄이는 데 유리합니다.
첫 번째 차트 그리기
Chart.js는 HTML5의 <canvas> 요소 위에 차트를 그립니다.
SVG 기반의 그래픽과 달리 캔버스(canvas) 기반이라 큰 데이터셋을 다룰 때도 렌더링 성능이 뛰어나죠.
가장 기본적인 막대 차트를 한번 그려볼까요?
<canvas id="myChart"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
const ctx = document.getElementById("myChart");
new Chart(ctx, {
type: "bar",
data: {
labels: ["HTML", "CSS", "JavaScript", "Python", "Java", "Go"],
datasets: [
{
label: "개발자 선호도 (%)",
data: [95, 90, 85, 70, 55, 30],
borderWidth: 1,
},
],
},
options: {
scales: {
y: {
beginAtZero: true,
},
},
},
});
</script>
Chart 생성자에 전달하는 객체의 구조를 살펴보면 크게 세 부분으로 나뉩니다.
우선 type은 차트의 종류를 지정합니다. 'bar', 'line', 'pie', 'doughnut', 'radar', 'polarArea', 'scatter', 'bubble'까지 총 8가지를 지원합니다.
data는 차트에 표시할 데이터를 담습니다.
labels에 X축 라벨을, datasets 배열에 실제 데이터 값과 스타일을 넣어주면 되고요. 하나의 차트에 여러 개의 데이터셋을 넣으면 비교 차트를 만들 수도 있습니다.
options는 차트의 동작과 외형을 세부적으로 조정합니다. 축 설정, 범례, 툴팁, 애니메이션 등 다양한 옵션이 있는데 이건 뒤에서 더 자세히 다뤄보겠습니다.
번들러에서 사용하기
CDN으로 Chart.js를 불러오면 모든 차트 타입과 플러그인이 한꺼번에 로드됩니다. 번들 크기가 커질 수 있으니 Vite나 Webpack 같은 번들러를 사용하는 프로젝트에서는 필요한 컴포넌트만 골라서 등록하는 게 좋습니다.
빠르게 프로토타이핑할 때는 chart.js/auto에서 모든 걸 한 번에 가져올 수 있습니다.
import Chart from "chart.js/auto";
프로덕션에서는 필요한 것만 개별 임포트해서 등록하는 걸 권장합니다.
import {
Chart,
BarController,
BarElement,
CategoryScale,
LinearScale,
Tooltip,
Legend,
} from "chart.js";
Chart.register(
BarController,
BarElement,
CategoryScale,
LinearScale,
Tooltip,
Legend,
);
이렇게 하면 사용하지 않는 차트 타입이나 플러그인이 번들에서 빠지기 때문에 최종 파일 크기를 상당히 줄일 수 있습니다.
혹시 필요한 컴포넌트를 빼먹으면 콘솔에 "bar" is not a registered controller 같은 친절한 에러 메시지가 나오니까 걱정하지 않으셔도 됩니다.
꺾은선 차트
시간에 따른 변화나 추세를 보여줄 때는 꺾은선(line) 차트가 제격입니다.
new Chart(ctx, {
type: "line",
data: {
labels: ["1월", "2월", "3월", "4월", "5월", "6월"],
datasets: [
{
label: "방문자 수",
data: [1200, 1900, 1500, 2500, 2200, 3000],
borderColor: "rgb(75, 192, 192)",
tension: 0.3,
fill: true,
backgroundColor: "rgba(75, 192, 192, 0.1)",
},
],
},
});
tension 속성으로 선의 곡률을 조절할 수 있습니다.
0이면 직선, 0.4 정도면 부드러운 곡선이 되죠.
fill: true를 주면 선 아래 영역이 채워져서 영역 차트(area chart)로 변합니다.
여러 데이터셋을 함께 넣으면 비교 분석에 유용합니다.
new Chart(ctx, {
type: "line",
data: {
labels: ["1월", "2월", "3월", "4월", "5월", "6월"],
datasets: [
{
label: "웹사이트 A",
data: [1200, 1900, 1500, 2500, 2200, 3000],
borderColor: "rgb(75, 192, 192)",
},
{
label: "웹사이트 B",
data: [800, 1100, 1700, 1400, 2100, 2800],
borderColor: "rgb(255, 99, 132)",
},
],
},
});
원형 차트와 도넛 차트
전체에서 각 항목이 차지하는 비율을 보여주려면 원형(pie) 차트나 도넛(doughnut) 차트를 사용합니다.
new Chart(ctx, {
type: "doughnut",
data: {
labels: ["모바일", "데스크톱", "태블릿"],
datasets: [
{
data: [60, 30, 10],
backgroundColor: ["#FF6384", "#36A2EB", "#FFCE56"],
},
],
},
options: {
plugins: {
legend: {
position: "bottom",
},
},
},
});
도넛 차트는 가운데가 뚫려 있어서 원형 차트보다 시각적으로 깔끔합니다.
type을 'pie'로 바꾸기만 하면 가운데가 채워진 원형 차트가 되고요.
도넛 구멍의 크기는 options.cutout으로 조절할 수 있습니다.
기본값은 '50%'이고, '70%'로 하면 구멍이 더 커지고 '0%'로 하면 원형 차트와 동일해집니다.
레이더 차트
여러 항목의 능력치나 점수를 한눈에 비교할 때는 레이더(radar) 차트가 제격입니다. 게임 캐릭터의 스탯이나 제품 비교표를 떠올리시면 됩니다.
new Chart(ctx, {
type: "radar",
data: {
labels: ["속도", "내구성", "편의성", "디자인", "가격"],
datasets: [
{
label: "제품 A",
data: [80, 90, 70, 85, 60],
borderColor: "rgb(255, 99, 132)",
backgroundColor: "rgba(255, 99, 132, 0.2)",
},
{
label: "제품 B",
data: [65, 75, 90, 70, 95],
borderColor: "rgb(54, 162, 235)",
backgroundColor: "rgba(54, 162, 235, 0.2)",
},
],
},
options: {
scales: {
r: {
suggestedMin: 0,
suggestedMax: 100,
},
},
},
});
레이더 차트의 축 설정은 scales.r로 합니다.
suggestedMin과 suggestedMax를 지정해 놓으면 데이터 범위에 상관없이 일정한 스케일을 유지할 수 있죠.
차트 꾸미기
기본 차트만으로도 충분히 쓸 만하지만 실제 서비스에 넣으려면 세부적인 스타일 조정이 필요할 겁니다.
색상과 테두리
데이터셋에 backgroundColor와 borderColor를 지정하면 차트 요소의 색상을 바꿀 수 있습니다.
막대 차트에서 borderRadius를 주면 막대 모서리가 둥글어지고, barPercentage로 막대의 너비 비율도 조절할 수 있습니다.
datasets: [
{
label: "매출",
data: [30, 50, 40, 60, 45, 70],
backgroundColor: "rgba(54, 162, 235, 0.5)",
borderColor: "rgba(54, 162, 235, 1)",
borderWidth: 2,
borderRadius: 8,
},
];
범례와 제목
plugins 옵션 안에서 범례(legend)와 제목(title)을 설정합니다.
options: {
plugins: {
legend: {
position: "bottom",
labels: {
usePointStyle: true,
padding: 20,
},
},
title: {
display: true,
text: "월별 매출 현황",
font: { size: 16 },
},
},
}
툴팁
마우스를 차트 위에 올렸을 때 나타나는 툴팁도 커스터마이징할 수 있습니다.
callbacks.label을 사용하면 표시 텍스트를 자유롭게 바꿀 수 있거든요.
options: {
plugins: {
tooltip: {
callbacks: {
label: (context) => {
return `${context.dataset.label}: ${context.parsed.y.toLocaleString()}원`;
},
},
},
},
}
축 설정
X축과 Y축의 라벨, 눈금, 격자선 등을 세부적으로 조정할 수 있습니다.
options: {
scales: {
x: {
title: {
display: true,
text: "월",
},
grid: {
display: false,
},
},
y: {
beginAtZero: true,
title: {
display: true,
text: "금액 (만원)",
},
ticks: {
callback: (value) => value.toLocaleString(),
},
},
},
}
beginAtZero: true는 Y축을 항상 0부터 시작하게 만듭니다.
이걸 빼면 데이터 범위에 맞춰서 축이 자동으로 조정되기 때문에 간혹 차트가 과장되어 보일 수 있으니 주의하셔야 합니다.
반응형 차트
Chart.js는 기본적으로 반응형을 지원합니다. responsive 옵션의 기본값이 true라서 브라우저 창 크기가 바뀌면 차트도 자동으로 리사이즈되죠.
다만 한 가지 주의할 점이 있습니다. 캔버스 요소를 감싸는 컨테이너의 크기 지정을 제대로 해줘야 합니다.
<div style="position: relative; width: 100%; max-width: 600px;">
<canvas id="myChart"></canvas>
</div>
maintainAspectRatio: true(기본값)로 설정하면 차트의 가로세로 비율이 유지되고, aspectRatio로 그 비율을 지정할 수 있습니다.
만약 컨테이너 높이에 맞추고 싶다면 maintainAspectRatio: false로 설정하고 컨테이너에 높이를 직접 지정하면 됩니다.
데이터 동적 업데이트
한번 그린 차트를 나중에 업데이트하고 싶을 때가 있죠? 실시간 대시보드나 사용자 입력에 따라 차트가 바뀌어야 하는 경우 말이에요.
Chart 인스턴스의 data 속성을 직접 수정한 뒤 update() 메서드를 호출하면 됩니다.
const chart = new Chart(ctx, config);
// 데이터 추가
chart.data.labels.push("7월");
chart.data.datasets[0].data.push(3500);
chart.update();
// 데이터 제거
chart.data.labels.pop();
chart.data.datasets[0].data.pop();
chart.update();
// 애니메이션 없이 즉시 업데이트
chart.update("none");
update()를 호출하면 기본적으로 부드러운 애니메이션이 적용됩니다.
데이터가 빈번하게 바뀌는 경우에는 'none'을 인자로 넘겨서 애니메이션을 끄는 게 성능에 좋고요.
더 이상 차트가 필요 없을 때는 destroy() 메서드로 인스턴스를 정리하는 것도 잊지 마세요.
chart.destroy();
이걸 빠뜨리면 같은 캔버스에 새 차트를 그릴 때 이전 차트와 겹치는 문제가 생길 수 있습니다.
React에서 사용하기
React 프로젝트에서는 react-chartjs-2 라이브러리를 사용하면 Chart.js를 React 컴포넌트로 편리하게 쓸 수 있습니다.
먼저 필요한 패키지를 설치합니다.
bun add chart.js react-chartjs-2
npm install chart.js react-chartjs-2
그다음 필요한 Chart.js 컴포넌트를 등록하고 차트 컴포넌트를 사용하면 됩니다.
import { Bar } from "react-chartjs-2";
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend,
} from "chart.js";
ChartJS.register(
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend,
);
const data = {
labels: ["1월", "2월", "3월", "4월", "5월", "6월"],
datasets: [
{
label: "매출 (만원)",
data: [120, 190, 150, 250, 220, 300],
backgroundColor: "rgba(54, 162, 235, 0.5)",
},
],
};
const options = {
responsive: true,
plugins: {
legend: { position: "top" },
title: { display: true, text: "월별 매출" },
},
};
export default function SalesChart() {
return <Bar data={data} options={options} />;
}
react-chartjs-2는 <Bar>, <Line>, <Pie>, <Doughnut>, <Radar> 같은 차트 타입별 컴포넌트를 제공합니다.
각 컴포넌트에 data와 options props를 넘기기만 하면 되고, React의 state가 바뀌면 차트도 자동으로 다시 그려지기 때문에 update()를 직접 호출할 필요가 없습니다.
마치며
지금까지 Chart.js의 기본적인 사용법을 알아봤는데요. 막대, 꺾은선, 원형, 도넛, 레이더까지 다양한 차트 타입을 살펴보았고, 세부 설정 조정부터 동적 데이터 업데이트, React 연동까지 다뤄봤습니다.
Chart.js의 가장 큰 매력은 역시 진입 장벽이 낮다는 점이죠. 복잡한 설정 없이 몇 줄의 코드만으로 깔끔한 차트를 만들 수 있으면서도 필요하면 플러그인 시스템을 통해 기능을 확장할 수도 있습니다.
좀 더 깊이 공부하고 싶으신 분들은 Chart.js 공식 문서를 참고해 보시길 추천합니다. 특히 커스텀 플러그인 문서를 보시면 차트 배경 색상 변경이나 워터마크 삽입 같은 고급 기능도 직접 구현할 수 있습니다.
This work is licensed under
CC BY 4.0