D3.js로 차트 그리기
D3.js로 차트 그리기
데이터 로딩하기
1. CSV 데이터 로딩
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// CSV 파일 구조
// date,value
// 2024-01-01,10
// 2024-01-02,15
// 2024-01-03,12
d3.csv('data.csv').then(data => {
// 데이터 파싱
const parsedData = data.map(d => ({
date: new Date(d.date),
value: +d.value // 문자열을 숫자로 변환
}));
createChart(parsedData);
}).catch(error => {
console.error('Error loading data:', error);
});
2. JSON 데이터 로딩
1
2
3
4
5
6
7
8
9
10
11
12
13
// JSON 파일 구조
// [
// {"date": "2024-01-01", "value": 10},
// {"date": "2024-01-02", "value": 15}
// ]
d3.json('data.json').then(data => {
const parsedData = data.map(d => ({
date: new Date(d.date),
value: d.value
}));
createChart(parsedData);
});
스케일 함수 이해하기
스케일 함수는 데이터 값을 화면 픽셀 값으로 변환한다. D3.js는 다양한 유형의 스케일을 제공한다.
1. 선형 스케일 (Linear Scale)
연속적인 수치 데이터를 위한 가장 기본적인 스케일이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
// 기본 사용법
const yScale = d3.scaleLinear()
.domain([0, 100]) // 데이터 범위
.range([height, 0]); // 픽셀 범위
// 고급 사용법
const yScale = d3.scaleLinear()
.domain([0, d3.max(data, d => d.value)]) // 데이터의 최대값 사용
.range([height - margin.bottom, margin.top])
.nice(); // 도메인을 적절한 값으로 조정
// 사용 예시
const y = yScale(15); // 데이터 값 15를 픽셀 값으로 변환
2. 밴드 스케일 (Band Scale)
범주형 데이터를 일정한 너비의 밴드로 매핑할 때 사용한다. 주로 막대 차트에서 활용된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 기본 사용법
const xScale = d3.scaleBand()
.domain(['A', 'B', 'C', 'D']) // 카테고리 배열
.range([0, width]) // 픽셀 범위
.padding(0.1); // 밴드 간 여백 (0~1)
// 실제 데이터 활용
const xScale = d3.scaleBand()
.domain(data.map(d => d.category))
.range([margin.left, width - margin.right])
.paddingInner(0.1) // 밴드 내부 여백
.paddingOuter(0.2); // 밴드 외부 여백
// 사용 예시
const x = xScale('A'); // 카테고리 'A'의 시작 위치
const bandWidth = xScale.bandwidth(); // 밴드의 너비
3. 포인트 스케일 (Point Scale)
범주형 데이터를 동일한 간격의 점으로 매핑할 때 사용한다. 산점도나 라인 차트의 점 위치에 주로 사용된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 기본 사용법
const xScale = d3.scalePoint()
.domain(['A', 'B', 'C', 'D']) // 카테고리 배열
.range([0, width]) // 픽셀 범위
.padding(0.5); // 양끝 여백
// 실제 데이터 활용
const xScale = d3.scalePoint()
.domain(data.map(d => d.category))
.range([margin.left, width - margin.right]);
// Band Scale과의 차이점
// 1. bandwidth() 메서드가 없음
// 2. 각 카테고리가 하나의 점으로 매핑됨
// 3. padding은 전체 범위의 비율로 적용
// 사용 예시
const x = xScale('A'); // 카테고리 'A'의 점 위치
const step = xScale.step(); // 점 사이의 간격
4. 시간 스케일 (Time Scale)
시간 데이터를 다룰 때 사용하는 특별한 형태의 선형 스케일이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 기본 사용법
const xScale = d3.scaleTime()
.domain([new Date('2024-01-01'), new Date('2024-12-31')])
.range([0, width]);
// 실제 데이터 활용
const xScale = d3.scaleTime()
.domain(d3.extent(data, d => d.date)) // 데이터의 시작일과 종료일
.range([margin.left, width - margin.right])
.nice(d3.timeMonth); // 월 단위로 도메인 조정
// 날짜 포맷팅과 함께 사용
const xAxis = d3.axisBottom(xScale)
.ticks(d3.timeMonth.every(1)) // 매월 눈금 표시
.tickFormat(d3.timeFormat('%Y-%m')); // YYYY-MM 형식
스케일 활용 예제: 복합 차트
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 여러 스케일을 조합한 차트 예제
const chart = {
// 카테고리형 X축 (Point Scale)
xScale: d3.scalePoint()
.domain(data.map(d => d.category))
.range([margin.left, width - margin.right])
.padding(0.5),
// 수치형 Y축 (Linear Scale)
yScale: d3.scaleLinear()
.domain([0, d3.max(data, d => d.value)])
.range([height - margin.bottom, margin.top])
.nice(),
// 시간 기반 부차적 X축 (Time Scale)
timeScale: d3.scaleTime()
.domain(d3.extent(data, d => d.date))
.range([margin.left, width - margin.right])
};
// 산점도 그리기
svg.selectAll('circle')
.data(data)
.join('circle')
.attr('cx', d => chart.xScale(d.category))
.attr('cy', d => chart.yScale(d.value))
.attr('r', 5);
// 연결선 그리기 (Line Generator 사용)
const line = d3.line()
.x(d => chart.xScale(d.category))
.y(d => chart.yScale(d.value));
svg.append('path')
.datum(data)
.attr('d', line)
.attr('fill', 'none')
.attr('stroke', 'steelblue');
축 만들기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// X축 생성
const xAxis = d3.axisBottom(xScale)
.ticks(5) // 눈금 개수 지정
.tickFormat(d3.timeFormat('%Y-%m-%d')); // 날짜 형식 지정
// X축 그리기
svg.append('g')
.attr('class', 'x-axis')
.attr('transform', `translate(0,${height - margin.bottom})`)
.call(xAxis);
// Y축 생성
const yAxis = d3.axisLeft(yScale)
.tickSize(-width + margin.left + margin.right) // 그리드 라인
.ticks(5);
// Y축 그리기
svg.append('g')
.attr('class', 'y-axis')
.attr('transform', `translate(${margin.left},0)`)
.call(yAxis);
// 축 스타일링
svg.selectAll('.y-axis line')
.style('stroke', '#ddd');
실전 예제: 라인 차트 만들기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
// 데이터 예시
const data = [
{ date: '2024-01-01', value: 10 },
{ date: '2024-01-02', value: 15 },
{ date: '2024-01-03', value: 12 },
{ date: '2024-01-04', value: 18 },
{ date: '2024-01-05', value: 14 }
];
// 1. 차트 크기 설정
const width = 800;
const height = 400;
const margin = {top: 20, right: 30, bottom: 30, left: 40};
// 2. SVG 생성
const svg = d3.select('#chart')
.append('svg')
.attr('width', width)
.attr('height', height);
// 3. 데이터 파싱
const parsedData = data.map(d => ({
date: new Date(d.date),
value: +d.value
}));
// 4. 스케일 설정
const xScale = d3.scaleTime()
.domain(d3.extent(parsedData, d => d.date))
.range([margin.left, width - margin.right]);
const yScale = d3.scaleLinear()
.domain([0, d3.max(parsedData, d => d.value)])
.range([height - margin.bottom, margin.top]);
// 5. 라인 생성기
const line = d3.line()
.x(d => xScale(d.date))
.y(d => yScale(d.value))
.curve(d3.curveMonotoneX); // 부드러운 곡선
// 6. 라인 그리기
svg.append('path')
.datum(parsedData)
.attr('class', 'line')
.attr('d', line)
.style('fill', 'none')
.style('stroke', 'steelblue')
.style('stroke-width', 2);
// 7. 데이터 포인트 추가
svg.selectAll('circle')
.data(parsedData)
.join('circle')
.attr('cx', d => xScale(d.date))
.attr('cy', d => yScale(d.value))
.attr('r', 4)
.style('fill', 'steelblue');
// 8. 축 추가
const xAxis = d3.axisBottom(xScale);
const yAxis = d3.axisLeft(yScale);
svg.append('g')
.attr('class', 'x-axis')
.attr('transform', `translate(0,${height - margin.bottom})`)
.call(xAxis);
svg.append('g')
.attr('class', 'y-axis')
.attr('transform', `translate(${margin.left},0)`)
.call(yAxis);
스타일링과 차트 꾸미기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 1. CSS 스타일링
svg.selectAll('.line')
.style('stroke-dasharray', '5,5'); // 점선 스타일
// 2. 그리드 라인 추가
svg.selectAll('g.y-axis g.tick')
.append('line')
.attr('x2', width - margin.left - margin.right)
.style('stroke', '#ddd')
.style('stroke-dasharray', '2,2');
// 3. 차트 제목 추가
svg.append('text')
.attr('x', width / 2)
.attr('y', margin.top)
.style('text-anchor', 'middle')
.text('차트 제목');
// 4. 축 레이블 추가
svg.append('text')
.attr('x', width / 2)
.attr('y', height - 5)
.style('text-anchor', 'middle')
.text('시간');
svg.append('text')
.attr('transform', 'rotate(-90)')
.attr('x', -height / 2)
.attr('y', 15)
.style('text-anchor', 'middle')
.text('값');
This post is licensed under CC BY 4.0 by the author.