서론
처음으로 React에서 D3(D3.js)를 사용할 때 들었던 궁금점은 "D3와 React 중 어느 쪽에서 DOM을 제어할 것인가?"였습니다. React와 D3 모두 DOM을 변경할 수 있기 때문입니다.
이번 글에서는 React에서 D3를 사용하는 방식에 대해 이야기해보려고 합니다.
D3.js란 무엇인가?
D3는 Data-Driven Documents의 약자로 이름 그대로 데이터 시각화를 위한 라이브러리입니다. D3는 차트를 그리는 기능뿐만 아니라, 데이터 시각화를 위한 수학적인 계산을 처리하는 기능도 함께 제공합니다. (예시: d3-scale)
이제 React 환경에서 D3를 사용하는 방식을 알아봅시다.
React에서 D3 사용하기
D3로 DOM 조작하기
useRef로 빈 <svg> 컨테이너를 생성하고, useEffect 내부에서 D3에게 DOM 조작을 위임하는 것입니다. 이 방식은 React를 단순히 컨테이너 역할로만 사용합니다.
import { useRef, useEffect } from 'react';
import * as d3 from 'd3';
const Chart = ({ data }) => {
const svgRef = useRef(null);
useEffect(() => {
const svg = d3.select(svgRef.current);
if (!svg) {
return;
}
svg.selectAll('*').remove();
const lineGenerator = d3
.line()
.x((d) => d.x)
.y((d) => d.y);
svg.append('path')
.datum(data)
.attr('d', lineGenerator)
.attr('stroke', 'steelblue')
.attr('fill', 'none');
svg
.selectAll('circle')
.data(data)
.enter()
.append('circle')
.attr('cx', (d) => d.x)
.attr('cy', (d) => d.y)
.attr('r', 5);
}, [data]);
return <svg ref={svgRef} width={500} height={300} />;
};
export default Chart;
React로 DOM 조작하기
D3의 역할을 데이터의 계산으로 한정하고, React에게 DOM 조작을 위임하는 것입니다. D3는 DOM에 필요한 데이터를 계산하고, React가 DOM 조작의 역할을 가집니다.
import * as d3 from 'd3';
const Chart = ({ data }) => {
const lineGenerator = d3
.line()
.x((d) => d.x)
.y((d) => d.y);
const pathData = lineGenerator(data);
return (
<svg width={500} height={300}>
<path d={pathData} stroke="steelblue" fill="none" />
{data.map((d, i) => (
<circle key={i} cx={d.x} cy={d.y} r={5} />
))}
</svg>
);
};
export default Chart;
어떤 방식이 더 좋은가?
개인적으로 "React로 DOM을 조작하는 방식"의 경험은 괜찮았습니다. DOM이 그려지는 형태를 JSX로 확인 가능하고 D3와 React의 책임을 분리할 수 있는 장점이 있었습니다.
하지만 이 방식은 DOM 요소를 일일이 JSX로 관리해야 단점이 있습니다. 특히 프로젝트 규모가 커질수록 "어느 수준까지 공통 코드로 추상화할 것인가?"에 대한 커뮤니케이션 비용을 발생시킬 수 있습니다. 이 경우 recharts, visx 와 같은 라이브러리가 대안이 될 수 있습니다.
결론
서론에서 던졌던 "D3와 React 중 어느 쪽에서 DOM을 제어할 것인가?"라는 질문에 대한 답은 다음 두 가지 관점에서 찾을 수 있습니다.
첫째, 관심사의 분리입니다. 코드의 가독성, 예측 가능성을 고려하여 관심사를 분리하여 유지보수가 쉬운 구조에 대한 고민이 필요합니다.
둘째, 데이터와 DOM 간의 동기화 비용입니다. 비록 이번 글에서는 다루지 않았지만, 관리해야 할 DOM의 개수가 많은 상황이라면 두 방식에 대한 성능 벤치마크에 대한 고민이 필요합니다.
두 방식 중 정답은 없지만, React에서 D3를 사용하는데 도움이 되었길 바랍니다.
긴 글 읽어주셔서 감사합니다.
참고 자료
- [FEConf Korea] [B3] React Component와 D3 Object를 유기적으로 연결하는 전략 - 박승현
- 선언적 프로그래밍에 대한 착각과 오해
댓글