본문 바로가기
Vue.js

[Chart.js] Vue에서 사용하기.

by devebucks 2021. 7. 26.
728x90

안녕하세요.

회사에서 외주로 맡겼던 제품코드를 보면서, Chart.js라는 라이브러리를 처음 접하게 되었습니다. 기존에는 apex chart를 제품에 사용하고 있었습니다만, chart.js 또한, 커스텀이 광범위하게 가능하다는 점에서 많이 사용하고 있는 것 같더라구요.

 

그래서, chart.js를 사용해서 실제 작업 중인 프로젝트에 처음 적용해보면서 공부한 내용을 정리해보고자 합니다.

제가 chart.js로 프로토타입은 만든 결과는 다음과 같습니다.

 

Chart 만들기

Vue 프레임워크에 chart.js를 사용해서 chart를 화면에 그리는 과정을 설명한 글입니다.

일단, 공용으로 여러 컴포넌트에서 재사용할 수 있도록,  파일 구성을 다음처럼 구성했습니다.

📂src
| - 📂Modules
    |- 📂LineChart
        |- 📄 index.vue       // 👈 LineChart를 임포트하는 컴포넌트, 다른 컴포넌트 통합 사용.
        |- 📄 LineChart.vue  // 👈 chart.js 인스턴스가 생성되고 차트가 그려지는 <canvac> 요소가 있는 컴포넌트.
    |- ...
    |- 📂 Main
        |- 📄 TagChart.vue  // 👈 LineChart파일을 임포트해서 차트를 그리는 모달 컴포넌트.

 

npm chart 페이지

npm i chart.js

 

Chart.js 가이드 문서

가이드 문서대로 코드 입력

data property에서 chart에 사용되는 데이터를 다 넣어 보려고 했는데, 

차트가 들어갈 <canvas> HTML 요소 id값을 chart 생성자의 매개변수로 넣어줘야 합니다. 그래서, data property에서 chart 생성자에 필요한 모든 매개변수를 갖출 수 없었습니다.

Vue 프레임워크의 mounted 훅에서 chart 생성자를 정의했습니다.

그리고, mounted 훅에서 DOM요소를 쿼리할 수 없습니다. mounted 훅안에서 nextTick을 사용해서 완전히 mount과 보장된 상태에서 생성자가 만들어지도록 했습니다.

// 📂 LineChart.vue
<template>
    <div>
        <canvas id="chart" width="400" height="400"></canvas>
    </div>
</template>
<script>
import { Chart } from 'chart.js';
export default {
    methods: {
        fillData() {
            const ctx = document.getElementById('chart').getContext('2d');
            this.myChart = new Chart(ctx, {
                type: 'line',
                data: {},
                option: {}
            });
        }
    },
    mounted() {
        this.fillData();
    },
    data() {
        return {
            myChart: null
        };
    }
};
</script>
<style lang=""></style>

 

chart.js의 예시 데이터를 넣고 실행해 보니, 에러가 발생했습니다.

에러 발생

[Vue warn]: Error in mounted hook: "Error: "linear" is not a registered scale."found in

일반 Javascript에서 사용하는 방법과 달리, bundler(webpack)을 사용한 프로젝트에서는 chart.js에서 Chart에 사용할 모듈들을 import하고, Chart에 등록하는 작업이 필요했습니다.

왜 별도로 처리해야 하는지 좀 알아봤습니다.
webpack은 죽은 코드를 제거합니다.(참고: webpack tree-shaking) 이걸 tree-shaking이라 부릅니다. ES2015 내장 스펙입니다.
chart.js 3은 webpack에 의해서 tree-chaking될 수 있다고 합니다.(참고: Chart.js-bundler ) 그래서, bundler를 사용한다면, 개발자가 chart.js에서 사용하려는 controller, elements, scales 그리고 plugins을 직접 import하고 register해줘야 하는 겁니다.

 

chart의 모듈을 import와 register하는 방법은 chart.js 문서에 잘 나와있습니다.

가이드에서는 다음처럼 사용했습니다.

 

위에 처럼 많이 등록하고 사용할 거면, 그냥 chart.js의 모듈을 모두 등록하는 방법도 알려주고 있습니다.

 

 

저는 아래 코드처럼 사용할 부분만 불러서 사용했습니다.

<template>
 <!-- ... -->
</template>
<script>
   import { Chart, BarElement, BarController, LinearScale, CategoryScale } from 'chart.js'; //👈 Chart 모듈 임포트
   Chart.register(BarElement, BarController, LinearScale, CategoryScale);  // 👈 chart.js 모듈 Chart 모듈에 등록
   export default {
      methods: {
         //...
      
      }
   }
//...

</script>

 

정상적으로 화면에 차트가 그려졌습니다.

 

 

컴포넌트 전체 코드

<template>
    <div>
        <canvas id="chart" width="400" height="400"></canvas>
    </div>
</template>
<script>
import { Chart, BarElement, BarController, LinearScale, CategoryScale } from 'chart.js';
Chart.register(BarElement, BarController, LinearScale, CategoryScale);
export default {
    methods: {
        fillData() {
            const ctx = document.getElementById('chart').getContext('2d');
            this.myChart = new Chart(ctx, {
                type: 'bar',
                data: {
                    labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
                    datasets: [
                        {
                            label: '# of Votes',
                            data: [12, 19, 3, 5, 2, 3],
                            backgroundColor: [
                                'rgba(255, 99, 132, 0.2)',
                                'rgba(54, 162, 235, 0.2)',
                                'rgba(255, 206, 86, 0.2)',
                                'rgba(75, 192, 192, 0.2)',
                                'rgba(153, 102, 255, 0.2)',
                                'rgba(255, 159, 64, 0.2)'
                            ],
                            borderColor: [
                                'rgba(255, 99, 132, 1)',
                                'rgba(54, 162, 235, 1)',
                                'rgba(255, 206, 86, 1)',
                                'rgba(75, 192, 192, 1)',
                                'rgba(153, 102, 255, 1)',
                                'rgba(255, 159, 64, 1)'
                            ],
                            borderWidth: 1
                        }
                    ]
                },
                options: {
                    scales: {
                        y: {
                            beginAtZero: true
                        }
                    }
                }
            });
        }
    },
    mounted() {
        this.fillData();
    },
    data() {
        return {
            myChart: null
        };
    }
};
</script>
<style lang=""></style>

 

 

그런데 또 다른 문제가 있었습니다.

여러 개의 버튼으로 클릭하면, 하나의 차트 컴포넌트를 재사용해서 다른 데이터의 차트를 그려줄 계획이었습니다.

 

첫 번째 버튼을 눌렀을 때, chart는 잘 나왔습니다. 문제 없었는데,

두 번째 버튼을 눌러서 모달 창에 차트를 띄우는 순간부터 에러가 발생했습니다.

에러는 다음과 같습니다.

[Vue warn]: Error in nextTick: "Error: Canvas is already in use. Chart with ID '17' must be destroyed before the canvas can be reused."

쉽게 말해서, 이미 chart 인스턴스가 들어가 있는 변수에 다시 chart 생성자를 사용할 수 없다는 메시지입니다.

 

이 해결 방법도 chart.js 문서에 나와있습니다.

chart 인스턴스에 destroy()함수를 사용하면 됩니다.

 

그래서 저는 생성자가 사용되기 전에 미리 chart 인스턴스가 변수에 들어 있는지 확인하고, destroy함수를 사용해서 인스턴스를 제거하고, 같은 변수에 새로 인스턴스를 대입하는 방식으로 이 문제를 해결했습니다.

 

 

이렇게 해결하고 나니, 다시 문제가 발생했습니다.

첫 번째 차트는 차트가 잘 나왔습니다. 두 번째 버튼을 눌러서 뜬 모달에는 차트가 그려지지 않았습니다.

개발자 도구에는 에러 메시지도 없었습니다. 

 

 

이 문제도 마찬가지로 계속 같이 chart 컴포넌트를 사용했기 때문이었습니다.

<canvas id="">에서 id값을 계속 동일하게 사용한 것이 문제였습니다.

 

같은 컴포넌트로 여러 차트를 그리려면 canvas 요소의 id값이 서로 고유해야 했습니다.

고유하게 값을 맞춰주니 정상적으로 모든 차트가 동작했습니다.

728x90

댓글