[ChartJS / react-chartjs-2] options 를 알아보기 #3

김하정·2024년 1월 2일
7
post-thumbnail

이번에는 ChartJS 의 options 를 활용하여 차트를 이쁘게 꾸며보겠다.

먼저 options 에 대한 설명을 주석으로 달아보겠다.

👩🏼‍🏫 기본 옵션

const options = {
    // 기본 설정
    spanGaps: true, // 데이터가 중간중간 없을 때 이어 그릴지의 여부
    grouped: true, // x축 값이 같은 것끼리 묶일지의 여부 (주로 Line Chart에서)
    responsive: true, // canvas의 반응형 여부
    maintainAspectRatio: false, // 캔버스의 가로세로 비율 유지 여부
    
    // 차트 크기 설정
    aspectRatio: 2, // maintainAspectRatio가 true일 때 사용되는 비율
    
    // 애니메이션 설정
    animation: {
      duration: 1000, // 애니메이션 지속 시간 (밀리초)
      easing: 'easeInOutQuart', // 이징 함수 설정
      delay: (context) => context.dataIndex * 100, // 각 데이터 포인트별 딜레이
      mode: 'linear', // 애니메이션 모드
    },

    // 인터랙션 설정
    interaction: {
      mode: 'index' as const, // 같은 선상(index)에 있는 값들 툴팁 다 보여줌
      // mode: 'point' as const, // 특정 지점에 마우스를 호버하였을 때, 해당 툴팁 보여줌
      axis: 'x' as const, // mode가 index일 때, 같은 선상이 x축인지 y축인지
      intersect: false, // 교차 지점에서만 상호작용할지 여부
    },

    // 호버 설정
    hover: {
      mode: 'nearest',
      intersect: true,
      animationDuration: 400,
    },

    plugins: {
      // 범례(Legend) 설정
      legend: {
        display: true, // legend 표시 여부
        position: 'top' as const, // 'top'|'left'|'bottom'|'right'
        align: 'center' as const, // 'start'|'center'|'end'
        labels: {
          padding: 10,
          boxWidth: 13,
          color: '#000',
          usePointStyle: true, // 포인트 스타일 사용 여부
          pointStyle: 'circle', // circle, cross, dash, line, rect, star, triangle
          font: {
            family: 'Noto Sans KR',
            size: 12,
            lineHeight: 2,
            weight: 'normal',
          },
          generateLabels: (chart) => {
            // 커스텀 레이블 생성 로직
            return [];
          },
        },
        onClick: (e, legendItem, legend) => {
          // 범례 클릭 이벤트 핸들러
        },
      },

      // 툴팁 설정
      tooltip: {
        enabled: true, // 툴팁 표시 여부
        reverse: true, // 반대로 보일건지 여부
        padding: 20,
        caretPadding: 10,
        backgroundColor: 'rgba(0, 0, 0, 0.8)',
        titleColor: '#fff',
        bodyColor: '#fff',
        borderColor: 'rgba(0, 0, 0, 0.1)',
        borderWidth: 1,
        font: {
          family: "'Noto Sans KR', sans-serif" as const,
          size: 14,
        },
        titleMarginBottom: 10,
        titleSpacing: 10,
        bodySpacing: 10,
        filter: (item: { parsed: any }) => item.parsed.y !== null,
        // 툴팁에 보여질 데이터를 필터링해줌. 위 코드는 null인 값은 보이지 않게 함
        usePointStyle: false,
        callbacks: {
          beforeTitle: (tooltipItems) => {
            // 타이틀 이전에 표시될 내용
            return '';
          },
          title: (tooltipItems) => {
            // 타이틀 커스터마이징
            return tooltipItems[0].label;
          },
          label: (context: any) => {
            if (context.parsed.y === 0) {
              return ' ' + context.dataset.label + ' -건';
            }
            return ' ' + context.dataset.label + ' ' + context.parsed.y + '건';
          },
          afterLabel: (tooltipItem) => {
            // 라벨 이후에 표시될 내용
            return '';
          },
        },
        events: ['click'], // 이벤트 발생 여부에 대해서 컨트롤 가능
      },

      // 타이틀 설정
      title: {
        display: true,
        text: '차트 제목',
        position: 'top', // 'top', 'bottom', 'left', 'right'
        align: 'center', // 'start', 'center', 'end'
        color: '#666',
        font: {
          family: 'Noto Sans KR',
          size: 16,
          weight: 'bold',
        },
        padding: {
          top: 10,
          bottom: 10,
        },
      },
    },

    // 스케일 설정
    scales: {
      x: {
        axis: 'x',
        display: true, // 축 표시 여부
        position: 'bottom', // 축의 위치
        offset: true, // 오프셋 적용 여부
        grid: {
          display: true, // 그리드 표시 여부
          color: '#ddd', // 그리드 색상
          lineWidth: 1, // 그리드 굵기
          drawBorder: true, // 테두리 그리기
          drawOnChartArea: true, // 차트 영역에 그리기
          drawTicks: true, // 틱 마크 그리기
          borderDash: [], // 점선 설정
          borderDashOffset: 0,
        },
        afterDataLimits: (scale: { max: number }) => {
          scale.max = scale.max * 1;
          // 정해진 범위만큼 보일건지, 그 이상으로 넓혀지게 보일건지
        },
        title: {
          display: false,
          align: 'end' as const,
          color: '#808080',
          font: {
            size: 12,
            family: "'Noto Sans KR', sans-serif",
            weight: 300,
          },
          text: '건수',
          padding: {
            top: 10,
            bottom: 10,
          },
        },
        ticks: {
          display: true, // 틱 표시 여부
          color: '#808080',
          padding: 8,
          stepSize: 1, // 틱 간격
          maxRotation: 45, // 최대 회전 각도
          minRotation: 0, // 최소 회전 각도
          autoSkip: true, // 자동 건너뛰기
          autoSkipPadding: 10, // 자동 건너뛰기 패딩
          callback: (value) => {
            // 틱 라벨 커스터마이징
            return value;
          },
        },
      },
      y: {
        // y축은 x축과 동일한 옵션을 사용할 수 있음
        beginAtZero: true, // 0부터 시작할지 여부
        min: 0, // 최소값
        max: 100, // 최대값
        ticks: {
          stepSize: 20, // 눈금 간격
          callback: (value) => value + '%', // 단위 표시
        },
      },
    },

    // 레이아웃 설정
    layout: {
      padding: {
        left: 10,
        right: 10,
        top: 10,
        bottom: 10,
      },
    },

    // 데이터셋별 설정
    elements: {
      point: {
        radius: 4, // 포인트 크기
        hoverRadius: 6, // 호버 시 포인트 크기
        borderWidth: 2,
        backgroundColor: '#fff',
      },
      line: {
        tension: 0.4, // 선의 곡률
        borderWidth: 2,
        fill: false, // 선 아래 영역 채우기
      },
      bar: {
        borderWidth: 1,
        borderRadius: 4,
        borderSkipped: false,
      },
    },
};

위의 옵션들을 활용하여 실제로 차트를 구현해보면 아래와 같이 사용할 수 있다.

import { Line } from 'react-chartjs-2';

const MyChart = () => {
  const data = {
    labels: ['1월', '2월', '3월', '4월', '5월'],
    datasets: [
      {
        label: '매출',
        data: [10, 20, 30, 40, 50],
        borderColor: '#36A2EB',
        backgroundColor: 'rgba(54, 162, 235, 0.2)',
      },
    ],
  };

  return <Line options={options} data={data} />;
};

export default MyChart;

👩🏼‍🏫 자주 사용하는 옵션 조합 예시

1. 심플한 라인 차트

const simpleLineOptions = {
  responsive: true,
  plugins: {
    legend: {
      display: false,
    },
    tooltip: {
      enabled: true,
      mode: 'index',
      intersect: false,
    },
  },
  scales: {
    x: {
      grid: {
        display: false,
      },
    },
    y: {
      beginAtZero: true,
      grid: {
        color: '#f0f0f0',
      },
    },
  },
};

2. 대시보드용 바 차트

const dashboardBarOptions = {
  responsive: true,
  maintainAspectRatio: false,
  plugins: {
    legend: {
      position: 'bottom',
      labels: {
        usePointStyle: true,
        padding: 20,
      },
    },
    tooltip: {
      backgroundColor: 'rgba(0, 0, 0, 0.8)',
      padding: 12,
      titleFont: {
        size: 14,
      },
      bodyFont: {
        size: 13,
      },
    },
  },
  scales: {
    x: {
      grid: {
        display: false,
      },
    },
    y: {
      grid: {
        borderDash: [2, 4],
      },
    },
  },
};

3. 리포트용 복합 차트

const reportOptions = {
  responsive: true,
  interaction: {
    mode: 'index',
    intersect: false,
  },
  plugins: {
    title: {
      display: true,
      text: '월별 실적 리포트',
      font: {
        size: 16,
        weight: 'bold',
      },
    },
    legend: {
      position: 'bottom',
      labels: {
        padding: 20,
        usePointStyle: true,
      },
    },
  },
  scales: {
    x: {
      ticks: {
        maxRotation: 45,
        minRotation: 45,
      },
    },
    y: {
      beginAtZero: true,
      ticks: {
        callback: (value) => value.toLocaleString() + '원',
      },
    },
  },
};

이렇게 ChartJS의 다양한 옵션들을 활용하면, 목적에 맞는 차트를 더욱 완성도 있게 구현할 수 있다. 특히 plugins와 scales 옵션을 잘 활용하면 차트의 시각적 요소와 사용자 경험을 크게 개선할 수 있다는 점을 기억하자.

profile
web developer

0개의 댓글