Recharts 차트 커스터마이징하기

짜장킴·2026년 7월 14일

실무

목록 보기
16/16
  • 시력(UDVA)은 Line Chart를 사용하고, 구면도수(SPH)와 난시도수(CYL)는 Bar Chart를 사용했지만 대부분의 설정은 공통으로 사용하였다.
<ResponsiveContainer width="100%" height="100%">
  <LineChart data={chartData}>
    ...
    
    <Line />
  </LineChart>
</ResponsiveContainer>

<ResponsiveContainer width="100%" height="100%">
  <BarChart data={chartData}>
    ...
    
    <Bar />
  </BarChart>
</ResponsiveContainer>
  • 실제로 차트 종류만 다를 뿐,
    - ResponsiveContainer
    - CartesianGrid
    - XAxis
    - YAxis
  • 설정은 동일하게 적용하였다.

ResponsiveContainer

  • 차트는 카드 크기에 따라 반응형으로 동작해야 했다.
  • 그래서 ResponsiveContainer를 사용하여 부모 요소 크기에 맞게 자동으로 렌더링되도록 구성했다.
<ResponsiveContainer width="100%" height="100%">

CartesianGrid

  • 차트의 가독성을 높이기 위해 배경 격자선을 추가하였다.
<CartesianGrid
  vertical={false}
  stroke="#E2E4E8"
/>
  • 세로선은 제거하고 가로선만 표시하였다.
  • 가로선만 남겨두면 값의 위치를 쉽게 파악할 수 있으면서도 화면이 복잡해 보이지 않는다.

XAxis

  • X축은 예측 시점을 표시한다.
<XAxis
  dataKey="label"
  tick={renderXAxisTick}
  tickLine={false}
  axisLine={false}
  padding={{ left: 60, right: 60 }}
/>
  • dataKey="label"을 통해(chartData의 label을 사용한다는 뜻)
    - 3일
    - 3주
    - 3개월
  • 값을 표시하였다.
 const chartData = [
    {
      label: "3일",
      LASIK: ClampLineValue(data.LASIK?.["3days"]),
      LASEK: ClampLineValue(data.LASEK?.["3days"]),
      SMILE: ClampLineValue(data.SMILE?.["3days"]),
      ICL: ClampLineValue(data.ICL?.["3days"]),
    },
    {
      label: "3주",
      LASIK: ClampLineValue(data.LASIK?.["3weeks"]),
      LASEK: ClampLineValue(data.LASEK?.["3weeks"]),
      SMILE: ClampLineValue(data.SMILE?.["3weeks"]),
      ICL: ClampLineValue(data.ICL?.["3weeks"]),
    },
    {
      label: "3개월",
      LASIK: ClampLineValue(data.LASIK?.["3months"]),
      LASEK: ClampLineValue(data.LASEK?.["3months"]),
      SMILE: ClampLineValue(data.SMILE?.["3months"]),
      ICL: ClampLineValue(data.ICL?.["3months"]),
    },
  ];
  • 기본 Tick 대신 커스텀 Tick을 사용하여 디자인 시스템에 맞는 폰트와 색상을 적용하였다.
  const renderXAxisTick = ({ x, y, payload }: TickProps) => (
    <text
      x={Number(x)}
      y={Number(y) + 24}
      textAnchor="middle"
      fill="#989DA8"
      fontSize={16}
      fontWeight={500}
      letterSpacing="-0.6px"
    >
      {payload?.value}
    </text>
  );

YAxis

  • Y축은 데이터 범위에 맞게 설정하였다.
  • 시력 차트의 경우 0~1.2 범위를 사용하였다.
<YAxis
   width={50}
   domain={[0, 1.2]}
   ticks={[0, 0.4, 0.8, 1.2]}
   tick={renderYAxisTick}
   tickLine={false}
   axisLine={false}
/>
  • 또한 모든 Tick Label(Y축 라벨)을 표시하는 대신 최소값과 최대값만 노출하였다.
  • 0.4, 0.8은 라벨 안 보이게 함
  const renderYAxisTick = ({ x, y, payload }: TickProps) => {
    const value = Number(payload?.value);

    if (value !== 0 && value !== 1.2) {
      return null;
    }
  • 중간 값은 가이드 라인 역할만 수행하도록 구성하였다.
  • 구면도수, 난시도수 차트의 경우
  const yAxisConfig = {
    sph: {
      domain: [-1.5, 1.5],
      ticks: [-1.5, 0, 1.5],
    },
    cyl: {
      domain: [-1.5, 1.5],
      ticks: [-1.5, 0, 1.5],
    },
  }[chartType];
  
  const renderYAxisTick = ({ x, y, payload }: TickProps) => {
    const value = Number(payload?.value);

    return (
      <text
        x={Number(x) - 5}
        y={Number(y)}
        dy={value === -1.5 ? 2 : value === 0 ? 2 : -4}
        dx={value === -1.5 ? -1 : 0}
        textAnchor="end"
        dominantBaseline="middle"
        fill="#989DA8"
        fontSize={16}
        fontWeight={500}
        letterSpacing="-0.6px"
      >
        {value.toFixed(2)}
      </text>
    );
  };

차트 종류에 따른 렌더링

  • 공통 설정 이후 실제 데이터 표현 방식만 달라진다.

Line Chart

  • 시력(UDVA)은 시간에 따른 변화 추이를 확인하는 것이 중요했다.
  • 그래서 Line Chart를 사용하였다.
<Line
  dataKey={type}
  stroke={chip.color}
  strokeWidth={3}
  dot={false}
  activeDot={false}
  isAnimationActive={false}
/>
  • 수술 방식별 회복 추세 표현
  • 점 제거
  • Hover 제거
  • PDF 출력을 위한 애니메이션 제거
  • Bar Chart

Bar Chart

  • 구면도수(SPH)와 난시도수(CYL)는 변화 추이보다 수치 비교가 중요했다.
  • 그래서 동일한 데이터 구조를 사용하면서 렌더링 방식만 Bar Chart로 변경하였다.
<Bar
    key={type}
    dataKey={type}
    fill={chip.color}
    radius={[2, 2, 0, 0]}
    isAnimationActive={false}
    barSize={12}
/>
profile
프론트엔드

0개의 댓글