[d3.js] d3 랑 친해지기 - 기초

Castle_Junny·2023년 4월 3일
0

d3.js랑 친해지기

목록 보기
1/4
post-thumbnail

1. d3.js 란?

  • 데이터 주도문서의 약자(Data-Driven Documnt)
  • 웹에서 다양한 형식의 데이터를 이용하여 시각화를 할 수 있도록 도와준다.
  • CSS3, HTML5, SVG등 웹 표준 기능을 최대로 활용하여 독점적인 기술은 피하고 커다란 융통성을 제공 => d3.js의 근본적 특징

2. d3.js와 친해지기

2.1 svg

  • svg(Scalable Vector Graphics)는 기본적으로 벡터(vector)타입으로 수학저인 함수 관계로 만든 그래픽 이미지이다
  • 확대/축소, 회전 등에 손상이 일어나지 않는다.
  • svg는 다양한 도형, 선, 곡선, 텍스트 등 다양한 요소를 제공된다

2.2 svg에서 사용하는 요소 (참고)

d3.js를 처음 접할 때 svg로 데이터를 시각화 해본 경험이 없어서 되게 생소하였지만, 간단한 요소들을 그려 봄으로써 d3의 컨셉을 잘 이해할 수 있게 되었다.

2.2.1 circle, rect, line 요소 그려보기

  • mdn 추가
<script src="https://d3js.org/d3.v7.min.js"></script>
  • div 생성
<div id="d3_test"></div>
  • 요소 그리기
    // svg를 그릴 div 선택 
    const svg = d3.select('#d3_test')
        .append('svg')
        .attr('width','100%')
        .attr('height','100vh')
        .style('background-color','pink')

    // circle 추가 !!
    svg.append('circle')
        .attr('fill','blue')
        .attr('r', 30)
        .attr('cx', 35)
        .attr('cy', 35)

    // rect 추가 !!
    svg.append('rect')
        .attr('fill', 'red')
        .attr('x', 70)
        .attr('y', 50)
        .attr('rx', 5)
        .attr('width', 40)
        .attr('height', 40)

    // line 추가 !!
    svg.append('line')
        .attr('x1', 10)
        .attr('x2', 60)
        .attr('y1', 60)
        .attr('y2', 90)
        .attr('stroke', 'yellow')

별 다른 설명을 안해도
svg 요소에 circle, rect, line을 추가하는구나~ 라고 생각 할 수 있다. 개인적인 생각으론 이 개념이 절반을 먹고 들어가는 것 같다.

여기에 하나의 객체에 연속적으로 메서드를 호출하고 메서드가 실행된 후 객체 자신을 반환하는 method chaining 개념도 들어간다

2.2.2 요소 grouping

데이터를 시각화 하다보면 내가 만든 도형들을 grouping 해야하는 경우가 있다. 이때 사용하는게 <g> 요소이다

<g>는 공간으르 따로 차지 않고, 요소들을 논리적으로 그룹화한다.
예를 들어 circle안에 text가 있다고 할 경우 따로 따로 생성하고 움직이는 것 보다 두개를 하나의 group으로 묶어서 이용하는게 더 편리하다.

    const group1 = svg.append('g')

    // circle 추가 !!
    group1.append('circle')
        .attr('fill','yellow')
        .attr('r', 30)
        .attr('cx', 35)
        .attr('cy', 35)

    group1.append('text')
        .text('input text')
        .attr("x",'10')
        .attr("y",'35')
        .style("font-size",'12px')

하지만 <g>요소를 이동하려면 transform 속성을 조정해야한다.

2.3 정규화 : 데이터 스케일

현재 d3.js를 이용하여 세계/국내 지도 위에 데이터를 시각화 하는 업무를 하다보니 데이터 정규화작업이 중요하게 다가왔다.
표출해야하는 화면의 규격은 작고, 데이터는 크고.. 만약 sacle을 조절하지 않으면 도형으로 화면이 꽉 찼을 것이다.

2.3.1 d3.scaleLinear()

범위 안에 있는 값들을 선형으로 scale 조절 하는 것
쉽게 이야기하면 주어지는(input 되는) 데이터의 범위(domain)사용자 범위(range)이내로 줄이는 것이다.

scalesLineae()의 경우 function을 return 하기에 값을 전달해주어야 한다.

    const linear = d3.scaleLinear().domain([300(min), 1800(max)]).range([10(최소크기), 100(최대크기)]);

console을 찍어보면 function이 반환 됨

  • circle을 그릴 데이터 (min : 300, max : 1800)
    const data = [
        {x: 15, y: 40, r: 300, color: '#ffdde1'},
        {x: 50, y: 40, r: 600, color: '#304352'},
        {x: 90, y: 40, r: 900, color: '#d7d2cc'},
        {x: 15, y: 90, r: 1200, color: '#CCCCB2'},
        {x: 50, y: 90, r: 1500, color: '#3498db'},
        {x: 90, y: 90, r: 1800, color: '#ee9ca7'},
    ]
  • circle 그리는 코드 (with. scaleLinear())
    const linear = d3.scaleLinear().domain([300, 1800]).range([10, 100]);


    svg.selectAll('circle')
        .data(data)
        .enter()
        .append('circle')
        .attr('fill', d => d.color)
        
        // -----------------------------
        // linear를 안해주면 화면을 가득 채운다.
        
        // .attr('r', d => d.r) 
        .attr('r', d => linear(d.r))
        // -----------------------------
        
        
        .attr('cx', d => d.x * 3)
        .attr('cy', d => d.y * 3)

3. 데이터 바인딩

d3에서는 셀렉션으로 웹 페이지으 구조를 바꾼다. 셀렉션은 하나 이상의 DOM 요소로 구성되며 연관된 데이터를 가질 수도 있다.

데이터를 바인딩 하는 방법에 대해서 header를 달아가며 설명하기 어려운 것 같아서 구구절절 정리를 해보려고 한다.

  • select() 은 하나의 요소만 선택
  • selectAll()은 모든 요소 선택

예시 상황

   <div class="container">
    <div class="test_div" id="id1"></div>
    <div class="test_div" id="id2"></div>
    <div class="test_div" id="id3"></div>
    <div class="test_div" id="id4"></div>
</div>

3.1 "id2" 에 특정 데이터를 넣으려면??

    const data = ['test1', 'test2', 'test3', 'test4', 'test5'];
    
    const select = d3.select('#id2')
        .data(data)
        .append('text')
        .text(d => d)
  • 결과 : test1 하나만 들어간다

3.2 'test_div'에 모두 데이터를 넣으려면?

    const data = ['test1', 'test2', 'test3', 'test4', 'test5'];

    const selectAll = d3.selectAll('.test_div')
        .data(data)
        .append('text')
        .text(d => d)
  • 결과

3.3 부모요소(container)의 내부에 새로운 요소를 추가하려면?

append()를 이용해서 추가하면 된다.

enter()

  • data 배열의 내용물은 다섯개 이고 (데이터 개수)
  • container 내부에 <div>네 개 이다. (dom 개수)

데이터를 select 하여 biding 할 때 데이터의 개수와 dom의 개수가 차이가 날 때 어떻게 처리할 수 있을까???

  • enter() ??
    데이터가 dom보다 더 많아 잔여 데이터를 어떻게 처리할 지 정의한다.

  • enter() 하기전
    먼저 enter()를 하지 않으면 4개의 dom에만 text가 작성된다.

const data = ['test1', 'test2', 'test3', 'test4', 'test5'];
    
const select = d3.select('.container')
        .selectAll('div')
        .data(data)
        .append('div')
        .text(d => d)
  • 중간 결과

  • enter() 적용 시
    새로 변수를 호출해서 data binding을 해주고, enter()를 한 뒤 잔여 데이터 에 대해 어떻게 할건지 정의 해주었다.

    const addEnter = select
        .data(data)
        .enter()
        .append('h1')
        .text(d => d);
  • enter() 결과

3.4 데이터를 업데이트 하려면?

updata는 다양한 의미가 있을 거 같은데, 아래에서는 1.데이터 변경2. 로직 실행 시 데이터 update(?) 두가지에 대해서 설명하고 있다.

3.4.1 지우고 다시 그리기

d3.js에는 update 함수가 따로 없다. 그렇기에 dom 내부를 지운 뒤 다시 그리는 방식으로 진행하고 있다.
내부를 지우는 방법도 동일하게 원하는 dom 선택 후 remove를 해주면 된다.

    const remove = d3.select('.container')
        .selectAll('div')
        .remove();        
  • remove 결과

3.4.2 로직 진행 시 데이터 업데이트

배열(Array)혹은 JSON 형태의 데이터를 다룰 경우 특정 컬럼 값에 대해서 연산을 해줄 필요가 있다. 보통 데이터에 대한 연산은 외부에서 선행되는 경우가 일반적이겠지만, 불가피한 경우 로직에서 구현하기도 한다.

그때는 each()를 이용해보자.


    const each = d3.select('.container')
        .selectAll('div')
        .data(data)
        .each((d,i) => console.log('each index = %o , data = %s ',i,d));

4. 결론

d3는 자유도가 높은 만큼 사용자가 모든 걸 다 설정해야하기 때문에 사용하기 번거로운 라이브러리인거 같다. 특히나 지도를 다루다 보니 안그래도 어려운 거에 어려운게 더해져서 겁나 어렵다..

아무튼 d3.js를 사용하면서 헤깔리고 정리를 하면 좋을거 같은 부분들을 간단하게 정리해보았다.
이렇게 정리해보니 확실히 이해도가 많이 향상되었다,

0개의 댓글