[Chart.js + Vue (+ TypeScript)] 반응형, 도넛 차트 가운데 글자, typescirpt 사용 시 options type에러 해결
그래프, 차트를 화면에 보여줘야 될 일이 있어서 사용하게 된 chart.js 사용 이유는 간단했다.
1. vue와의 호환 🧪
가장 중요한게 프로젝트가 vue라 호환돼야 사용할 수 있었는데 chart.js는 호환되고, vue 전용 wrapper도 있다.
📈 vue-chartjs
vue-chartjs.org
2. 다양한 옵션 ⚙️
chart.js 공식 문서의 옵션이나 추가적인 플러그인만 봐도 옵션이 아주 다양하다는 걸 알 수 있다.
진짜 방대해서 옵션을 다 공부해야지 보다 필요할 때 찾아봐도 웬만한건 다 있다.
아주 디테일하게 조정을 못할지언정 기능적으로는 찾아보면(물론 딥하게 찾아야 할 때가 많음) 거의 다 있다.
3. 다른 사람들도 이거 많이 쓰던데? 😀
이게 은근 중요하다. 왜냐하면 사람들이 많이 사용했다라는건 그만큼 퀄리티나 사용성에서 보장을 한 번 거쳤다고 판단되고,
내가 기능을 구현하기 전 다른 사람들도 필요한 기능에 대해 미리 서치하고 시행착오도 겪어보고 글도 남겨주고⭐️
이게 왜 중요하냐면 누군가가 미리 구현을 해서 글을 쓴걸 보고 내가 구현한다는건 그만큼 시행착오를 거치지 않아도 돼 시간을 아낄 수 있기 때문이다.
이 외에도 캔버스로 렌더링하여 성능이 좋다, 반응형 지원, 다양한 차트 유형 등등이 있어서 chart.js로 선택했다.
설치나 찾으면 금방 나오는 정보에 대해서는 다른 좋은 블로그도 많으니 내가 겪었던 문제 중 좀 찾기 어렵거나 구현에 애먹었던 문제를 중심으로 글을 써보고자 한다.
1. 반응형
의외로 chart.js 반응형 구현이 시간이 많이 걸렸고, 최근에야 간단한 방법을 알게 됐다.
<div style="position: relative; width: 100%; height: 200px">
<Line :data="LineCharttData" :options="LineChartOptions" />
</div>
해결 방법
차트를 감싼 div 요소(부모 요소) 생성 후 style에 positon: relative; 적용해주고 width나 height에 원하는 크기 사용하기, 만약 부모 요소에 따라서 크기 조정하고 싶다면 %로 적고 창 크기로 반응형 하고 싶다면 vw로 적어주기
차트옵션에서 responsive: true, maintainAspectRatio: false로 설정
과정
필요한 차트의 크기 지정을 위해 div로 차트를 감싸주고 여기서 너비와 높이를 정해줘야한다. < 고정된 너비는 블로그 글이 많아서 금방 적용할 수 있었다.
하지만 내가 필요한건 고정된 너비가 아니라 반응형으로 브라우저 창크기나 부모 요소 크기에 맞게 변화를 주고 싶었다.
chart.js에 보면 onResize같은 옵션이 있어서 이걸 이용하거나 canvas resize를 해야된다. 이런 글도 보여서 시도하다가
공식 문서 반응형 파트에 대놓고 적혀 있는걸 발견했다.
영어 공부 많이해서 훑어보더라도 이런게 눈에 들어오도록 해야겠다. 이걸 아예 안본건 아닌데 해도 안됐어서(= 밑에 옵션까지 추가 제대로 안해서) 잘 안보게 됐었다.
안되면 공식문서가 이해 안가는데 구현하고 나서야 공식문서가 이해되고 적힌 그대로가 맞았다는걸 알게 된다...ㅋㅋㅋ 역시 나는 돼야 이해하는 사람인가보다.
삽질하지 말라고 떠먹여주는데 꼭 삽질을 한다니깐🥲 덕분에 차트가 막 늘어난다거나 흐리게 보여진다거나 했던 경험 제대로 했다. 아님 창이 늘때만 차트 반응하고 멈춰있다든가.
요점은 canvas에 직접적으로 반응형 사이즈 적어놓는건 비추천하고, 작동이 잘 안된다는 것 부모 요소에 작성하라는 거다. 이렇게 하면 늘렸다 줄었다. 자유자재로 가능👍
출처 : https://www.chartjs.org/docs/latest/configuration/responsive.html#configuration-options
Responsive Charts | Chart.js
Responsive Charts When it comes to changing the chart size based on the window size, a major limitation is that the canvas render size (canvas.width and .height) can not be expressed with relative values, contrary to the display size (canvas.style.width an
www.chartjs.org
https://yeon22.github.io/Chartjs-kr/docs/latest/general/responsive.html
여기는 누군가 한글로 번역해둔 문서다. 정리 너무 잘 돼있고, 영어 못하는 나한테 한 줄기 빛이었다.
반응형 · Chart.js 문서
반응형 차트 창 크기를 기준으로 차트 크기를 변경하는 경우, 캔버스 렌더링 크기(canvas.width 와 .height)를 디스플레이 크기(canvas.style.width 와 .height)와 달리 상대 값으로 표현할 수 없다는 주요 제
yeon22.github.io
왜 해결에 시간이 걸렸을까?
가장 근본적인 이유는 영어 문서에 익숙하지 않다는 점.
구현하는거에 급급해서 문서를 제대로 꼼꼼하게 읽어보지 않고 시도하고 안되면 다른 글을 찾아봤던 점.
= 마음이 급해서 보이는 결과에만 집중해 오히려 더 돌아갔다.
개발하면서 시도하다 안되면 다음으로 넘어가서 다른걸 해야되는 상황이 있고, 문서를 시간이 걸리더라도 읽고 확인한 후 적용해야하는 상황이 있다고 생각한다.
맘이 급하면 전자의 방식을 많이 사용하게 되는데 공식문서에 해당 파트가 명백히 적혀 있고, 그걸 발견했다면 시간을 들여서 확인하는게 시간을 단축 시킬 수 있을 것 같다.
2. 옵션
알다싶이 chart.js에는 옵션이 정말 정말 많다.
처음 사용할때 왜 옵션 적용이 안되는거야?? 할만한 것들 대부분이 스코프 문제였다.
그니까 정확한 범위에 옵션을 적지 않으면 해당 옵션이 적용이 안될 수 있다.
Namespace 라고 적힌 부분이 옵션을 적는 위치다.
밑에 예시로 보자면 options객체 안에 plugins 속성에 legend 안에 있는 속성이고 그 안에서 유효하다는 말이다.
default는 안 적었을 때 해당 속성이 적용된다는 의미 type도 중요한데 적힌 그대로 boolean이면 true, false를 적어주고 string이면 string값 넣어주면 된다.
const options = {
plugins:{
legend:{
display:false
}
}
}
이 Namespace 진짜 중요하다! 처음에 왜 안되지 싶은거 대부분 namespace 안 지켜서 그런거다. 실수하기 쉬운 부분이 끝에 s빼먹었거나 아님 저기 적힌 namespace 뎁스 이름 그대로 안 적었을 때 혹은 타입 틀렸을수도 있으니 왜 안되지 싶을 때는 가장 먼저 이걸 확인해보고 검색하자
진짜 처음 쓰는 사람은 헷갈릴수도 있고, 문서 자세히 안보는 사람이면 가장 실수하기 쉽다고 생각드는 부분이었다. 물론 나도 그랬고
속성 안적어도 default 값이 있는 경우에는 해당 속성이 자동으로 적용되니 이런 부분도 확인해보면 시간을 아낄 수 있을것이다!
이거 했는데 안되는데 싶으면 혹시 등록 안했나 봐야한다. chart.js에서는 사용하려면 등록을 해야 사용할 수 있는게 있다.
밑에 링크 타면 좀 더 자세히 확인 가능하다. 필요한 부분 꼭 등록 후 사용하기로 밑에 코드에서 방법 2 적용시 갑자기 적용된다면 등록을 안한거 100% datalabels 플러그인 설치 안했다면 해당 부분 제외하고 적용하기!
https://www.chartjs.org/docs/latest/getting-started/integration.html
Integration | Chart.js
Integration Chart.js can be integrated with plain JavaScript or with different module loaders. The examples below show how to load Chart.js in different systems. If you're using a front-end framework (e.g., React, Angular, or Vue), please see available int
www.chartjs.org
// 방법 1 필요한 것만 적용
import {
Chart as ChartJS,
Title,
Tooltip,
Legend,
Filler,
BarElement,
PointElement,
LineElement,
CategoryScale,
LinearScale,
} from 'chart.js';
import ChartDataLabels from 'chartjs-plugin-datalabels'; // 이건 따로 설치한 플러그인
ChartJS.register(
CategoryScale,
LinearScale,
BarElement,
PointElement,
LineElement,
Title,
Tooltip,
Legend,
Filler,
ChartDataLabels
);
// 방법 2 다 불러오기 (만약 등록 안돼서 작동 안되나 확인할 때 사용해도 좋을 듯)
import { Chart as ChartJS, registerables } from 'chart.js';
import ChartDataLabels from 'chartjs-plugin-datalabels';
ChartJS.register(...registerables, ChartDataLabels); // ChartDataLabels는 따로 추가
3. PlugIn
chart.js는 많은 사람들이 사용하고 좋은 플러그인도 많다.
따로 설치해서 쓰는 플러그인 중 가장 추천하는건 datalabels다. 라벨 커스텀에 유용하고 시각화하기 좋다.
이 외에도 좀 구현하기 어렵거나 이미 있을 것 같은건 plugin을 살펴보자 이미 구현 돼있을 경우가 많다!
있으면 설치 후 간단히 사용하면 된다.
사람들이 사용한 예제를 보면서 구현하는걸 추천
https://chartjs-plugin-datalabels.netlify.app/guide/getting-started.html
Getting Started | chartjs-plugin-datalabels
Getting Started Installation npm (opens new window) (opens new window) CDN (opens new window) (opens new window) By default, https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels returns the latest (minified) version, however it's highly recommended (open
chartjs-plugin-datalabels.netlify.app
4. 도넛차트 가운데 글자 넣기
이것도 플러그인 있는데 왠지 모르겠지만 내가 했을 때 안되더라고;;
5년전 업데이트긴한데 이거 써서 되면 이거 사용하시고 아니면 내가 찾은 방법이 있는데 그건 설치 안해도 간단하니 먼저 밑에 해결방법 보고 되길!!
급하면 파란 글씨랑 코드만 확인하세요!! 해결하느라 시간 많이써서 괜히 주절거려 쓸데없는 글이 많습니다.
https://www.npmjs.com/package/chartjs-plugin-doughnutlabel
chartjs-plugin-doughnutlabel
Chart.js plugin for doughnut chart to display lines of text in the center. Latest version: 2.0.3, last published: 5 years ago. Start using chartjs-plugin-doughnutlabel in your project by running `npm i chartjs-plugin-doughnutlabel`. There are 4 other proje
www.npmjs.com
찾아보면 canvas fillText 사용하는건 알겠는데 그래서 canvas context객체 어디서 얻냐고,,,
진짜 나한테는 context객체 찾기가 너무 어려웠다.
vue chart.js를 사용하여 wrapping한 형식이 조금 다른 부분이 있어 chart.js 공식문서에서 config 부분을 어디에 써야하는지 한참을 찾고 시도했어야 됐기 때문이다.
이게 맞는 방법이 아닐 수 있지만 나는 vue chart.js wrapper 하는 부분에 id를 넣어주고 plugins를 바인딩하였다. 그리고 chart.js에 적힌 config를 참고해(밑에 링크 있어요 canvas background 링크) beforeDraw로 context객체를 얻어 사용했다.
<div style="width: 500px">
<Doughnut
id="myChart"
:data="doughnutData"
:options="doughnutOption"
:plugins="[plugin]"
/>
</div>
const plugin = {
id: 'myChart', // 차트에 표시한 id
beforeDraw: (chart: any) => {
const ctx = chart.ctx;
const { width, height } = chart.chartArea;
const { data } = myData.value; // 만약 고정된 데이터가 아니라 유동적인 값이라면 변수 사용 가능
const text = '표시할 내용'; // 도넛 차트 가운데 넣을 글자 1
const text2 =
Number(data).toLocaleString() + '원'; // 나는 2줄로 표시해야해서 하나 더 만들었다. 이건 유동적인 값일 때
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
const textY = height / 2; // 높이 조절
ctx.fontWeight = 'bold'; // 폰트 굵기 조절
ctx.font = "600 16px '폰트스타일'"; // 적용할 font 속성 넣기 (차트 글자 1)
ctx.fillText(text, width / 2, textY - 10); 차트에 넣을 글자 1의 위치 조정
ctx.font = "600 24px '폰트스타일'"; // 적용할 font 속성 넣기 (차트 글자 2)
ctx.fillText(text2, width / 2, textY + 10); 차트에 넣을 글자 2의 위치 조정
},
};
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillText
CanvasRenderingContext2D: fillText() method - Web APIs | MDN
The CanvasRenderingContext2D method fillText(), part of the Canvas 2D API, draws a text string at the specified coordinates, filling the string's characters with the current fillStyle. An optional parameter allows specifying a maximum width for the rendere
developer.mozilla.org
키워드 캔버스에 글자 텍스트 출력이나 fillText 로 검색하면 친절하게 설명된 블로그가 많을거다.
그거 보고 canvas에 context객체 filltext를 사용해 넣으면 되겠구나는 알았는데
chart.js에서 도넛차트 가운데 글자 넣기위해서 canvas 정보 가져와야하는데 어디 있는지 모르겠었고, 또 ref 사용해서 가져온 context에는 filltext속성이 없고 읽기형식으로만 되어 있어서 이 fillText가 있는 context 가져오는게 힘들었다.
외부에서 chart에 접근하기보다는 chart.js에서 context 객체에 접근이 가능하면서도 바로 그려지는 옵션이 필요했다.
beforeDraw를 사용해서 context객체를 얻어 fillText를 이용해 값을 넣을 수 있었다.
밑에 보면 chart.js 렌더링 되는 과정이 있다.
dataset이 렌더링 되기전에 beforeDraw를 사용해 도넛차트 가운데 글자를 먼저 렌더링 시켜줬다.
https://www.chartjs.org/docs/latest/developers/plugins.html
Plugins | Chart.js
Plugins Plugins are the most efficient way to customize or change the default behavior of a chart. They have been introduced at version 2.1.0 (opens new window) (global plugins only) and extended at version 2.5.0 (opens new window) (per chart plugins and o
www.chartjs.org
도넛 차트 가운데 이미지도 넣을 수 있는 방법이 있다.
밑에 링크 들어가서 코드블럭에 plugin을 참고하면된다.
https://www.chartjs.org/docs/latest/configuration/canvas-background.html#image
Canvas background | Chart.js
Canvas background In some use cases you would want a background image or color over the whole canvas. There is no built-in support for this, the way you can achieve this is by writing a custom plugin. In the two example plugins underneath here you can see
www.chartjs.org
지금이야 구현해서 쉬워보이지만 chart.js vue wrapper 사용하면서 config하는 부분이 조금 달라서 어떻게 해야 beforeDraw를 사용할 수 있는지에 대해 고민을 많이 했고 삽질 많이 했다.
5. typescript options type error
타입스크립트 사용한 프로젝트에서 options 타입 에러가 생겼다.
옵션 잘 적었고, 적용도 잘 되는데 이런 에러가 생겼었다.
= options에 ChartOpritons 타입 붙여줬더니 해결 됐다. 🙌
import { ChartOptions } from 'chart.js';
const options: ChartOptions = { ...옵션들}
전부터 블로그에 글 써야지 써야지 했고, 모아둔 글감들 많은데 일정이랑 귀차니즘에 밀려서 안쓰다 맘먹고 다시 쓰게 됐다.
가장 먼저 써야겠다고 생각든건 chart.js 였고 드디어 쓰게 됐다.
아직도 많은 옵션들과 다양한 메서드들에 대해서 모르지만 이제 필요한건 조금 찾아볼 수 있게 됐다.
프로젝트하면서 차트 처음 접해봤는데 api로 받는 동적 데이터인데 렌더링이 먼저 돼 차트가 안보이거나(이건 v-if사용해서 데이터 받고 렌더링 해주시면 돼요 loadData=ref(false) 이렇게! 추가로 글 작성하기에 남은 기력이 없어서ㅜㅜ 자식 컴포넌트가 부모 컴포넌트보다 mouted가 먼저돼서 그럽니다.) 반응형 도넛 차트 가운데 글자 넣기, 툴팁 커스텀등 여러모로 까다로웠지만 해결될 때 뿌듯했다.
혹시라도 나처럼 빙빙 돌고 있는 분이 있을까 + 내가 나중에 까먹을까봐 시간이 걸린것들 정리해봤다.
도움이 미래의 나든 정보가 필요하신 분이든 되면 너무 좋을 것 같다.🧞♂️
추가로 오류나 잘못된 정보 있다면 알려주세요~!