차트 라이브러리는 숫자 데이터를 시각적인 그래픽(막대 차트, 선 그래프, 파이 차트 등)으로 바꿔주는 코드 묶음임.
“라이브러리”라는 말 자체가 미리 작성해둔 코드를 재사용 할 수 있게 묶어놓은 것을 의미함. 차트 라이브러리는 그중에서도 차트를 그리는 데 특화된 라이브러리임.
차트 라이브러리는 여러 언어로 존재하는데(Python의 matplotlib, Java의 JFree Chart 등), 이번 스터디에서는 웹에서 사용하는 JavaScript 차트 라이브러리에 한정해서 알아봄.
"Chart.js is a free, open-source JavaScript library for data visualization, which supports eight chart types: bar, line, area, pie (doughnut), bubble, radar, polar, and scatter." — Chart.js 공식 문서: Introduction
: Chart.js는 8가지 차트 타입을 지원하는 무료 오픈소스 라이브러리임.
"Recharts is a Redefined chart library built with React and D3. The main purpose of this library is to help you to write charts in React applications without any pain."— Recharts 공식 문서: Getting Started
: Recharts는 React에서 차트를 쉽게 그릴 수 있도록 만들어진 라이브러리임.
차트를 직접 그리려면 생각보다 많은 작업이 필요함:
차트 라이브러리는 이런 복잡한 과정을 대신 처리해줌. 개발자는 데이터와 간단한 옵션만 전달하면 됨.
차트 라이브러리가 내부에서 어떤 과정을 거쳐서 숫자를 그림으로 바꾸는지 알아봄.
핵심은 3단계 과정:
1. 정규화 → 2. 스케일 계산 → 3. 렌더링
정규화는 들어온 데이터를 일정한 형식으로 맞추는 과정임.
예를 들어, 사용자가 이렇게 데이터를 넘겼다고 가정해보자:
// 사용자가 넘긴 데이터const data = [{ month: 'Jan', value: 100 },{ month: 'Feb', value: 200 },{ month
차트 라이브러리는 내부적으로 이걸 처리함.
// 라이브러리 내부에서 정규화된 데이터const normalizedData = [{ month: 'Jan', value: 100 },{ month: 'Feb', value: 200 },{ month
동시에 최솟값(0)과 최댓값(200)도 계산해둠. 다음 단계에서 필요하기 때문.
스케일 계산은 “데이터 값”을 화면에 그릴 위치”로 바꾸는 과정임.
이게 차트의 핵심인데, 예를 들어:
데이터: [100, 200, 150, 180]차트 높이: 400픽셀의 상황..!!!질문: "값 150"은 화면의 어느 높이에 그려야 할까?
이런 상황의 답을 주는 게 스케일 계산의 과정!
비율 = 값 / 최댓값픽셀 위치 = 비율 × 차트 높이
만약 최댓값이 200이고 차트 높이가 400픽셀이라면:
scale(value) = ((value - min) / (max -
// 스케일 함수function createScale(min, max, chartHeight) {return function(value) {const ratio = (value - min) / (max - min);
여기서 한 가지 주의할 점이 있음‼️
화면 좌표계는 위에서 아래로 증가함.

그래서 차트를 그릴 때는 Y좌표를 뒤집어야 함:
// 스케일 결과: 값이 클수록 큰 숫자scale(200); // → 400// 화면에 그릴 때: Y좌표 뒤집기const y = chartHeight - scale(value);// 값 200 → y = 400 - 400 = 0 (화면 맨 위)// 값 100 → y = 400 - 0 = 400 (화면 맨 아래)
이렇게 하면 "값이 클수록 막대가 위로" 그려짐.
렌더링은 계산된 좌표를 바탕으로 실제 도형을 화면에 그리는 과정임.
const data = [100, 200, 150, 180];const scale = createScale(100, 200, 400); // 위에서 만든 스케일 함수data.forEach((value,
여기서 "실제로 막대를 그리는 방식"이 SVG와 Canvas, 두 가지로 나뉨. 이건 다음 섹션에서 다룰 내용으로 넘어가겠음!
상단에 Rechart 공식문서(“Recharts is built with React and D3”)에서도 언급하기도 했고 D3.js 라는 라이브러리 이름을 들어봤을 수 있음. 근데 엄밀히 말하면 D3는 차트를 “그려주는”라이브러리는 아니고, 스케일 계산을 도와주는 도구(수학느낌)로, 특히 스케일(scale) 계산을 중심으로 한 변환 로직에 가까움.
위에서 살펴봤듯이, 차트를 그릴 때 스케일 개념이 필요하지만 구현 디테일은 사실 라이브러리마다 다를 수 있음.
예를 들어:
등….
그래서 많은 차트 라이브러리들은 D3의 스케일 개념(혹은 구현)을 직접 차용하거나, 거의 동일한 로직을 재구현해서 사용함.
결과적으로 라이브러리 간 차이는 스케일의 ‘수학’보다는 ‘옵션과 디테일’에서 발생함.
이제 “실제로 화면에 그리는 방법”을 알아봄!
크게 SVG 방식과 Canvas 방식 두가지가 있음.
SVG(Scalable Vector Graphics)로 막대 3개를 그리면 이런 코드가 됨:
<svg width="400" height="300"><rect x="10" y="200" width="30" height="100" fill="blue" /><rect x="50" y="150" width="30" height="150" fill="red"
각 <rect> 태그가 막대 하나를 나타냄. HTML 요소처럼 브라우저가 이 구조를 기억하고 있음
이걸 “Retained Mode”렌더링이라고 함. ”Retain”은 유지하다라는 뜻이니까 “그린 내용을 유지하는 방식”이라고 이해하면 좋음.
"SVG is XML based, which means that every element is available within the SVG DOM. You can attach JavaScript event handlers for an element."— MDN: SVG Tutorial

F12 → Elements 탭에서 이렇게 보임. (막대가 하나하나 <rect> 태그로 존재하는 거 확인 가능)
각 막대를 클릭해서 선택할 수 있고, 속성도 직접 수정할 수 있음.
// 첫 번째 막대 색상 바꾸기const bar = document.querySelector('rect');bar.setAttribute('fill', 'purple');// 클릭 이벤트 달기
Recharts는 React 전용이고, SVG 방식으로 차트를 그림:
import { BarChart, Bar, XAxis, YAxis } from 'recharts';const data = [{ name: 'Jan', value: 100 },

Canvas로 막대 3개를 그리면:
const canvas = document.getElementById('myChart');const ctx = canvas.getContext('2d'); // "2D로 그릴 거야"// 파란색 막대 그리기ctx.fillStyle = 'blue';ctx.fillRect(
getContext(’2d’) 는 “이 캔버스에 2D그림을 그릴 준비를 해줘” 라는 의미임. 그 다음부터 fillRect() 같은 명령으로 도형을 그림.
핵심뽀인트! 그리는 순간 픽셀로 찍히고 끝. 브라우저는 “여기에 파란 막대다 있다”라는 걸 기억하지 않음. 그냥 픽셀 색깔만 바뀐 것.
이걸 “Immediate Mode” 렌더링이라고 함. ”Immediate”는 “즉시”라는 뜻으로, “즉시 그리고 끝내버리는 방식”으로 이해하면 됨.
"Canvas provides a means for drawing graphics via JavaScript and the HTML <canvas> element."— MDN: Canvas API

F12 → Elements 탭에서 이렇게 보임. (<canvas> 태그 하나만 보임. 막대 3개가 그려져 있지만, 브라우저는 그 내용을 모름. 그냥 하나의 그림일 뿐)
데이터 1000개를 그려도 브라우저가 관리할 요소는 오직 <canvas> 하나! 그래서 데이터가 많아져도 비교적 완만하게 느려짐(대체로 데이터 개수에 비례함)
최종 그림만 저장하면 되니까 메모리를 적게 씀.
“두번 째 막대 색 바꿔줘”라고 해도, 브라우저는 기억하고 있지 않음. (최종 그림만 기억하는 상태) 전체를 지우고 처음부터 다시 그려야 함.
// 다시 그리려면ctx.clearRect(0, 0, canvas.width, canvas.
chart.js는 Canvas 방식으로 차트를 그림.
Canvas 렌더링 덕분에 Chart.js는 특히 대용량 데이터셋과 복잡한 시각화에서 뛰어난 성능을 보인다고 공식문서에서 Canvas를 선택한 이유도 설명하고 있음.
"Chart.js renders chart elements on an HTML5 canvas... Canvas rendering makes Chart.js very performant, especially for large datasets and complex visualizations."— Chart.js 공식 문서: Performance
번역: "Canvas 렌더링 덕분에 Chart.js는 특히 대용량 데이터셋과 복잡한 시각화에서 뛰어난 성능을 보임."
// react-chartjs-2로 테스트해본 코드import {Chart as ChartJS,CategoryScale,LinearScale,BarElement,Tooltip,Legend,} from 'chart.js';

위에서 Canvas는 "DOM이 없어서 이벤트 처리가 복잡하다"고 했음. 그런데 Chart.js로 만든 차트에 마우스를 올리면 툴팁이 잘 뜸.
어떻게 된 걸까?!!
결론부터 말하면:
Canvas 차트에서 hover 툴팁은 라이브러리가 좌표 계산 + 히트 테스트 + 상태 관리를 전부 대신 해주는 것임.
Chart.js에서 hover 툴팁이 뜨는 실제 흐름:
[마우스 이동]↓[마우스 좌표 계산]↓[어느 데이터 영역 위인지 판단] - hit test↓[해당 데이터 index 찾기]↓[툴팁 내용 계산]↓[툴팁을 Canvas에 "다시 그려서" 표시]
| 항목 | SVG | Canvas |
| 렌더링 방식 | Retained Mode (유지) | Immediate Mode (즉시) |
| DOM 구조 | 각 도형이 요소로 존재 | <canvas> 하나만 존재 |
| 성능 (데이터 많을 때) | 급격히 느려짐 | 비교적 완만하게 느려짐 |
| 개별 요소 제어 | 쉬움 | 어려움 (직접 계산) |
| 이벤트 처리 | 요소에 직접 부착 | 좌표 계산 필요 |
| 확대 | 선명 |
3-panel-chart.html 활용
제가 직접해본 결과 기준..!
극단적인 확인을 위해 4x slowdown으로 확인해봄.

1. 타임라인 영역 (맨 위)

이건 시간 축임. 왼쪽이 과거, 오른쪽이 미래. 슬라이더를 움직인 순간부터 렌더링이 끝날 때까지의 시간이 표시됨.
2. Frames 영역

이건 화면이 실제로 그려진 순간을 보여줌.
스크린샷을 보면 프레임 사이 간격이 꽤 넓음. 10,000개 데이터를 처리하느라 화면이 잠깐 멈췄다는 의미임.
3. Main 영역

이게 실제로 어떤 코드가 실행됐는지 보여주는 부분임.
읽는 법:
스크린샷에서 renderChartJS가 가장 긴 막대임. 449ms나 걸렸다는 뜻.

읽는 법:
스크린샷에서 constructor가 447.1ms인 게 보임. Chart.js가 새 차트를 만드는 과정 자체가 오래 걸리는 것.
<BarChart data={data}><Bar dataKey="value" /></BarChart>
예를 들어 데이터가 [50, 100, 75]이고 차트 높이가 300픽셀이라면:

rect:hover {fill: orange;}


막대를 클릭했는지 알려면 마우스 좌표를 직접 계산해야 함:
canvas.addEventListener('click', (e) => {const x = e.offsetX;const y = e.offsetY;// "이 좌표가 어느 막대 범위 안인지" 직접 계산...if (x >= 10 && x <= 40 && y >= 200) {console.log('첫 번째 막대 클릭');}});

Canvas는 픽셀 그림이라서 확대하면 뿌옇게 보임.



이걸 매 마우스 이동마다 반복.
내부적으로는 이런 데이터가 저장:
Chart.js는 그림만 그리는 게 아니라, "그림을 그릴 때 쓴 계산 결과"를 JS 객체로 저장해둠.
개념적으로는 이런 구조
bars = [{index: 0,label: 'Jan',value: 100,x: 40,y: 180,width: 40,height: 120,},{index: 1,label: 'Feb',value: 200,x: 100,y: 80,width: 40,height: 220,},];
마우스가 움직일 때마다 이런 판단을 함:
if (mouseX >= bar.x &&mouseX <= bar.x + bar.width &&mouseY >= bar.y &&mouseY <= bar.y + bar.height) {
히트 테스트(hit test) 라고 부름.
툴팁도 Canvas에 그린 그림임:
중요한 포인트
즉, 툴팁이 뜰 때 내부적으로는:
그래서 툴팁도 픽셀! DOM에서 선택 불가능하고, CSS로 스타일링도 안 됨 (옵션으로만 가능).
| 대표 라이브러리 | Recharts | Chart.js |