D3.js는 데이터에 기반해 문서를 조작하는 자바스크립트 라이브러리이다. D3는 HTML, SVG, CSS를 이용해 데이터를 생동감 있게 표현한다.
const svgElement = d3.select(ref.current);
svgElement.attr('width', width).attr('height', height);
svgElement.append('g').attr('transform', `translate(${margin}, ${margin})`);
React와 D3.js 모두 DOM에 대한 제어권을 갖기 때문에 이를 컨트롤 해주어야 한다. 해당 프로젝트에서는 useRef 훅을 통해서 할당받은 reference를 svg에 지정하고 아래 코드와 같이 select한다. 그리고 svg에 크기와 색상과 같은 기본 속성을 부여한다.
또한 리액트의 컴포넌트가 자주 리랜더링되는 점을 고려해 D3역시 중복되어 랜더링 되는 것을 방지해야 한다. 따라서 useEffect 훅을 통해 랜더링 되는 조건을 제한한다.
var xScale = d3.scaleLog().domain([0.01, 1000]).range([0, innerWidth]);
var yScale = d3.scaleLinear().domain([-70, -40]).range([innerHeight, 0]);
다음으로 축에 대한 스케일을 지정한다. 스케일은 데이터를 표현할 방식에 대한 설정이다. domain()메서드를 이용해 렌더링할 값의 범위를 지정하고 range()를 통해 렌더링할 사이즈를 지정한다.
const xAxis = d3.axisBottom(xScale);
const yAxis = d3.axisLeft(yScale);
const xAxisG = svgElement.append('g').call(xAxis);
const yAxisG = svgElement.append('g').call(yAxis);
xAxisG.attr('transform', `translate(${margin}, ${innerHeight + margin})`);
yAxisG.attr('transform', `translate(${margin}, ${margin})`);
아래는 축을 랜더링하는 코드이다. 축을 그리는 방식을 지정한 뒤 축을 그린다. 다음으로 축을 오프셋 이동하여 사용자에게 익숙한 형태로 보여준다.
const line = svgElement.selectAll('.line');
line.data(data)
.enter()
.append('path')
.attr('fill', 'none')
.attr('stroke-width', stkWidth)
.attr('class', 'line')
.attr('d', function (d) {
return d3
.line()
.x(function (d) {
return xScale(d.xVal);
})
.y(function (d) {
return yScale(d.yVal);
})
.curve(d3.curveBasis)(d.vals);
})
데이터를 이용해 그래프 라인을 랜더링했다. selectAll()메서드를 이용해 객체를 가지정했다.
datum, data
렌더링할 데이터를 선택하는 방법에는 두 가지가 있는데 datum()메서드는 고정적인 데이터를 렌더링하고 data()메서드는 가변적인 데이터를 렌더링한다.
데이터를 지정했으면 attr()메서드를 이용해서 해당 요소에 속성을 부여한다. 속성에는 선의 굵기, 클래스 이름, 오프셋 등이 있다. D3.js는 메서드 체이닝을 지원하는데 이러한 패턴은 D3.js의 특징 중 하나이다.
D3.js의 또 다른 특징은 메서드 내부에서 콜백함수의 역할이다. 위 코드와 같이 function(d) { ... }로 콜백함수를 받는데 이 때 인자 d의 값은 data()메서드에서 지정한 데이터이다. 따라서 해당 콜백함수를 이용해 객체 데이터의 변수를 추출한 뒤 그 값을 사용 할 수 있다. 위 코드에서는 콜백함수를 통해 데이터에 존재하는 x값, y값을 추출했다. 만약 위와 같은 복합데이터일 때는 체인메서드 끝에 (d.vals)와 같이 데이터를 세부지정해 접근할 수 있다.
enter, exit, update
가변적인 데이터를 다루는 경우 먼저 데이터와 요소의 개수를 비교한다. 만약 데이터의 개수가 더 많은 경우에는 enter()메서드와 append()메서드를 통해 요소를 추가한다. 요소의 개수가 더 많은 경우에는 exit()메서드와 remove()메서드를 통해 매칭되지 않는 요소를 삭제한다. update는 enter, exit과 더불어 자주 언급되는 개념이다. 하지만 나머지 두 개의 개념과 달리 이를 지원하는 메서드는 없다. 즉 update()라는 메서드는 존재하지 않는다. update를 하는 방법은 간단하다. 데이터를 추가하거나 삭제하는 것이 아니라 현재 존재하는 데이터의 속성(attr)를 변경하는 것이다. 따라서 업데이트 의존목록에 맞추어 useEffect를 생성하여 업데이트 작업에 대한 내용을 랜더링할 때와 마찬가지로 작성하면 된다. 업데이트 작업에 대한 코드는 아래와 같다.
useEffect(() => {
const newLine = d3.select(ref.current);
newLine.selectAll('.line').attr('stroke', function (d) {
return channelToColor(d);
});
}, [buttons]);
canvas API는 선을 직접 그리는 개념이다. 하지만 D3js는 캔버스와 같이 그래픽을 그리는 것이 아니라 단지 DOM처럼 문서 객체를 관리하고 D3가 그 문서에 기반해 그래픽을 랜더링 하는 형식이다. 이 개념이 이해하기 어려우면서도 중요한 개념이다.
'SSLAB' 카테고리의 다른 글
DECK.GL (0) | 2021.07.12 |
---|---|
git branch 관리요령 (0) | 2021.06.21 |
Header Layout (0) | 2021.06.21 |