본문 바로가기
Frontend/React

React 시각화/차트 라이브러리 추천

by 신림쥐 2024. 5. 17.
728x90
반응형

 

     


     

    데이터 시각화란, 데이터를 선으로 연결하여 시각적으로 표현하는 것을 말한다.
    대표적으로 차트. 그래프가 있다.

     

    개요

    이 글에서는 HTML5 Canvas 기반 렌더링에서 발생할 수 있는 성능 병목 문제와 이를 해결하기 위한 대표적인 최적화 전략을 소개합니다.
    이와 함께, 복잡한 데이터 시각화 작업에는 왜 차트 라이브러리 사용이 유리한지 설명하고, 실무에서 자주 사용되는 주요 차트 라이브러리들의 종류와 특징을 비교합니다.

     

     

    Canvas 렌더링

    Canvas 렌더링은 HTML5의 <canvas> 요소를 사용해 자바스크립트로 픽셀 단위의 그래픽을 그리는 방식입니다.
    주로 게임, 애니메이션, 실시간 시각화 등에 사용되며, 도형, 이미지, 텍스트 등을 자유롭게 조합할 수 있는 고성능 2D 그래픽 처리 도구입니다.

    브라우저가 제공하는 canvas.getContext('2d') API를 통해 fillRect, arc, drawImage 같은 메서드로 화면에 직접 그림을 그리게 됩니다.

     <canvas id="myCanvas" width="400" height="300" style="border:1px solid #000;"></canvas>
     <script>
        const canvas = document.getElementById('myCanvas');
        const ctx = canvas.getContext('2d');
    
        // 사각형 그리기
        ctx.fillStyle = 'skyblue';
        ctx.fillRect(20, 20, 100, 80);
      </script>

     

    Canvas 렌더링 한계

    캔버스 렌더링에서는 beginPath, arc, fill, stroke 등의 메서드와 save, restore 명령어가 반복적으로 호출됩니다.

    이러한 작업은 메인 스레드에서 실행되므로, 프레임당 수천~수십만 번 호출될 경우 성능 저하가 발생할 수 있습니다.

    따라서 데이터가 많아질수록 병목 현상이 심해지며, 이를 방지하기 위한 대안이 등장했습니다.

     

     

    렌더링 병목 현상 방지를 위한 주요 대안

    1. 오프스크린 캔버스 (OffscreenCanvas) 사용

    메인 스레드가 아닌 웹 워커(Worker)에서 캔버스를 렌더링하여, 메인 UI의 응답성 향상에 효과적입니다.

    2. 레이어별 캔버스 분리

    정적인 배경, 인터랙티브 요소 등 각 요소를 캔버스 레이어로 분리하면 전체 재렌더링을 피할 수 있습니다.

    3. requestAnimationFrame으로 렌더링 최적화

    브라우저의 리렌더링 타이밍에 맞춰 작업을 실행하므로, 프레임 드랍 없이 부드러운 애니메이션을 제공하게 도와줍니다.

    4. 비트맵 캐싱

    반복적으로 등장하는 객체는 Canvas에서 미리 렌더링해 이미지로 저장해두고, drawImage로 재사용하면 속도가 비약적으로 빨라집니다.

    5. 그리기 최소화 및 조건부 렌더링

    변경된 경우에만 조건부로 그리기 수행하게 합니다.

     

     

    왜 차트 라이브러리를 왜 사용하나요?

    Canvas나 SVG 기반으로 직접 시각화 로직을 구현하여 직접 Canvas 렌더링을 최적화하는 것도 중요하지만, 차트 같은 복잡한 시각화 작업은 검증된 라이브러리를 활용하는 것이 현명할 수 있습니다.

    보통 시각화데이터는 보통 SVG(Scalable Vector Graphics)와 Canvas를 사용하여 다양한 그래픽 요소를 만들지만 렌더링과 스타일링 등 아름답고 고성능의 차트를 만들고 싶다면 Charts 라이브러리를 사용하는 방법도 고려할 수 있습니다.

     

    728x90

     

    JavaScript 차트 라이브러리 추천

    1. chart.js

    https://www.chartjs.org/

    특징

    • 즉시 사용 가능한 차트
    • 뛰어난 구성
    • 선택적 구성 기능으로 인한 커스터마이징
    • 가장 사용자가 많다.

     

    예시

    <div>
      <canvas id="myChart"></canvas>
    </div>
    
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    
    <script>
      const ctx = document.getElementById('myChart');
    
      new Chart(ctx, {
        type: 'bar',
        data: {
          labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
          datasets: [{
            label: '# of Votes',
            data: [12, 19, 3, 5, 2, 3],
            borderWidth: 1
          }]
        },
        options: {
          scales: {
            y: {
              beginAtZero: true
            }
          }
        }
      });
    </script>

     

     

    React 차트 라이브러리 추천

    1. recharts

    • Recharts는 ReactD3 로 구축된 재정의된 차트 라이브러리
      • 장점
        • React 구성 요소를 사용하여 간단히 배포하세요.
        • 일부 D3 하위 모듈에만 의존하는 경량 기본 SVG 지원.
        • 선언적 구성 요소, 차트 구성 요소는 순전히 표현(조회성)
    • React Component를 명확히 구분해줌
      • lineChart는 x축, 툴팁, 그리드, 라인 항목 등

    https://recharts.org/en-US

    특징

    • 즉시 사용 가능한 차트
    • 뛰어난 구성
    • 선택적 구성 기능으로 인한 커스터마이징
    • 가장 사용자가 많다.

     

    예시

    import { LineChart, Line } from 'recharts';
    const data = [{name: 'Page A', uv: 400, pv: 2400, amt: 2400}, ...];
    
    const renderLineChart = (
      <LineChart width={400} height={400} data={data}>
        <Line type="monotone" dataKey="uv" stroke="#8884d8" />
      </LineChart>
    );

     

     

    2. nivo

    https://nivo.rocks/

     

     

    특징

    • 즉시 사용 가능한 차트
    • 커스텀이 어려움
    •  

    예시

    • @nivo/line
    • @nivo/bar
    import { ResponsiveLine } from '@nivo/line'
    
    const MyResponsiveLine = ({ data /* see data tab */ }) => (
        <ResponsiveLine
            data={data}
            margin={{ top: 50, right: 110, bottom: 50, left: 60 }}
            xScale={{ type: 'point' }}
            yScale={{
                type: 'linear',
                min: 'auto',
                max: 'auto',
                stacked: true,
                reverse: false
            }}
            yFormat=" >-.2f"
            axisTop={null}
            axisRight={null}
            axisBottom={{
                tickSize: 5,
                tickPadding: 5,
                tickRotation: 0,
                legend: 'transportation',
                legendOffset: 36,
                legendPosition: 'middle',
                truncateTickAt: 0
            }}
            axisLeft={{
                tickSize: 5,
                tickPadding: 5,
                tickRotation: 0,
                legend: 'count',
                legendOffset: -40,
                legendPosition: 'middle',
                truncateTickAt: 0
            }}
            pointSize={10}
            pointColor={{ theme: 'background' }}
            pointBorderWidth={2}
            pointBorderColor={{ from: 'serieColor' }}
            pointLabel="data.yFormatted"
            pointLabelYOffset={-12}
            enableTouchCrosshair={true}
            useMesh={true}
            legends={[
                {
                    anchor: 'bottom-right',
                    direction: 'column',
                    justify: false,
                    translateX: 100,
                    translateY: 0,
                    itemsSpacing: 0,
                    itemDirection: 'left-to-right',
                    itemWidth: 80,
                    itemHeight: 20,
                    itemOpacity: 0.75,
                    symbolSize: 12,
                    symbolShape: 'circle',
                    symbolBorderColor: 'rgba(0, 0, 0, .5)',
                    effects: [
                        {
                            on: 'hover',
                            style: {
                                itemBackground: 'rgba(0, 0, 0, .03)',
                                itemOpacity: 1
                            }
                        }
                    ]
                }
            ]}
        />
    )

     

     

    3. react-charts

    https://react-charts.tanstack.com/

     

    특징

    • 즉시 사용 가능한 차트
    • 커스텀이 어려움

    예시

    function App() {
       const primaryAxis = React.useMemo(
         (): AxisOptions<DailyStars> => ({
           getValue: datum => datum.date,
         }),
         []
       )
     
       const secondaryAxes = React.useMemo(
         (): AxisOptions<DailyStars>[] => [
           {
             getValue: datum => datum.stars,
           },
         ],
         []
       )
     
       return (
         <Chart
           options={{
             data,
             primaryAxis,
             secondaryAxes,
           }}
         />
       )
     }

     

     

    4. victory

    https://commerce.nearform.com/open-source/victory

     

    특징

    • 즉시 사용 가능한 차트
    • 커스텀이 어려움

    예시

    const data2012 = [
      {quarter: 1, earnings: 13000},
      {quarter: 2, earnings: 16500},
      {quarter: 3, earnings: 14250},
      {quarter: 4, earnings: 19000}
    ];
    
    const data2013 = [
      {quarter: 1, earnings: 15000},
      {quarter: 2, earnings: 12500},
      {quarter: 3, earnings: 19500},
      {quarter: 4, earnings: 13000}
    ];
    
    const data2014 = [
      {quarter: 1, earnings: 11500},
      {quarter: 2, earnings: 13250},
      {quarter: 3, earnings: 20000},
      {quarter: 4, earnings: 15500}
    ];
    
    const data2015 = [
      {quarter: 1, earnings: 18000},
      {quarter: 2, earnings: 13250},
      {quarter: 3, earnings: 15000},
      {quarter: 4, earnings: 12000}
    ];
    
    function App() {
      return (
        <VictoryChart
          domainPadding={20}
          theme={VictoryTheme.material}
        >
          <VictoryAxis
            tickValues={[1, 2, 3, 4]}
            tickFormat={["Quarter 1", "Quarter 2", "Quarter 3", "Quarter 4"]}
          />
          <VictoryAxis
            dependentAxis
            tickFormat={(x) => (`$${x / 1000}k`)}
          />
          <VictoryStack
            colorScale={"warm"}
          >
            <VictoryBar
              data={data2012}
              x="quarter"
              y="earnings"
            />
            <VictoryBar
              data={data2013}
              x="quarter"
              y="earnings"
            />
            <VictoryBar
              data={data2014}
              x="quarter"
              y="earnings"
            />
            <VictoryBar
              data={data2015}
              x="quarter"
              y="earnings"
            />
          </VictoryStack>
        </VictoryChart>
      )
    }
    
    render(<App/>);

     

     

    5. react-vis

    https://uber.github.io/react-vis/

    특징

    • 고수준 추상화보다 낮은 수준의 D3에 더 가까움
    • 가파른 학습 곡선

    예시

    const data = [
      {x: 0, y: 8},
      {x: 1, y: 5},
      {x: 2, y: 4},
      {x: 3, y: 9},
      {x: 4, y: 1},
      {x: 5, y: 7},
      {x: 6, y: 6},
      {x: 7, y: 3},
      {x: 8, y: 2},
      {x: 9, y: 0}
    ];
    
    ...
    
    <XYPlot height={200} width={200}>
      <VerticalBarSeries data={data} />
    </XYPlot>
    <XYPlot height={200} width={200}>
      <LineSeries data={data} />
    </XYPlot>
    <XYPlot height={200} width={200}>
      <MarkSeries data={data} />
    </XYPlot>

     

     

    6. AG Charts

    AG Charts는 AG-Grid에서 제공하는 차트 라이브러리입니다. AG Charts는 데이터를 시각적으로 표시할 수 있는 다양한 차트 컴포넌트를 제공하며, React뿐만 아니라 Angular, Vue와 같은 다른 프레임워크에서도 사용할 수 있습니다.

    https://www.ag-grid.com/charts/?ref=blog.ag-grid.com

     

     

    차트 라이브러리 비교

    • JavaScript
      chart.js D3.js HighCharts GoogleCharts
    특징
    • 다양한 차트 유형
    • 커스터마이징, 확장성 우수
    • 데이터 시각화가 뛰어 남
    • 시간과 노력이 필요
    • 다양한 차트 유형
    • 문서가 잘 되있음
    • 구글에서 제공하는 무료 차트 라이브러리
    • 다양한 차트 유형
    • 사용하기 쉬움
    라이센스 MIT 라이센스 MIT 라이센스, BSD 라이센스 상업용은 라이센스 구매가 필요
    • 개인 및 상업용 모두 가능
    • 구글의 서비스 이용 약관을 준수

     

    • React
      ReCharts Victorys Chart.js fo React HighCharts for React
    특징
    • React와 D3.js를 조합한 라이브러리
    • Airbnb에서 개발한 라이브러리
    • 리액트 컴포넌트로 차트를 만듦
    • Chart.js의 기능 + 리액트 애플리케이션 통합(react-chartjs-2)
    • Highcharts.js의 기능 + 리액트 애플리케이션 통합(highcharts-react-official)
    라이센스 MIT 라이센스
    라이센스와 저작권 표시를 유지
    MIT 라이센스
    라이센스와 저작권 표시를 유지
    MIT 라이센스
    라이센스와 저작권 표시를 유지
    상업용은 라이센스 구매가 필요

     

    728x90
    반응형