4️⃣

04장 Chart.js를 사용한 데이터 시각화

4.1 소개

 
4장에서는 React와 Chart.js를 사용하여 다양한 유형의 차트를 만드는 방법을 설명한다. 각 차트 유형에 대한 예제와 함께 축 설정, 애니메이션, 컬러 설정, 데이터 처리 등의 공통 기능에 대해서 다루게된다. 이 책에서는 간단하게 사용하는 내용으로 작성되어 있기 때문에 좀 더 많은 예제를 보고 싶으면 Chart.js의 Samples(https://www.chartjs.org/docs/latest/samples/information.html)를 참고하면 좋다.
 
React에서 Chart.js를 함께 사용할 때는 보통 'react-chartjs-2' 라이브러리를 사용한다. 이 라이브러리는 Chart.js를 React 컴포넌트로 래핑한 것이다.
 
차트에 대한 설명을 들어가기 전 먼저 필요한 패키지를 설치한다.
# npm npm install react chart.js react-chartjs-2 # yarn yarn install react chart.js react-chartjs-2 # pnpm pnpm install react chart.js react-chartjs-2

4.2 차트 사용법

다음 장에서 소개할 <AreaChart /> 컴포넌트 코드를 사용하여 차트를 사용하는 방법에 대해 알아보겠다.
  1. 라이브러리 임포트
    1. import { Line } from 'react-chartjs-2'; import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Filler, Legend, } from 'chart.js';
      • react-chartjs-2에서 <Line /> 컴포넌트를 가져온다. 이는 리액트에서 선 그래프를 그리는 데 사용한다.
      • chart.js에서 여러 모듈을 가져온다. 이는 차트의 다양한 기능과 스타일을 정의하는 데 필요하다.
       
  1. 차트 등록
    1. ChartJS.register( CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Filler, Legend );
      • 사용할 차트 기능들을 등록한다. 이는 차트가 제대로 작동하고 그래프의 요소들을 나타내기 위해 필요하다.
       
  1. AreaChart 컴포넌트 정의
    1. const AreaChart = () => { ... };
      • 차트를 표시할 리액트 컴포넌트를 정의한다.
       
  1. 데이터 설정
    1. const data = { ... };
      • 차트에 표시될 데이터를 정의한다. 여기에는 x축 라벨(월)과 y축 데이터(숫자 값)와 같은 데이터가 포함된다.
       
  1. 옵션 설정
    1. const options = { ... };
      • 차트의 반응형 여부, 범례 위치, 제목 등의 옵션을 설정한다.
       
  1. 차트 렌더링
    1. return <Line options={options} data={data} />;
      • Line 컴포넌트처럼 Chart 컴포넌트를 사용하여 차트를 렌더링한다. 옵션과 데이터를 props로 전달합니다.
       
  1. 컴포넌트 내보내기
    1. export default AreaChart;
      • 정의한 AreaChart 컴포넌트를 내보내어 다른 파일에서 사용할 수 있다.
       

4.3 차트 유형

각 차트 유형에 대해 기본적인 React 컴포넌트를 만들어보자.

4.3.1 Area Chart

notion imagenotion image
Area Chart는 선 그래프의 변형으로, 선 아래 영역이 채워진 형태이다. 이 차트는 시간에 따른 데이터의 변화를 보여주는 데 효과적이다. 특히 누적 데이터나 전체에 대한 부분의 비율을 표현할 때 유용하다. 예를 들어, 시간에 따른 매출 변화나 시장 점유율의 변화를 보여줄 때 사용할 수 있다.
 
필수로 등록해야 하는 속성은 다음과 같다.
  • LineController
  • LineElement
  • PointElement
  • Default scales: CategoryScale (x), LinearScale (y)
 
아래는 위 차트 그래프의 예제 코드이다.
import { Line } from 'react-chartjs-2'; import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Filler, Legend, } from 'chart.js'; ChartJS.register( CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Filler, Legend ); const AreaChart = () => { const data = { labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'], datasets: [ { fill: true, label: 'Dataset 2', data: [28, 48, 40, 19, 86, 27, 90], borderColor: 'rgb(53, 162, 235)', backgroundColor: 'rgba(53, 162, 235, 0.5)', }, ], }; const options = { responsive: true, plugins: { legend: { position: 'top', }, title: { display: true, text: 'Area Chart', }, }, }; return <Line options={options} data={data} />; }; export default AreaChart;
 

4.3.2 Bar Chart

notion imagenotion image
Bar Chart는 범주형 데이터를 직사각형 막대로 표현하는 차트이다. 각 막대의 길이나 높이는 해당 범주의 값을 나타낸다. 이 차트는 여러 항목을 비교하거나 시간에 따른 변화를 보여줄 때 효과적이다. 예를 들어, 다양한 제품의 판매량 비교나 월별 수익 변화를 표현할 때 사용할 수 있다.
 
필수로 등록해야 하는 속성은 다음과 같다.
  • BarController
  • BarElement
  • Default scales: CategoryScale (x), LinearScale (y)
 
아래는 위 차트 그래프의 예제 코드이다.
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 BarChart = () => { const options = { responsive: true, plugins: { legend: { position: 'top', }, title: { display: true, text: 'Bar Chart', }, }, }; const labels = ['January', 'February', 'March', 'April', 'May', 'June', 'July']; const data = { labels, datasets: [ { label: 'Dataset 1', data: [65, 59, 80, 81, 56, 55, 40], backgroundColor: 'rgba(255, 99, 132, 0.5)', }, { label: 'Dataset 2', data: [28, 48, 40, 19, 86, 27, 90], backgroundColor: 'rgba(53, 162, 235, 0.5)', }, ], }; return <Bar options={options} data={data} />; }; export default BarChart;
 

4.3.3 Bubble Chart

notion imagenotion image
Bubble Chart는 세 가지 차원의 데이터를 표현할 수 있는 차트이다. x축과 y축으로 두 가지 차원을 나타내고, 버블의 크기로 세 번째 차원을 표현한다. 이 차트는 여러 변수 간의 관계를 보여주는 데 유용하다. 예를 들어, 국가별 인구(버블 크기), GDP(x축), 평균 수명(y축)을 한 번에 표현할 수 있다.
 
필수로 등록해야 하는 속성은 다음과 같다.
  • BubbleController
  • PointElement
  • Default scales: LinearScale (x/y)
 
아래는 위 차트 그래프의 예제 코드이다.
import { Bubble } from 'react-chartjs-2'; import { Chart as ChartJS, LinearScale, PointElement, Tooltip, Legend, } from 'chart.js'; ChartJS.register(LinearScale, PointElement, Tooltip, Legend); const BubbleChart = () => { const options = { scales: { y: { beginAtZero: true, }, }, }; const data = { datasets: [ { label: 'Red dataset', data: Array.from({ length: 50 }, () => ({ x: Math.random() * 100, y: Math.random() * 100, r: Math.random() * 15 })), backgroundColor: 'rgba(255, 99, 132, 0.5)', }, { label: 'Blue dataset', data: Array.from({ length: 50 }, () => ({ x: Math.random() * 100, y: Math.random() * 100, r: Math.random() * 15 })), backgroundColor: 'rgba(53, 162, 235, 0.5)', }, ], }; return <Bubble options={options} data={data} />; }; export default BubbleChart;
 

4.3.4 Doughnut and Pie Charts

notion imagenotion image
notion imagenotion image
Doughnut과 Pie Charts는 전체에 대한 부분의 비율을 원형으로 표현하는 차트이다. Pie Chart는 완전한 원을 사용하고, Doughnut Chart는 중앙이 비어 있는 형태이다. 이 차트들은 구성 비율을 직관적으로 보여주는 데 효과적이다. 예를 들어, 예산 분배나 시장 점유율을 표현할 때 사용할 수 있다.
 
필수로 등록해야 하는 속성은 다음과 같다.
  • DoughnutController
  • ArcElement
  • Not using scales
 
아래는 위 차트 그래프의 예제 코드이다.
import { Doughnut, Pie } from 'react-chartjs-2'; import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js'; ChartJS.register(ArcElement, Tooltip, Legend); const DoughnutPieChart = () => { const data = { labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'], datasets: [ { label: '# of Votes', data: [12, 19, 3, 5, 2, 3], backgroundColor: [ 'rgba(255, 99, 132, 0.2)', 'rgba(54, 162, 235, 0.2)', 'rgba(255, 206, 86, 0.2)', 'rgba(75, 192, 192, 0.2)', 'rgba(153, 102, 255, 0.2)', 'rgba(255, 159, 64, 0.2)', ], borderColor: [ 'rgba(255, 99, 132, 1)', 'rgba(54, 162, 235, 1)', 'rgba(255, 206, 86, 1)', 'rgba(75, 192, 192, 1)', 'rgba(153, 102, 255, 1)', 'rgba(255, 159, 64, 1)', ], borderWidth: 1, }, ], }; return ( <div> <h2>Doughnut Chart</h2> <Doughnut data={data} /> <h2>Pie Chart</h2> <Pie data={data} /> </div> ); }; export default DoughnutPieChart;
 

4.3.5 Line Chart

notion imagenotion image
Line Chart는 데이터 포인트를 직선으로 연결하여 연속적인 데이터의 변화를 보여주는 차트이다. 이 차트는 시간에 따른 트렌드나 여러 데이터 세트의 비교를 표현하는 데 효과적이다. 예를 들어, 주식 가격의 변동이나 월별 온도 변화를 나타낼 때 사용할 수 있다.
 
필수로 등록해야 하는 속성은 다음과 같다.
  • LineController
  • LineElement
  • PointElement
  • Default scales: CategoryScale (x), LinearScale (y)
 
아래는 위 차트 그래프의 예제 코드이다.
import { Line } from 'react-chartjs-2'; import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend, } from 'chart.js'; ChartJS.register( CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend ); const LineChart = () => { const options = { responsive: true, plugins: { legend: { position: 'top', }, title: { display: true, text: 'Line Chart', }, }, }; const labels = ['January', 'February', 'March', 'April', 'May', 'June', 'July']; const data = { labels, datasets: [ { label: 'Dataset 1', data: [65, 59, 80, 81, 56, 55, 40], borderColor: 'rgb(255, 99, 132)', backgroundColor: 'rgba(255, 99, 132, 0.5)', }, { label: 'Dataset 2', data: [28, 48, 40, 19, 86, 27, 90], borderColor: 'rgb(53, 162, 235)', backgroundColor: 'rgba(53, 162, 235, 0.5)', }, ], }; return <Line options={options} data={data} />; }; export default LineChart;
 

4.3.6 Mixed Chart Types

notion imagenotion image
Mixed Chart Types는 하나의 차트에 여러 가지 차트 유형을 결합한 것이다. 이는 복잡한 데이터 세트를 표현하거나 여러 종류의 데이터를 동시에 비교할 때 유용하다. 예를 들어, 막대그래프로 월별 매출을, 선 그래프로 같은 기간의 이익률 변화를 동시에 표현할 수 있다.
 
아래는 위 차트 그래프의 예제 코드이다.
import { Chart } from 'react-chartjs-2'; import { Chart as ChartJS, LinearScale, CategoryScale, BarElement, PointElement, LineElement, Legend, Tooltip, } from 'chart.js'; ChartJS.register( LinearScale, CategoryScale, BarElement, PointElement, LineElement, Legend, Tooltip ); const MixedChart = () => { const options = { responsive: true, plugins: { legend: { position: 'top', }, title: { display: true, text: 'Mixed Chart', }, }, scales: { y: { type: 'linear', display: true, position: 'left', }, y1: { type: 'linear', display: true, position: 'right', grid: { drawOnChartArea: false, }, }, }, }; const labels = ['January', 'February', 'March', 'April', 'May', 'June', 'July']; const data = { labels, datasets: [ { type: 'line', label: 'Dataset 1', borderColor: 'rgb(255, 99, 132)', borderWidth: 2, fill: false, data: [65, 59, 80, 81, 56, 55, 40], yAxisID: 'y', }, { type: 'bar', label: 'Dataset 2', backgroundColor: 'rgb(75, 192, 192)', data: [28, 48, 40, 19, 86, 27, 90], borderColor: 'white', borderWidth: 2, yAxisID: 'y1', }, ], }; return <Chart type='bar' options={options} data={data} />; }; export default MixedChart;
 

4.3.7 Polar Area Chart

notion imagenotion image
Polar Area Chart는 원형 차트의 한 종류로, 각 세그먼트의 각도는 동일하지만, 반지름이 다르다. 이 차트는 여러 범주의 데이터를 비교할 때 유용하며, 특히 순환적인 데이터를 표현하는 데 효과적이다. 예를 들어, 연중 각 월의 강수량이나 24시간 동안의 활동 패턴을 표현할 때 사용할 수 있다.
 
필수로 등록해야 하는 속성은 다음과 같다.
  • PolarAreaController
  • ArcElement
  • Default scale: RadialLinearScale (r)
 
아래는 위 차트 그래프의 예제 코드이다.
import { PolarArea } from 'react-chartjs-2'; import { Chart as ChartJS, RadialLinearScale, ArcElement, Tooltip, Legend, } from 'chart.js'; ChartJS.register(RadialLinearScale, ArcElement, Tooltip, Legend); const PolarAreaChart = () => { const data = { labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'], datasets: [ { label: '# of Votes', data: [12, 19, 3, 5, 2, 3], backgroundColor: [ 'rgba(255, 99, 132, 0.5)', 'rgba(54, 162, 235, 0.5)', 'rgba(255, 206, 86, 0.5)', 'rgba(75, 192, 192, 0.5)', 'rgba(153, 102, 255, 0.5)', 'rgba(255, 159, 64, 0.5)', ], borderWidth: 1, }, ], }; return <PolarArea data={data} />; }; export default PolarAreaChart;
 

4.3.8 Radar Chart

notion imagenotion image
Radar Chart는 여러 변수의 데이터를 동시에 표현할 수 있는 차트이다. 각 변수는 중심에서 뻗어 나오는 축으로 표현되며, 데이터 포인트들을 연결하여 다각형 형태를 만든다. 이 차트는 여러 특성을 가진 항목들을 비교할 때 유용하다. 예를 들어, 선수들의 다양한 능력치 비교나 제품의 여러 특성을 평가할 때 사용할 수 있다.
 
필수로 등록해야 하는 속성은 다음과 같다.
  • RadarController
  • LineElement
  • PointElement
  • Default scale: RadialLinearScale (r)
 
아래는 위 차트 그래프의 예제 코드이다.
import { Radar } from 'react-chartjs-2'; import { Chart as ChartJS, RadialLinearScale, PointElement, LineElement, Filler, Tooltip, Legend, } from 'chart.js'; ChartJS.register( RadialLinearScale, PointElement, LineElement, Filler, Tooltip, Legend ); const RadarChart = () => { const data = { labels: ['Thing 1', 'Thing 2', 'Thing 3', 'Thing 4', 'Thing 5', 'Thing 6'], datasets: [ { label: '# of Votes', data: [2, 9, 3, 5, 2, 3], backgroundColor: 'rgba(255, 99, 132, 0.2)', borderColor: 'rgba(255, 99, 132, 1)', borderWidth: 1, }, ], }; return <Radar data={data} />; }; export default RadarChart;
 

4.3.9 Scatter Chart

notion imagenotion image
Scatter Chart는 두 변수 간의 관계를 점으로 표현하는 차트이다. 각 데이터 포인트는 x축과 y축의 값에 따라 위치가 결정된다. 이 차트는 두 변수 사이의 상관관계나 분포를 파악하는 데 효과적이다. 예를 들어, 키와 몸무게의 관계, 광고 지출과 매출의 관계 등을 분석할 때 사용할 수 있다.
 
필수로 등록해야 하는 속성은 다음과 같다.
  • ScatterController
  • PointElement
  • Default scales: LinearScale (x/y)
 
아래는 위 차트 그래프의 예제 코드이다.
import { Scatter } from 'react-chartjs-2'; import { Chart as ChartJS, LinearScale, PointElement, LineElement, Tooltip, Legend, } from 'chart.js'; ChartJS.register(LinearScale, PointElement, LineElement, Tooltip, Legend); const ScatterChart = () => { const options = { scales: { y: { beginAtZero: true, }, }, }; const data = { datasets: [ { label: 'A dataset', data: Array.from({ length: 100 }, () => ({ x: Math.random() * 100, y: Math.random() * 100, })), backgroundColor: 'rgba(255, 99, 132, 1)', }, ], }; return <Scatter options={options} data={data} />; }; export default ScatterChart;
 

4.4 공통 기능

4.4.1 x, y축 계산식

x, y축의 계산식은 scales 옵션을 통해 설정할 수 있다.
const options = { scales: { x: { type: 'linear', position: 'bottom', ticks: { callback: function(value, index, values) { return 'x=' + value; } } }, y: { ticks: { callback: function(value, index, values) { return '€' + value; } } } } };
 

4.4.2 애니메이션 효과

애니메이션 효과는 animation 옵션을 통해 설정할 수 있다.
const options = { animation: { duration: 2000, easing: 'easeInOutQuart' } };
 

4.4.3 컬러 설정

컬러는 각 데이터셋의 backgroundColorborderColor 속성을 통해 설정할 수 있다.
const data = { datasets: [{ backgroundColor: 'rgba(255, 99, 132, 0.5)', borderColor: 'rgb(255, 99, 132)', }] };
 

4.4.4 데이터 처리 방법 (JSON, CSV)

JSON 데이터는 직접 사용할 수 있다. CSV 데이터는 파싱이 필요하다.
// JSON 데이터 const jsonData = [ { x: 1, y: 10 }, { x: 2, y: 15 }, { x: 3, y: 20 } ]; // CSV 데이터 파싱 const parseCSV = (csv) => { const lines = csv.split('\\n'); const headers = lines[0].split(','); return lines.slice(1).map(line => { const values = line.split(','); return headers.reduce((obj, header, index) => { obj[header] = parseFloat(values[index]); return obj; }, {}); }); }; const csvData = `x,y 1,10 2,15 3,20`; const parsedCSVData = parseCSV(csvData);
 

4.4.5 유용한 메소드들

Chart.js에는 여러 유용한 메소드가 있다. 예를 들어:
  • update(): 차트를 업데이트한다.
  • destroy(): 차트를 제거한다.
  • resize(): 차트 크기를 조정한다.
  • clear(): 차트를 지운다.
  • stop(): 차트의 애니메이션을 중지한다.
  • reset(): 차트를 초기 상태로 재설정한다.
이러한 메소드들은 Chart.js 인스턴스에서 직접 호출할 수 있다. React 컴포넌트에서는 useRef를 사용하여 차트 인스턴스에 접근할 수 있다.

4.5 실전예제

4.5.1 MBTI

notion imagenotion image
MBTI 수치 결과를 시각화하는 예제를 만들어 보자. 이 예제에서는 MBTI를 위한 Bar Chart를 사용한다.
 
이 예제는 다음과 같은 특징을 가지고 있다.
  • Bar Chart를 사용하여 MBTI의 8가지 지표(E, I, S, N, T, F, J, P)에 대한 점수를 표시한다.
  • 각 지표마다 다른 색상을 사용하여 구분을 용이하게 했다.
  • y축은 0부터 100까지의 점수를 나타낸다.
 
아래는 위 이미지를 구현한 코드이다.
// 필요한 Chart.js 컴포넌트와 모듈을 가져온다. import { Bar } from "react-chartjs-2"; import { Chart as ChartJS, CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend, } from "chart.js"; // Chart.js에 사용할 컴포넌트들을 등록한다. ChartJS.register( CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend ); const MBTI = () => { // MBTI 데이터를 정의한다. 각 항목은 좌우 특성과 점수를 가진다. const mbtiData = [ { left: "E", right: "I", leftScore: 65, rightScore: 35 }, { left: "S", right: "N", leftScore: 55, rightScore: 45 }, { left: "T", right: "F", leftScore: 70, rightScore: 30 }, { left: "J", right: "P", leftScore: 40, rightScore: 60 }, ]; // Chart.js에 전달할 데이터 객체를 생성한다. const data = { labels: mbtiData.map(() => ""), // 빈 레이블을 사용한다. datasets: [ { label: "첫 번째 요소", data: mbtiData.map((item) => item.leftScore), backgroundColor: "rgba(75, 192, 192, 0.8)", borderColor: "rgba(75, 192, 192, 1)", borderWidth: 1, }, { label: "두 번째 요소", data: mbtiData.map((item) => item.rightScore), backgroundColor: "rgba(255, 99, 132, 0.8)", borderColor: "rgba(255, 99, 132, 1)", borderWidth: 1, }, ], }; // Chart.js 옵션을 설정한다. const options = { indexAxis: "y", // 가로 방향 차트로 설정 responsive: true, scales: { x: { stacked: true, max: 100, }, y: { stacked: true, }, }, plugins: { legend: { display: false, // 범례를 숨김 }, title: { display: true, text: "MBTI 유형 분석", }, tooltip: { callbacks: { // 툴팁 커스터마이징 label: function (context) { const dataIndex = context.dataIndex; const datasetIndex = context.datasetIndex; const score = context.parsed.x; const item = mbtiData[dataIndex]; const label = datasetIndex === 0 ? item.left : item.right; return `${label}: ${score}%`; }, }, }, }, }; return ( <div style={{ position: "relative", height: "500px", width: "fit-content", marginBottom: "20px", padding: "20px", }} > {/* Bar 컴포넌트를 사용하여 차트를 렌더링한다. */} <Bar data={data} options={options} /> {/* MBTI 레이블을 차트 위에 오버레이로 표시한다. */} <div style={{ position: "absolute", top: 0, left: 0, right: 0, bottom: 0, pointerEvents: "none", }} > {mbtiData.map((item, index) => ( <div key={index} style={{ position: "absolute", left: 0, right: 0, top: `${index * 20 + 8}%`, height: "25%", display: "flex", justifyContent: "space-between", alignItems: "center", padding: "0 10px", fontSize: "14px", fontWeight: "bold", }} > <span>{item.left}</span> <span>{item.right}</span> </div> ))} </div> </div> ); }; export default MBTI;
 
위 코드에 구현되어있는 툴팁 커스터마이징 대해 더 자세히 알아보겠다.
const options = { // ... 다른 옵션들 ... plugins: { tooltip: { callbacks: { label: function (context) { const dataIndex = context.dataIndex; const datasetIndex = context.datasetIndex; const score = context.parsed.x; const item = mbtiData[dataIndex]; const label = datasetIndex === 0 ? item.left : item.right; return `${label}: ${score}%`; }, }, }, }, };
Chart.js에서 툴팁 커스터마이징은 options 객체 내의 plugins.tooltip.callbacks 속성을 통해 이루어진다. 이 MBTI 차트에서 사용된 툴팁 커스터마이징의 주요 특징은 다음과 같다.
  • callbacks 객체
    • 이 객체 내에서 다양한 콜백 함수를 정의하여 툴팁의 여러 부분을 커스터마이즈할 수 있다.
    • 이 예제에서는 label 콜백 함수만 사용되었지만, title, footer 등 다른 콜백도 사용할 수 있다.
  • label 콜백 함수
    • 이 함수는 툴팁에 표시될 각 데이터 항목의 레이블을 생성한다.
    • 함수는 context 객체를 매개변수로 받아, 현재 호버된 데이터 포인트의 정보에 접근할 수 있다.
  • context 객체 활용
    • context.dataIndex: 현재 데이터 포인트의 인덱스를 제공한다. 이를 통해 mbtiData 배열에서 해당 MBTI 항목을 찾는다.
    • context.datasetIndex: 현재 데이터셋의 인덱스를 제공한다. 0이면 왼쪽 특성(E, S, T, J), 1이면 오른쪽 특성(I, N, F, P)을 나타낸다.
    • context.parsed.x: 실제 데이터값(점수)을 제공한다.
  • 커스텀 레이블 생성:
    • MBTI 특성(E/I, S/N 등)과 해당 점수를 조합하여 사용자 친화적인 레이블을 생성한다.
    • 예: "E: 65%" 또는 "I: 35%"와 같은 형식으로 표시한다.
  • 반환 값:
    • 함수가 반환하는 문자열이 툴팁에 직접 표시된다.
 
실제 사용 시에는 다음과 같은 점을 고려할 수 있다.
  1. 데이터 입력: 현재는 고정된 데이터를 사용하고 있지만, 실제로는 사용자의 검사 결과를 입력받아 차트를 생성해야 한다.
  1. 결과 해석: 차트 아래에 간단한 해석이나 추천 직업군을 표시할 수 있다.
  1. 비교 기능: 시간에 따른 변화를 보기 위해 이전 검사 결과와 비교할 수 있는 기능을 추가할 수 있다.
  1. 상호작용: 차트의 각 부분에 마우스를 올렸을 때 상세 정보를 표시하는 등의 상호작용을 추가할 수 있다.

4.5.2 나의 소비 패턴 확인하기

이번에는 Chart.js를 활용하여 소비 패턴을 시각화하는 대시보드를 구현하는 방법을 살펴볼 것이다.

1. 개요

우리가 만들 대시보드는 다음과 같은 요소들로 구성된다.
  • 월별 지출 요약 카드
  • 카테고리별 지출 차트
  • 월간 지출 추이 라인 차트
  • 친구들과의 지출 비교 막대 차트
  • 일별 지출 버블 차트
  • 월간 지출 히트맵
 
notion imagenotion image
 
💡
참고 사항 아래에 해당하는 이미지는 금액을 나타낼 수 있는 카드로 Chart.js를 사용한 것은 아니지만 유용하게 사용될 수 있는 컴포넌트로 추가하였다. 그래서 별도의 코드 설명은 하지 않는다.
notion imagenotion image
더불어 히트맵 부분도 Chart.js 라이브러리를 사용하여 만든 UI가 아니기 때문에 별도의 코드 설명은 하지 않는다.
 

2. 데이터 준비

이 예제에서는 CSV 파일을 사용하여 데이터를 관리한다. CSV 파일을 파싱하기 위해 papaparse 라이브러리를 사용한다.
npm install papaparse
 
사용할 CSV 파일의 구조는 다음과 같다:
data_type,category,value,date monthly_expense,총액,300,2023-08-01 monthly_expense,총액,450,2023-08-05 monthly_expense,총액,320,2023-08-10 ...
 

3. 구현

1) 라이브러리 import
import { useState, useEffect } from "react"; import { Bar, Line, Doughnut, Bubble } from "react-chartjs-2"; import Papa from "papaparse"; import { Chart as ChartJS, CategoryScale, LinearScale, BarElement, LineElement, PointElement, ArcElement, Title, Tooltip, Legend, } from "chart.js";
  • React의 useState와 useEffect 훅을 사용하기 위해 import한다.
  • react-chartjs-2에서 다양한 차트 컴포넌트를 가져온다.
  • CSV 파일을 파싱하기 위해 Papa Parse 라이브러리를 import한다.
  • Chart.js의 여러요소들을 import하여 차트를 커스터마이징할 수 있게 한다.
 
2) CSV 파일 파싱
useEffect(() => { Papa.parse("/chartDashboard/data.csv", { download: true, header: true, complete: (results) => { console.log("Parsed data:", results.data); setData(results.data); }, error: (error) => { console.error("Error parsing CSV:", error); }, }); }, []);
  • useEffect 훅을 사용하여 컴포넌트가 마운트될 때 CSV 파일을 파싱한다.
  • Papa.parse 함수를 사용하여 CSV 파일을 다운로드하고 파싱한다.
  • 파싱이 완료되면 결과 데이터를 state에 저장한다.
  • 에러가 발생하면 콘솔에 로그를 출력한다.
 
3) 차트 데이터 생성 함수
const createMonthlyExpenseData = () => { const monthlyData = filterData("monthly_expense"); return { labels: monthlyData.map((item) => item.date), datasets: [ { label: "지출액", data: monthlyData.map((item) => item.value), borderColor: "#4ade80", backgroundColor: "rgba(74, 222, 128, 0.2)", tension: 0.4, }, ], }; };
  • 월별 지출 데이터를 생성한다.
  • filterData 함수를 사용하여 월별 지출 데이터만 필터링한다.
  • 차트에 필요한 형식으로 데이터를 가공하여 반환한다.
  • 날짜를 라벨로, 지출액을 데이터로 사용한다.
 
4) 차트 옵션 설정
const lineChartOptions = { responsive: true, plugins: { legend: { display: false }, tooltip: { mode: "index", intersect: false, callbacks: { label: function (context) { let label = context.dataset.label || ""; if (label) { label += ": "; } if (context.parsed.y !== null) { label += new Intl.NumberFormat("ko-KR", { style: "currency", currency: "KRW", }).format(context.parsed.y); } return label; }, }, }, }, // ... 기타 옵션 };
  • 라인 차트의 옵션을 설정하는 객체이다.
  • 차트를 반응형으로 만들고, 범례를 숨기는 설정이다.
  • 툴팁을 커스터마이징하여 한국 원화 형식으로 금액을 표시한다.
 
5) 대시보드 렌더링
return ( <div className="dashboard"> <main className="main-content"> <h1>8월 소비 내역</h1> {/* 요약 카드 */} <div className="summary-cards"> {/* ... 카드 내용 */} </div> {data.length > 0 ? ( <div className="chart-section"> {/* 카테고리별 지출 차트 */} <div className="chart-card"> <div className="chart-header"> <h3>이번달 제일 돈이 많이 나간 곳</h3> </div> <Bar data={createCategoryExpenseData()} options={{ plugins: { legend: { display: false } }, scales: { y: { display: false }, x: { grid: { display: false } }, }, }} /> </div> {/* 월간 지출 추이 차트 */} <div className="chart-card"> <div className="chart-header"> <h3>이번달 지출 추이</h3> <p>일자별 총 지출액</p> </div> <Line data={createMonthlyExpenseData()} options={lineChartOptions} /> </div> {/* ... 기타 차트 컴포넌트 */} </div> ) : ( <p>데이터를 불러오는 중입니다...</p> )} </main> </div> );
  • 대시보드의 전체 구조를 렌더링하는 JSX 코드이다.
  • 데이터가 로드되었는지 확인하고, 로드되었다면 차트를 렌더링한다.
  • 각 차트는 별도의 카드 형태로 표시되며, 헤더와 차트 컴포넌트로 구성한다.
  • 데이터가 로드되지 않았다면 로딩 메시지를 표시한다.
 
6) 카테고리별 지출 데이터 생성 함수
const createCategoryExpenseData = () => { const categoryData = filterData("category_expense"); return { labels: categoryData.map((item) => item.category), datasets: [ { data: categoryData.map((item) => item.value), backgroundColor: [ "#b1f9cb", "#5bef91", "#4ade80", "#2f8f53", "#1f5e36", "#0c2415", ], }, ], }; };
  • 카테고리별 지출 데이터를 생성하는 함수이다.
  • filterData 함수를 사용하여 카테고리별 지출 데이터만 필터링한다.
  • 각 카테고리를 라벨로, 지출액을 데이터로 사용한다.
  • 각 카테고리에 대해 다른 색상을 지정하여 시각적으로 구분하기 쉽게 만든다.
 
7) 친구 비교 데이터 생성 함수
const createFriendComparisonData = () => { const friendData = filterData("friend_comparison"); return { labels: friendData.map((item) => item.category), datasets: [ { label: "이번 달 총 지출", data: friendData.map((item) => item.value), backgroundColor: (context) => { return context.dataIndex === 0 ? "#4ade80" : "#9ca3af"; }, borderColor: (context) => { return context.dataIndex === 0 ? "#2ebd67" : "#8b97a8"; }, borderWidth: 1, }, ], }; };
  • 친구들과의 지출 비교 데이터를 생성하는 함수이다.
  • 사용자의 데이터는 첫 번째 항목으로 가정하고, 다른 색상으로 강조한다.
  • 각 친구의 이름을 라벨로, 지출액을 데이터로 사용한다.
 
8) 일별 지출 버블 차트 데이터 생성 함수
const createDailyExpenseBubbleData = () => { const dailyData = filterData("daily_expense"); return { datasets: [ { label: "일별 지출", data: dailyData.map((item) => ({ x: new Date(item.date).getDate(), y: Math.floor((new Date(item.date).getDate() - 1) / 7), r: Math.sqrt(item.value / 1000), })), backgroundColor: (context) => { const value = context.raw && context.raw.r ? context.raw.r : 0; const alpha = Math.min(value / 10, 1); return `rgba(74, 222, 128, ${alpha})`; }, }, ], }; };
  • 일별 지출 데이터를 버블 차트에 적합한 형태로 변환하는 함수이다.
  • x축은 날짜, y축은 주차, 버블의 크기는 지출액에 비례하도록 설정한다.
  • 지출액에 따라 버블의 투명도를 조절하여 시각적 효과를 높인다.
 
9) 도넛 차트 옵션
const doughnutChartOptions = { responsive: true, plugins: { legend: { position: "right" }, tooltip: { callbacks: { label: function (context) { let label = context.label || ""; if (label) { label += ": "; } if (context.parsed !== null) { label += new Intl.NumberFormat("ko-KR", { style: "currency", currency: "KRW", }).format(context.parsed); } return label; }, }, }, }, };
  • 도넛 차트의 옵션을 설정하는 객체이다.
  • 범례를 오른쪽에 위치시키고, 툴팁에 한국 원화 형식으로 금액을 표시한다.
  • 차트를 반응형으로 만들어 다양한 화면 크기에 대응할 수 있게 한다.
 
10) 가로 막대 차트 옵션
const horizontalBarOptions = { indexAxis: "y", responsive: true, plugins: { legend: { display: false }, tooltip: { callbacks: { label: function (context) { let label = context.dataset.label || ""; if (label) { label += ": "; } if (context.parsed.x !== null) { label += new Intl.NumberFormat("ko-KR", { style: "currency", currency: "KRW", }).format(context.parsed.x); } return label; }, }, }, }, scales: { x: { beginAtZero: true, ticks: { callback: function (value) { return new Intl.NumberFormat("ko-KR", { style: "currency", currency: "KRW", notation: "compact", }).format(value); }, }, }, }, };
  • 가로 막대 차트의 옵션을 설정하는 객체이다.
  • indexAxis를 'y'로 설정하여 가로 방향 막대 차트로 만든다.
  • x축에 금액을 표시할 때 한국 원화 형식으로 간단하게 표현한다.
  • 툴팁에도 한국 원화 형식으로 상세 금액을 표시한다.
 
11) 버블 차트 옵션
const bubbleChartOptions = { scales: { x: { min: 0, max: 31, ticks: { stepSize: 1, callback: (value) => (value % 7 === 1 ? value : ""), }, title: { display: true, text: "날짜", }, }, y: { min: -1, max: 4, ticks: { stepSize: 1, callback: (value) => ["", "1주차", "2주차", "3주차", "4주차", "5주차"][value + 1] || "", }, title: { display: true, text: "주차", }, }, }, plugins: { tooltip: { callbacks: { label: (context) => { const day = context.raw.x; const amount = (context.raw.r * context.raw.r * 1000).toFixed(0); return `${day}일: ${new Intl.NumberFormat("ko-KR", { style: "currency", currency: "KRW", }).format(amount)}`; }, }, }, }, };
  • 버블 차트의 옵션을 설정하는 객체이다.
  • x축은 1부터 31까지의 날짜를 나타내며, 7일마다 라벨을 표시한다.
  • y축은 주차를 나타내며, 각 주차 별로 라벨을 표시한다.
  • 툴팁은 날짜와 해당 날짜의 지출 금액을 한국 원화 형식으로 표시한다.
  • 버블의 크기(r)를 다시 원래 금액으로 변환하여 표시한다.
 
12) 차트 섹션
{data.length > 0 ? ( <div className="chart-section"> <div className="chart-card"> <div className="chart-header"> <h3>이번달 제일 돈이 많이 나간 곳</h3> </div> <Bar data={createCategoryExpenseData()} options={{ plugins: { legend: { display: false } }, scales: { y: { display: false }, x: { grid: { display: false } }, }, }} /> </div> {/* ... 다른 차트들 ... */} </div> ) : ( <p>데이터를 불러오는 중입니다...</p> )}
  • 데이터가 로드되었는지 확인하고, 로드된 경우 차트를 렌더링하는 조건부 렌더링 코드이다.
  • 각 차트는 별도의 카드 형태로 표시되며, 헤더와 차트 컴포넌트로 구성되는 것이다.
  • Bar, Line, Doughnut, Bubble 등 다양한 유형의 차트를 사용하여 데이터를 시각화하는 것이다.
  • 데이터가 아직 로드되지 않은 경우 로딩 메시지를 표시하는 것이다.
 
이렇게 구현된 대시보드는 사용자의 재무 상태를 다양한 관점에서 시각화하여 보여줄 수 있다. 월별 총지출, 카테고리별 지출 분포, 일별 지출 추이, 그리고 친구들과의 비교 등을 통해 사용자가 자신의 소비 패턴을 쉽게 파악할 수 있도록 도와주는 것이다. 코드의 양이 많은 관계로 책에는 전체 코드를 표시하지 않으며, 전제 코드에 대한 내용은 깃허브에서 확인 할 수 있다.

4.6 실무에서 사용하는 Chart.js 폴더 구조

src/ |-- components/ | |-- charts/ | | |-- BarChart.js | | |-- LineChart.js | | |-- DoughnutChart.js | | |-- BubbleChart.js | | |-- ExpenseHeatmap.js | |-- Dashboard.js | |-- SummaryCard.js | |-- hooks/ | |-- useChartData.js | |-- utils/ | |-- chartOptions.js | |-- dataProcessing.js | |-- data/ | |-- mockData.js | |-- services/ | |-- api.js | |-- styles/ | |-- chart.css | |-- dashboard.css | |-- constants/ | |-- chartColors.js | |-- chartTypes.js | |-- App.js |-- index.js
  • components/charts/: 각 차트 타입별로 컴포넌트를 분리하여 관리한다. 이렇게 하면 코드의 재사용성과 유지보수성을 향상할 수 있다.
  • hooks/useChartData.js: 차트 데이터를 가져오고 처리하는 로직을 커스텀 훅으로 분리한다. 이를 통해 데이터 로직을 재사용하고 컴포넌트를 더 깔끔하게 유지할 수 있다.
  • utils/: 차트 옵션 설정이나 데이터 처리 함수 등 재사용할 수 있는 유틸리티 함수들을 모아둔다.
  • data/: 목업 데이터나 정적 데이터를 저장한다. 개발 단계에서 유용하게 사용할 수 있다.
  • services/api.js: API 호출 관련 로직을 분리하여 관리한다. 실제 백엔드와 통신할 때 사용한다.
  • styles/: 차트와 대시보드 관련 스타일을 분리하여 관리한다.
  • constants/: 차트 색상, 차트 타입 등 상숫값들을 관리한다.

4.7 주의사항

4.7.1 공통 주의사항

  • 라이브러리 버전 호환성
    • react-chartjs-2chart.js의 버전이 서로 호환되어야 한다. 버전 불일치 시 차트가 제대로 렌더링되지 않을 수 있다.
  • 모듈 등록
    • ChartJS.register()에 필요한 모든 모듈을 등록해야 한다. 누락된 모듈이 있으면 차트의 일부 기능이 작동하지 않을 수 있다.
  • 데이터 형식
    • data 객체의 구조를 정확히 지켜야 한다. 특히 labelsdatasets 배열의 길이가 일치해야 한다.
  • 반응형 설정 options에서 responsive: true로 설정하려면 차트를 감싸는 컨테이너의 크기가 적절히 설정되어야 한다.
  • 스타일 충돌
    • 전역 CSS 스타일이 차트 스타일과 충돌할 수 있으므로, 차트 컨테이너에 고유한 클래스나 ID를 부여하고 스타일을 격리하는 것이 좋다.
  • 성능 고려
    • 대량의 데이터를 다룰 때는 성능 최적화를 위해 데이터 포인트 수를 제한하거나 차트 업데이트 빈도를 조절해야 할 수 있다. animation: false 옵션을 고려할 수 있다.
  • 타입스크립트 사용 시 주의
    • 타입스크립트를 사용하는 경우, 적절한 타입 정의를 import하고 사용해야 한다. 그렇지 않으면 타입 에러가 발생할 수 있다.
  • 범위 설정
    • 데이터의 특성에 따라 축의 범위(min, max)를 적절히 설정해야 한다.

4.7.2 차트 종류별 주의사항

  • Area Chart(영역 차트)
    • 연속적인 데이터 포인트가 필요하다.
    • 'fill' 옵션을 true로 설정하여 영역을 채우는 것이 중요하다.
    • 여러 데이터 세트를 쌓아 올릴 때는 'stacked' 옵션을 고려해야 한다.
  • Bar Chart(막대 차트)
    • 범주형 데이터와 수치 데이터가 필요하다.
    • 'barPercentage'와 'categoryPercentage' 옵션으로 막대 너비와 간격을 조절하는 것이 좋다.
    • 수직 또는 수평 방향을 'indexAxis' 옵션으로 설정할 수 있다.
  • Bubble Chart(버블 차트)
    • x, y 좌표와 크기를 나타내는 세 가지 데이터 세트가 필요하다.
    • 'radius' 옵션으로 버블의 최소 및 최대 크기를 설정하는 것이 중요하다.
  • Doughnut and Pie Charts(도넛 및 파이 차트)
    • 각 부분의 값과 레이블이 필요하다.
    • 'cutout' 옵션으로 도넛 차트의 중앙 구멍 크기를 조절할 수 있다.
    • 'rotation' 옵션으로 시작 각도를 설정할 수 있다.
  • Line Chart(선 차트)
    • 연속적인 데이터 포인트가 필요하다.
    • 'tension' 옵션으로 선의 곡률을 조절할 수 있다.
    • 'pointRadius' 옵션으로 데이터 포인트의 크기를 설정할 수 있다.
  • Mixed Chart Types(혼합 차트)
    • 여러 유형의 데이터 세트가 필요하다.
    • 각 데이터 세트의 'type' 속성을 개별적으로 설정해야 한다.
    • 축 설정에 주의를 기울여야 한다.
  • Polar Area Chart(극좌표 영역 차트)
    • 각 세그먼트의 값과 레이블이 필요하다.
    • 'startAngle' 옵션으로 시작 각도를 설정할 수 있다.
    • 'angleLines' 옵션으로 각도 선의 표시를 조절할 수 있다.
  • Radar Chart(레이더 차트)
    • 여러 차원의 데이터 세트가 필요하다.
    • 'pointLabels' 옵션으로 각 축의 레이블을 설정할 수 있다.
    • 'ticks' 옵션으로 각 축의 눈금을 조절할 수 있다.
  • Scatter Chart(산점도)
    • x와 y 좌표를 나타내는 두 개의 데이터 세트가 필요하다.
    • 'showLine' 옵션을 false로 설정하여 점만 표시할 수 있다.
    • 'pointStyle' 옵션으로 점의 모양을 변경할 수 있다.
 
이렇게 Chart.js와 React를 활용하여 다양한 차트를 구현하는 방법을 살펴보았다.
💡
이번 장의 요약
  • React와 Chart.js를 함께 사용할 때는 'react-chartjs-2' 라이브러리를 주로 활용한다.
  • Chart.js는 다양한 유형의 차트(Area, Bar, Bubble, Doughnut, Pie, Line, Mixed, Polar Area, Radar, Scatter)를 제공한다.
  • 차트 사용 시 라이브러리 버전 호환성, 모듈 등록, 데이터 형식, 반응형 설정 등에 주의해야 한다.
  • Chart.js는 x, y축 계산식, 애니메이션 효과, 컬러 설정, 데이터 처리 방법 등 다양한 커스터마이징 옵션을 제공한다.
  • MBTI 수치와 직업적성검사 결과 시각화, 소비 패턴 확인 등 실전 예제를 통해 Chart.js의 활용 방법을 학습할 수 있다.
  • Chart.js를 활용한 대시보드 구현 시 CSV 파일 파싱, 차트 데이터 생성 함수, 차트 옵션 설정 등의 단계를 거친다.