D3.js 완벽 가이드 | 데이터 시각화·차트·SVG·인터랙션·실전 활용
이 글의 핵심
D3.js로 강력한 데이터 시각화를 구현하는 완벽 가이드입니다. Selection, Scale, Axis, 차트 생성, 인터랙션까지 실전 예제로 정리했습니다.
실무 경험 공유: Chart.js에서 D3.js로 전환하면서, 커스터마이징이 자유로워지고 복잡한 시각화가 가능해진 경험을 공유합니다.
들어가며: “차트 라이브러리가 제한적이에요”
실무 문제 시나리오
시나리오 1: 커스터마이징이 어려워요
Chart.js는 제한적입니다. D3.js는 완전히 자유롭습니다.
시나리오 2: 복잡한 시각화가 필요해요
기본 차트는 부족합니다. D3.js로 모든 것을 만들 수 있습니다.
시나리오 3: 인터랙션이 필요해요
정적 차트는 부족합니다. D3.js로 풍부한 인터랙션을 제공할 수 있습니다.
1. D3.js란?
핵심 특징
D3.js는 데이터 기반 문서 조작 라이브러리입니다.
주요 장점:
- 완전한 제어: 모든 것을 커스터마이징
- SVG: 벡터 그래픽
- 데이터 바인딩: 선언적 접근
- 애니메이션: 부드러운 전환
- 인터랙션: 이벤트 처리
2. 설치 및 기본 사용
설치
npm install d3
npm install -D @types/d3
Selection
import * as d3 from 'd3';
// 선택
d3.select('#chart');
d3.selectAll('.bar');
// 속성 설정
d3.select('#chart')
.attr('width', 500)
.attr('height', 300)
.style('background', '#f0f0f0');
// 텍스트 설정
d3.select('#title').text('My Chart');
3. 데이터 바인딩
const data = [10, 20, 30, 40, 50];
d3.select('#chart')
.selectAll('div')
.data(data)
.enter()
.append('div')
.style('width', (d) => `${d * 10}px`)
.style('height', '20px')
.style('background', 'steelblue')
.style('margin', '2px')
.text((d) => d);
4. Scale
// Linear Scale
const xScale = d3.scaleLinear()
.domain([0, 100])
.range([0, 500]);
console.log(xScale(50)); // 250
// Band Scale (막대 차트용)
const xScale = d3.scaleBand()
.domain(['A', 'B', 'C', 'D'])
.range([0, 500])
.padding(0.1);
// Color Scale
const colorScale = d3.scaleOrdinal(d3.schemeCategory10);
5. 막대 차트
const data = [
{ name: 'A', value: 30 },
{ name: 'B', value: 80 },
{ name: 'C', value: 45 },
{ name: 'D', value: 60 },
];
const width = 500;
const height = 300;
const margin = { top: 20, right: 20, bottom: 30, left: 40 };
const svg = d3
.select('#chart')
.append('svg')
.attr('width', width)
.attr('height', height);
const xScale = d3
.scaleBand()
.domain(data.map((d) => d.name))
.range([margin.left, width - margin.right])
.padding(0.1);
const yScale = d3
.scaleLinear()
.domain([0, d3.max(data, (d) => d.value)!])
.range([height - margin.bottom, margin.top]);
// X Axis
svg
.append('g')
.attr('transform', `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(xScale));
// Y Axis
svg
.append('g')
.attr('transform', `translate(${margin.left},0)`)
.call(d3.axisLeft(yScale));
// Bars
svg
.selectAll('.bar')
.data(data)
.enter()
.append('rect')
.attr('class', 'bar')
.attr('x', (d) => xScale(d.name)!)
.attr('y', (d) => yScale(d.value))
.attr('width', xScale.bandwidth())
.attr('height', (d) => height - margin.bottom - yScale(d.value))
.attr('fill', 'steelblue');
6. 선 차트
const data = [
{ date: new Date('2024-01-01'), value: 30 },
{ date: new Date('2024-02-01'), value: 80 },
{ date: new Date('2024-03-01'), value: 45 },
{ date: new Date('2024-04-01'), value: 60 },
];
const xScale = d3
.scaleTime()
.domain(d3.extent(data, (d) => d.date) as [Date, Date])
.range([margin.left, width - margin.right]);
const yScale = d3
.scaleLinear()
.domain([0, d3.max(data, (d) => d.value)!])
.range([height - margin.bottom, margin.top]);
const line = d3
.line<{ date: Date; value: number }>()
.x((d) => xScale(d.date))
.y((d) => yScale(d.value));
svg
.append('path')
.datum(data)
.attr('fill', 'none')
.attr('stroke', 'steelblue')
.attr('stroke-width', 2)
.attr('d', line);
7. 인터랙션
Tooltip
const tooltip = d3
.select('body')
.append('div')
.style('position', 'absolute')
.style('background', 'white')
.style('padding', '5px')
.style('border', '1px solid #ccc')
.style('display', 'none');
svg
.selectAll('.bar')
.on('mouseover', (event, d) => {
tooltip
.style('display', 'block')
.html(`${d.name}: ${d.value}`);
})
.on('mousemove', (event) => {
tooltip
.style('left', `${event.pageX + 10}px`)
.style('top', `${event.pageY + 10}px`);
})
.on('mouseout', () => {
tooltip.style('display', 'none');
});
8. React 통합
import { useEffect, useRef } from 'react';
import * as d3 from 'd3';
export default function BarChart({ data }) {
const svgRef = useRef<SVGSVGElement>(null);
useEffect(() => {
if (!svgRef.current) return;
const svg = d3.select(svgRef.current);
svg.selectAll('*').remove();
const width = 500;
const height = 300;
const margin = { top: 20, right: 20, bottom: 30, left: 40 };
const xScale = d3
.scaleBand()
.domain(data.map((d) => d.name))
.range([margin.left, width - margin.right])
.padding(0.1);
const yScale = d3
.scaleLinear()
.domain([0, d3.max(data, (d) => d.value)!])
.range([height - margin.bottom, margin.top]);
svg
.selectAll('.bar')
.data(data)
.enter()
.append('rect')
.attr('class', 'bar')
.attr('x', (d) => xScale(d.name)!)
.attr('y', (d) => yScale(d.value))
.attr('width', xScale.bandwidth())
.attr('height', (d) => height - margin.bottom - yScale(d.value))
.attr('fill', 'steelblue');
}, [data]);
return <svg ref={svgRef} width={500} height={300} />;
}
정리 및 체크리스트
핵심 요약
- D3.js: 데이터 시각화 라이브러리
- 완전한 제어: 모든 것을 커스터마이징
- SVG: 벡터 그래픽
- 데이터 바인딩: 선언적 접근
- Scale: 데이터 변환
- 인터랙션: 이벤트 처리
구현 체크리스트
- D3.js 설치
- Selection 학습
- Scale 이해
- 막대 차트 구현
- 선 차트 구현
- 인터랙션 추가
- React 통합
같이 보면 좋은 글
- Three.js 완벽 가이드
- React Native 완벽 가이드
- 데이터 시각화 가이드
이 글에서 다루는 키워드
D3.js, Data Visualization, Chart, SVG, JavaScript, Frontend, Analytics
자주 묻는 질문 (FAQ)
Q. Chart.js와 비교하면 어떤가요?
A. D3.js가 훨씬 강력하고 자유롭습니다. Chart.js는 더 간단하지만 제한적입니다.
Q. 러닝 커브가 높은가요?
A. 네, 초기 학습이 어렵습니다. 하지만 강력한 기능을 제공합니다.
Q. React와 함께 사용할 수 있나요?
A. 네, useRef와 useEffect로 통합할 수 있습니다.
Q. 프로덕션에서 사용해도 되나요?
A. 네, New York Times, Bloomberg 등 많은 곳에서 사용합니다.