[React] Chart.js 라이브러리로 도넛차트 구현

강개미·2024년 7월 12일

React

목록 보기
1/2
post-thumbnail

✍ 도입

동료 간 익명의 피드백을 주고 받을 수 있는 웹사이트를 개발하면서, 사용자가 피드백 결과를 한 눈에 파악할 수 있도록 시각화하기로 하였다. 관련 라이브러리를 찾아 보던 중, Chart.js를 알게 되었으며 이를 이용하여 도넛 차트를 구현하였다.

🙋‍♂️ 왜 Chart.js인가

Chart.js는 "Area Chart", "Bar Chart", "Bubble Chart" 등 다양한 차트 타입을 제공하고 있다. 도넛 차트를 구현하던 중 여러 이유로 다른 차트로 변경하게 되더라도 굳이 새로운 라이브러리를 설치할 필요없이 Chart.js의 또다른 타입을 사용할 수 있다. 또한, Chart.js의 기본 UI가 더없이 깔끔하고, Dataset Properties를 통해 스타일 속성을 자유롭게 변경할 수 있다는 게 마음에 들었다.

✨ 구현

동료가 사용자에게 피드백을 남길 때, 15개의 태그 중 5개의 태그를 선택하는 질문이 있다. 사용자가 수많은 피드백을 받았을 때, 가장 많은 선택을 받은 5개의 태그명과 차지하는 비율을 시각화하기 위해 도넛차트를 구현하였다.

데이터 설정

const top5ChartData = {
    labels: data.slice(0, 5).map((item) => item.tag),
    datasets: [
      {
        data: data.slice(0, 5).map((item) => item.percentage),
        backgroundColor: [
          "#D5FBE5",
          "#F9C7C7",
          "#F6EED4",
          "#E2E9FF",
          "#EDD0F5",
        ],
        borderColor: ["#D5FBE5", "#F9C7C7", "#F6EED4", "#E2E9FF", "#EDD0F5"],
      },
    ],
  };

먼저 차트에 넣을 데이터 값을 labelsdatasets 속성을 통해 설정한다.

  • labels : DB에서 받아온 데이터 중 상위 5개의 데이터명을 라벨로 지정하였다.
  • datasets : 상위 5개의 데이터가 차지하는 비율을 datasetdata로 설정하였다. backgroundColorborderColor는 차트에서 데이터가 차지하는 칸의 색상을 지정할 수 있기 때문에 원하는 색상코드를 차례대로 입력하면 된다.

옵션 설정

const options = {
    plugins: {
      legend: {
        display: true,
        labels: {
          fontSize: 10,
          boxWidth: 10,
          boxHeight: 10,
          color: "black",
          font: {
            family: "Pretendard",
          },
        },
      },
      tooltip: {
        enabled: false,
      },
      datalabels: {
        color: "black",
        font: {
          family: "Pretendard",
        },
        formatter: function (value: number, context: any) {
          const dataLabel = top5ChartData.labels[context.dataIndex];
          return `${dataLabel}\n${value}%`;
        },
      },
    },
  };

Chart.js는 다양한 plugins을 제공하고 있기 때문에 각 속성값들을 변경하여 원하는 형태로 차트를 구현할 수 있다. 위 이미지는 plugins을 통해 구현한 도넛 차트다.

  • legend : 범례 속성으로, labels 속성을 이용하여 범례의 크기 및 색상, 폰트를 설정할 수 있다. 만약 범례가 필요하지 않다면 display 속성을 false로 변경하면 된다.
  • tooltip : 툴팁 속성으로, 차트의 특정 데이터를 선택하면 화면에 해당 데이터의 도움말이 뜬다. Chart.js는 툴팁이 디폴트로 설정되어있지만, 구현 과정에서 툴팁이 굳이 필요하지 않았기 때문에 enalbedfalse로 설정하였다. 만약 툴팁이 필요하다면 enabled를 건드리지 않고 legend처럼 스타일을 설정하면 된다.
  • datalables : 데이터 라벨 속성으로, 색상, 폰트 뿐만 아니라 formatter를 통해 데이터 라벨의 형식도 지정할 수 있다.

return

<Doughnut data={top5ChartData} options={options}></Doughnut>

마지막으로 return문에 위 코드를 넣으면 차트 구현이 성공적으로 이루어진다!

🎈 전체 코드

import { Chart as ChartJS, ArcElement, Tooltip, Legend } from "chart.js";
import { Doughnut } from "react-chartjs-2";
import ChartDataLabels from "chartjs-plugin-datalabels";

ChartJS.register(ArcElement, Tooltip, Legend, ChartDataLabels);

const SkillChart = ({
  data,
}: {
  data: { tag: string; percentage: number }[];
}) => {
  const top5ChartData = {
    labels: data.slice(0, 5).map((item) => item.tag),
    datasets: [
      {
        data: data.slice(0, 5).map((item) => item.percentage),
        backgroundColor: [
          "#D5FBE5",
          "#F9C7C7",
          "#F6EED4",
          "#E2E9FF",
          "#EDD0F5",
        ],
        borderColor: ["#D5FBE5", "#F9C7C7", "#F6EED4", "#E2E9FF", "#EDD0F5"],
      },
    ],
  };

  const options = {
    plugins: {
      legend: {
        display: true,
        labels: {
          fontSize: 10,
          boxWidth: 10,
          boxHeight: 10,
          color: "black",
          font: {
            family: "Pretendard",
          },
        },
      },
      tooltip: {
        enabled: false,
      },
      datalabels: {
        color: "black",
        font: {
          family: "Pretendard",
        },
        formatter: function (value: number, context: any) {
          const dataLabel = top5ChartData.labels[context.dataIndex];
          return `${dataLabel}\n${value}%`;
        },
      },
    },
  };

  const generateSentence = () => {
    const [first, second, third] = top5ChartData.labels.slice(0, 3);
    const sentence = `당신은 ${first}, ${second}, ${third}있는 사람이네요`;
    return sentence;
  };

  return (
    <div className="flex flex-col justify-center w-full gap-4">
      <p className="font-pre text-[16px] font-bold">{generateSentence()}</p>
      <Doughnut data={top5ChartData} options={options}></Doughnut>{" "}
    </div>
  );
};

function Chart() {
  return (
    <SkillChart data={workData} />
  );
}
profile
프론트엔드 개발자

0개의 댓글