본문 바로가기

React

React에서 데이터 시각화를 위한 Recharts 라이브러리 활용

Recharts는 React 컴포넌트 기반으로 선언적 차트를 쉽게 구성할 수 있는 라이브러리입니다. 실무에서는 대시보드, 분석 리포트, 운영 모니터링 등에서 빠르게 안정적인 차트를 구현할 수 있어 생산성이 높습니다. 본 글에서는 설치부터 기본 차트, 커스텀 Tooltip, 반응형, 접근성(AEO), 성능/SSR 이슈까지 바로 적용 가능한 패턴을 다룹니다.

1. 설치 및 기본 준비

프로젝트에 Recharts를 설치합니다. Vite/CRA/Next.js 모두 동일하게 사용할 수 있습니다.

// 설치
npm install recharts
// 또는
yarn add recharts

차트에 사용할 최소한의 데이터 구조 예시입니다. 키 이름은 dataKey로 사용할 예정입니다.

// sampleData.js
export const data = [
  { date: '2024-06-01', users: 120, sales: 3400, conversion: 2.1 },
  { date: '2024-06-08', users: 180, sales: 4200, conversion: 2.3 },
  { date: '2024-06-15', users: 210, sales: 4800, conversion: 2.5 },
  { date: '2024-06-22', users: 260, sales: 5300, conversion: 2.8 },
  { date: '2024-06-29', users: 300, sales: 6000, conversion: 3.0 },
];

2. 가장 빠른 기본 LineChart

라인 차트는 추세 파악에 적합합니다. 모노톤 라인, 그리드, 툴팁, 범례를 기본으로 추가합니다.

// SalesLineChart.jsx
import { ResponsiveContainer, LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend } from 'recharts';
import { data } from './sampleData';

export default function SalesLineChart() {
  const formatXAxis = (v) => v.slice(5); // 'MM-DD'만 표시
  const formatYAxis = (v) => `${v.toLocaleString()}원`;

  return (
    <div role="img" aria-label="6월 주차별 매출 추이를 보여주는 선형 차트">
      <ResponsiveContainer width="100%" height={300}>
        <LineChart data={data} margin={{ top: 16, right: 24, left: 8, bottom: 8 }}>
          <CartesianGrid strokeDasharray="3 3" />
          <XAxis dataKey="date" tickFormatter={formatXAxis} />
          <YAxis tickFormatter={formatYAxis} />
          <Tooltip formatter={(value) => value.toLocaleString()} labelFormatter={(label) => `날짜: ${label}`} />
          <Legend />
          <Line type="monotone" dataKey="sales" stroke="#8884d8" strokeWidth={2} dot={false} name="매출" />
        </LineChart>
      </ResponsiveContainer>
    </div>
  );
}

3. BarChart와 ResponsiveContainer로 안정적 반응형

막대 차트는 범주 비교에 좋습니다. 부모 컨테이너의 폭에 맞춰 자동으로 크기가 조정되도록 ResponsiveContainer를 항상 감싸는 습관이 실무에서 유용합니다.

// UsersBarChart.jsx
import { ResponsiveContainer, BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip } from 'recharts';
import { data } from './sampleData';

export default function UsersBarChart() {
  return (
    <div role="img" aria-label="주차별 활성 사용자 수를 보여주는 막대 차트">
      <ResponsiveContainer width="100%" height={280}>
        <BarChart data={data} margin={{ top: 16, right: 16, left: 8, bottom: 8 }}>
          <CartesianGrid strokeDasharray="3 3" />
          <XAxis dataKey="date" tickFormatter={(v) => v.slice(5)} />
          <YAxis />
          <Tooltip />
          <Bar dataKey="users" fill="#82ca9d" name="사용자" />
        </BarChart>
      </ResponsiveContainer>
    </div>
  );
}

4. 커스텀 Tooltip/Legend/Axis로 읽기성 향상

툴팁 구성 요소를 커스터마이징하면 숫자 포맷, 단위, 강조 스타일을 쉽게 조정할 수 있습니다.

// CustomTooltip.jsx
export function CustomTooltip({ active, payload, label }) {
  if (!active || !payload || payload.length === 0) return null;
  const items = payload.map((p) => ({ name: p.name, value: p.value, color: p.color }));
  return (
    <div style={{ background: '#111', color: '#fff', padding: '8px 12px', borderRadius: 8, boxShadow: '0 4px 12px rgba(0,0,0,0.2)' }}>
      <div style={{ fontWeight: 600, marginBottom: 6 }}>{label}</div>
      {items.map((it) => (
        <div key={it.name} style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
          <span style={{ width: 10, height: 10, background: it.color, display: 'inline-block', borderRadius: 2 }} />
          <span>{it.name}: {Number(it.value).toLocaleString()}</span>
        </div>
      ))}
    </div>
  );
}

// 사용 예시
// <Tooltip content=<CustomTooltip /> />

Axis tickFormatter로 날짜, 통화, 퍼센트 등을 일관되게 표시하세요.

<XAxis dataKey="date" tickFormatter={(v) => v.slice(5)} />
<YAxis tickFormatter={(v) => `${v.toLocaleString()}원`} />

5. ComposedChart로 복합 지표 한 화면에

매출, 사용자, 전환율처럼 서로 다른 척도를 한 화면에서 비교할 때 ComposedChart가 유용합니다.

// KPIComposedChart.jsx
import { ResponsiveContainer, ComposedChart, Area, Bar, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ReferenceLine } from 'recharts';
import { data } from './sampleData';

export default function KPIComposedChart() {
  return (
    <div role="img" aria-label="매출, 사용자, 전환율을 동시에 표시하는 복합 차트">
      <ResponsiveContainer width="100%" height={320}>
        <ComposedChart data={data} margin={{ top: 16, right: 24, left: 8, bottom: 8 }}>
          <CartesianGrid strokeDasharray="3 3" />
          <XAxis dataKey="date" tickFormatter={(v) => v.slice(5)} />
          <YAxis yAxisId="left" tickFormatter={(v) => `${v.toLocaleString()}원`} />
          <YAxis yAxisId="right" orientation="right" tickFormatter={(v) => `${v}%`} />
          <Tooltip />
          <Legend />
          <Area yAxisId="left" type="monotone" dataKey="sales" fill="#8884d8" stroke="#8884d8" name="매출" />
          <Bar yAxisId="left" dataKey="users" fill="#82ca9d" name="사용자" />
          <Line yAxisId="right" type="monotone" dataKey="conversion" stroke="#ff7300" name="전환율" />
          <ReferenceLine yAxisId="right" y={2.5} stroke="#ff7300" strokeDasharray="4 2" label="목표 2.5%" />
        </ComposedChart>
      </ResponsiveContainer>
    </div>
  );
}

6. Next.js/SSR 환경에서의 안전한 로딩

Recharts는 브라우저 DOM에 의존하므로 SSR에서 바로 렌더링하면 오류가 발생할 수 있습니다. Next.js에서는 동적 임포트로 처리하세요.

// pages/dashboard.jsx
import dynamic from 'next/dynamic';

const SalesLineChart = dynamic(() => import('../components/SalesLineChart'), { ssr: false });
const UsersBarChart = dynamic(() => import('../components/UsersBarChart'), { ssr: false });

export default function DashboardPage() {
  return (
    <div>
      <h2>대시보드</h2>
      <SalesLineChart />
      <UsersBarChart />
    </div>
  );
}

7. 접근성(AEO)과 SEO 실무 팁

차트는 시각 정보가 강하므로 대체 텍스트와 구조화된 설명을 제공하면 답변 엔진(AEO)과 접근성에 모두 유리합니다.

실무 체크포인트:

1) 차트 컨테이너에 role="img"와 의미 있는 aria-label을 제공합니다. 2) Tooltip 내용은 숫자 포맷과 단위를 명확히 표기합니다. 3) 주요 포인트에 ReferenceLine/Label을 활용해 문맥을 제공합니다. 4) 차트 하단에 간단한 요약 문장을 제공하면 검색/답변 엔진이 핵심을 이해하는 데 도움이 됩니다.

// 차트 아래 요약 텍스트 예시
// <p>6월 한 달 매출은 지속 증가했고 전환율은 목표 2.5%를 초과했습니다.</p>

8. 성능 최적화: 재랜더 최소화

대용량 데이터나 대시보드에서 차트가 많다면 다음을 적용합니다.

- 데이터 전처리는 useMemo로 메모이징합니다. - dataKey, color 등 props는 안정적인 참조를 유지합니다. - 애니메이션이 불필요하면 isAnimationActive={false}로 끕니다. - 수천 포인트라면 다운샘플링(예: 시간 단위 리샘플링)을 고려합니다.

import { useMemo } from 'react';
import { LineChart, Line } from 'recharts';

export default function FastChart({ raw }) {
  const data = useMemo(() => raw.map((r) => ({ date: r.date, sales: Number(r.sales) })), [raw]);
  return (
    <LineChart width={600} height={240} data={data}>
      <Line type="monotone" dataKey="sales" stroke="#8884d8" isAnimationActive={false} dot={false} />
    </LineChart>
  );
}

9. 흔한 오류와 빠른 디버깅

- window is not defined: SSR 환경에서 동적 임포트로 해결합니다. - 차트가 비어 보임: dataKey 오타 또는 데이터 타입(Number) 확인하세요. - 축 눈금 겹침: tickFormatter로 간결하게 표시하거나 interval="preserveStartEnd"를 적용합니다. - 반응형이 동작 안 함: 부모 컨테이너가 width/height를 갖도록 CSS를 확인하세요.

// 축 눈금 간격 유지 예시
<XAxis dataKey="date" interval="preserveStartEnd" />

10. 마무리와 실무 체크리스트

Recharts는 선언적 API로 빠른 구현, 커스텀 구성 요소로 높은 유연성, ResponsiveContainer로 쉬운 반응형을 제공합니다. 아래 체크리스트로 품질을 확보하세요.

- ResponsiveContainer로 감싸기 - 의미 있는 aria-label 제공 - Tooltip/Axis 포맷 일관화 - SSR 환경은 dynamic import 사용 - 대용량 데이터는 메모이징/다운샘플링

이제 프로젝트에 바로 적용해 대시보드의 가독성과 신뢰도를 높여보세요.