본문 바로가기
BOOKS/수월한 자바스크립트

chart.js를 사용한 데이터 시각화하기 #2/3

by IT여행자 2024. 12. 27.
728x90


https://jobtc.tistory.com/146 (기본 차트)
https://jobtc.tistory.com/147 (실시간 차트)
https://jobtc.tistory.com/148 (서버 데이터 차트)

 

안녕하세요 IT여행자입니다.

 

두 번째 시간으로 이번에는 로컬에서 실시간으로 변경되는 데이터를 사용하여 그래프를 동적으로 그려서, 보다 생동감 있는 데이터 시각화를 해 보도록 하겠습니다.

 

주요 요점은 아래의 2가지입니다.

  • line 그래프에서 꺾인 부분을 부드럽게 표현하기
  • 실시간으로 데이터를 발생시켜 그래프에 적용하기

1. line 그래프에서 꺾인 부분을 부드럽게 표현하기

line 그래프에서 꺾인 부분을 부드럽게 표현하려면 tension 속성값을 지정하면 됩니다. tension은 곡률을 의미하는데, tension의 범위는 0~1까지입니다. 0으로 갈수록 직선이 되며, 1로 갈수록 곡선형으로 변경됩니다.

datasets: [
          {
              label: '판매', 
              data: data,
              type : 'line',
              borderWidth: 2, /* 선의 굵기 */
              borderColor: '#00f',
              pointBackgroundColor:colors, /* 포인트의 색상 */
              pointRadius: 4, /* 포인트의 크기 */
              tension : .35 /* 곡률 */
          },

 

2. 실시간으로 그래프 그리기

데이터가 서버로부터 전달되는 것이 아니라 로컬에서 변경되는 상황이라 설정하고 그래프를 동적으로 그리도록 하겠습니다. 

<button onclick="prev()">prev</button>
<button onclick="next()">next</button>
<button onclick="auto()">auto</button>

 

먼저 버튼 3개 만들고 각각 이전 데이터(prev), 다음 데이터(next), 실시간 변경데이터(auto) 형식으로 작업하도록 하겠습니다. prev 버튼과 next 버튼이 클릭되면 아래와 같은 함수가 호출됩니다.

var prev = ()=>{
    var data=[44,22,66,44,88,55,77]
    myChart.data.datasets[0].data = data;
    myChart.data.datasets[1].data = data;
    myChart.update();
}
var next = ()=>{
    var data=[77,66,55,88,99,33,88]
    myChart.data.datasets[0].data = data;
    myChart.data.datasets[1].data = data;
    myChart.update();
}

 

임의의 데이터를 data변수에 담은 뒤, myChart.data.datasets [0]과 myChart.data.datasets [1]은 화면에 그려진 첫 번째 그래프와 두 번째 그래프를 의미하며 이 객체의 data 속성에 동일한 데이터를 대입하여 같은 값을 갖는 그래프를 화면에 표시하였습니다. 그러나 전체 코드를 보면 알 수 있듯이 첫 번째 그래프는 막대그래프를 그리게 되고, 두 번째 그래프는 선 그래프를 그리게 됩니다.

 

auto 버튼을 클릭하면 약 20회에 걸쳐 렌덤 하게 발생한 데이터가 두 개의 그래프에 각각 적용되어 그래프의 모양은 다르지만 같은 값의 데이터가 화면에 출력됩니다.

var auto = async ()=>{
    let cnt=0;
    while(cnt<20){
        var data = [];
        for(i=0; i<7; i++){
            data.push(Math.random()*90+10);
        }
        myChart.data.datasets[0].data = data;
        myChart.data.datasets[1].data = data;
        myChart.update();
        cnt++;
        await new Promise(resolve => setTimeout(resolve, 1000)); /* 시간 지연 */
    }        
}

 

반복문 while을 다시 반복하기 전에 setTimeout() 함수를 사용하여 시간 지연 효과를 집어넣었습니다. new Promise()는 새로운 promise를 생성하는데 이를 해결하기 위해서는 resolve 콜백함수를 리턴 받아야 합니다. 따라서 setTimeout의 두 번째 인수값이 1000(1초)이 지나면 콜백 함수인 resolve를 실행하여 resolve를 리턴합니다. 이 resolve가 리턴될 때까지 기다리도록 하기 위해 new Promise() 앞에 await 명령을 추가하였습니다.  그런데 await 명령은 반드시 동기화 블록 내에서만 사용할 수 있기 때문에 해당 함수 블록의 시작 부분에 async명령을 사용하게 된 것입니다.

 

만약 이런 식으로 시간 지연이 일어나지 않으면 차트를 렌더링 하기도 전에 반복문이 모두 처리되고 말 것이기 때문에 정상적인 그래프를 볼 수 없게 됩니다. 

<!DOCTYPE html>
<html lang="ko">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="http://cdn.jsdelivr.net/npm/chart.js"></script>
    <title>chart step 2</title>
    <style>
        #chart {
            width: 600px;
            height: 300px;
            position: relative;
        }

        .btnZone {
            display: flex;
            justify-content: center;
        }
    </style>
</head>

<body>
    <h3 class="info">
        동적 챠트 그리기
    </h3>
    <div id="chart">
        <canvas id="myChart"></canvas>
        <div class="btnZone">
            <button onclick="prev()">prev</button>
            <button onclick="next()">next</button>
            <button onclick="auto()">auto</button>
        </div>
    </div>

    <script>
        var ctx = document.querySelector('#myChart').getContext('2d')
        var data = [10, 50, 30, 70, 90, 40, 55]
        var x_labels = ['1월', '2월', '3월', '4월', '5월', '6월']

        var colors = ['#f55a', '#5f5a', '#ff5a', '#f5fa', '#345a', '#123a', '#986a']
        var myChart = new Chart(ctx, {
            data: {
                labels: x_labels, /*x축 라벨 */
                datasets: [
                    {
                        label: '판매',
                        data: data,
                        type: 'line',
                        borderWidth: 2, /* 선의 굵기 */
                        borderColor: '#00f',
                        pointBackgroundColor: colors, /* 포인트의 색상 */
                        pointRadius: 4, /* 포인트의 크기 */
                        tension: .35 /* 곡률 */
                    },
                    {
                        label: '순이익',
                        data: data,
                        type: 'bar',
                        borderWidth: 0,
                        backgroundColor: colors,
                    },

                ]
            },
            options: {
                responsive: true, /* 차크 크기 자동 조정 */
                plugins: {
                    title: {
                        display: true,
                        text: '전체 판매현황',
                    }
                },
                scale: {
                    y: {
                        min: 0,
                        max: 110,
                        ticks: {
                            stepSize: 20,
                        }
                    }
                }
            }
        })

        var prev = () => {
            var data = [44, 22, 66, 44, 88, 55, 77]
            myChart.data.datasets[0].data = data;
            myChart.data.datasets[1].data = data;
            myChart.update();
        }
        var next = () => {
            var data = [77, 66, 55, 88, 99, 33, 88]
            myChart.data.datasets[0].data = data;
            myChart.data.datasets[1].data = data;
            myChart.update();
        }

        var auto = async () => {
            let cnt = 0;
            while (cnt < 20) {
                var data = [];
                for (i = 0; i < 7; i++) {
                    data.push(Math.random() * 90 + 10);
                }
                myChart.data.datasets[0].data = data;
                myChart.data.datasets[1].data = data;
                myChart.update();
                cnt++;
                await new Promise(resolve => setTimeout(resolve, 1000));
            }
        }
    </script>
</body>
</html>

 

 

auto 버튼이 클릭되면 차트의 모양이 20회 실시간으로 변화되는 것을 확인해 볼 수 있습니다.