D3.js 완벽 가이드 | 데이터 시각화·차트·SVG·인터랙션·실전 활용

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 등 많은 곳에서 사용합니다.

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3