∙React

하이차트 (Highcharts) 사용하기

coor 2024. 8. 5. 21:03
 

 

1. Highcharts 개념과 사용


[ Highcharts 이란? ]

자바스크립트만으로 다양한 차트를 그려낼 수 있는 차트 라이브러리로, 웹 페이지에서 실시간으로 변경되는 다양한 차트를 그려줄 때 매우 유용한 라이브러리입니다.

출처 - https://www.highcharts.com/demo/line-basic

 

 

[ Highcharts 설치 ]

npm install highcharts

 

 

개발환경 : React 18, highcharts 6.0.4

[ Highcharts 생성 ]

import React, { Component } from "react";
import Highcharts from "highcharts";

const defaultOptions = {
  chart: { 
    width: 1560,  
    height: 250, 
  },
  xAxis: {
    crosshair: true,
    categories: [],
  },
  yAxis: {
    title: {
      text: null,
    },
    min:0
  },
  plotOptions: {
    line: {
      marker: {
        enabled: false,
      },
    },
  },
  series : [{
    data:[12,12,52,2]
  }],
  legend: {
    align: "right",
    verticalAlign: "top",
    layout: "vertical",
    x: 0,
    y: 100,
  },
};

export default class ChartComponent extends Component {
  componentDidMount() {
    this.chart = Highcharts.chart(this.container, defaultOptions);
  }

  render() {
    return <div ref={(container) => (this.container = container)}></div>;
  }
}

import를 통해 Highcharts 라이브러리를 가져옵니다. 차트의 기본 설정을 정의하기 위해 defaultOptions 객체에 담아두고 componentDidMount 라이프사이클 메서드에서 Highcharts를 초기화하여 Highcharts.chart() 함수를 통해 차트를 생성합니다. 차트의 각 설정은 다음과 같습니다. 자세한 설명은 공식 문서에서 확인 가능합니다.

  • chart: 차트의 전반적인 크기를 정의
  • legend: 차트의 범례 설정을 정의
    • layout: 범례 항목의 레이아웃을 설정
    • maxHeight: 범례의 최대 높이를 설정
    • borderWidth: 범례의 테두리 두께를 설정
    • backgroundColor: 범례의 배경색을 설정
    • align: 범례의 가로 정렬을 설정
    • verticalAlign: 범례의 세로 정렬을 설정
    • x와 y: 범례의 위치를 설정
  • xAxis: x축의 설정을 정의
    • crosshair: x축의 크로스헤어를 활성화
    • categories: x축의 카테고리를 설정
  • yAxis: y축의 설정을 정의
    • title: y축의 제목을 설정합니다. 여기서는 제목이 없도록 설정함
    • min: y축의 최솟값을 설정
  • plotOptions: 각 시리즈 유형에 대한 세부 설정을 정의
    • line: 선형 차트에 대한 설정을 정의
  • series: 차트에 표시될 데이터 시리즈를 정의
    • data: 시리즈에 대한 데이터 배열을 설정합니다.



 

지금까지 기본적인 사용법에 대해 알아보았습니다.
이제 차트를 통해 서버 모니터링 시스템에 적용시켜보겠습니다.
요구사항은 다음과 같습니다.

  1. 시간별로 서버 응답 시간과 오류율 차트 그리기
  2. 차트 유형 line -> bar 변경






1.1 시간별로 서버 응답 시간과 오류율 차트 그리기


일단 서버 응답 시간과 오류율 차트를 그리기 전에, 차트 컴포넌트와 모니터링 컴포넌트를 분리해야 합니다. 이유는 차트를 그릴 때 기본 설정, 데이터, 인터페이스 관리, 렌더링이 필요한 데 하나의 컴포넌트 안에서 모든 역할을 수행하면 중복 코드와 유지보수성이 떨어집니다. 그래서 요구사항을 시작하기 전에 차트 컴포넌트 만들고 시작하겠습니다.

 

[ 차트 컴포넌트 만들기 ]

먼저 기본 설정과 렌더링을 담당하는 ChartComponent 만들어보겠습니다.

ChartComponent.js

import React, { Component } from "react";
import Highcharts from "highcharts";

// 기본 옵션 설정
const defaultOptions = {
  chart: {
    width: 1560,
    height: 250,
  },
  credits: {
    enabled: false,
  },
  legend: {
    layout: "horizontal",
    maxHeight: 40,
    borderWidth: 1,
    backgroundColor: "#FFFFFF",
  },
  yAxis: {
    title: {
      text: null,
    },
  },
  tooltip: {
    shared: true,
  },
  plotOptions: {
    line: {
      marker: {
        enabled: false,
      },
    },
  },
  legend: {
    align: "right",
    verticalAlign: "top",
    layout: "vertical",
    x: 0,
    y: 100,
  },
};

export default class ChartComponent extends Component {
  // option 병합
  componentDidMount() {
    const options = Highcharts.merge(defaultOptions, this.props.option);
    this.chart = Highcharts.chart(this.container, options);
  }

  // 차트 인스턴스 제거
  componentWillUnmount() {
    this.chart.destroy();
  }

  // 차트 업데이트
  componentDidUpdate(prevProps) {
    if (this.props.option !== prevProps.option) {
      this.chart.update(this.props.option);
    }
  }

  render() {
    return <div ref={(container) => (this.container = container)}></div>;
  }
}

ChartComponent는 Highcharts 차트를 렌더링 하는 기본 컴포넌트입니다. 이 컴포넌트는 다음과 같은 역할을 합니다.

  • defaultOptions 기본 옵션 설정: 모든 차트에 공통적으로 적용함. 중복 코드 방지(차트의 크기, 레이아웃, 범례 설정 등등)
  • option 병합: componentDidMount 메서드에서 defaultOptions와 this.props.option을 병합하여 추가설정을 쉽게 적용
  • 차트 업데이트: componentDidUpdate 메서드를 통해 차트 옵션이 변경될 때마다 업데이트를 처리

 

이제 기본적인 뼈대인 ChartComponent 만들어졌으니, 서버 응답 시간과 오류율 차트를 그려보겠습니다.

const responseTimeOptions = {
  chart: { type: "line" },
  title: {
    text: "Server Response Time(ms)",
    align: "left",
    style: {
      fontSize: "12px",
    },
  },
  xAxis: {
    categories: Array.from({ length: 13 }, (_, i) => `${i * 2}:00`),
    crosshair: true,
  },
  plotOptions: {
    line: { marker: { enabled: false } },
  },
  series: [
    { name: "Server 1", data: [50, 40, 36, 52, 50, 22, 42, 29, 19, 28, 50, 20, 100],},
    { name: "Server 2", data: [40, 50, 20, 36, 29, 52, 22, 20, 28, 28, 50, 42, 19],},
    { name: "Server 3", data: [40, 70, 50, 42, 28, 36, 19, 20, 29, 52, 22, 50, 20],},
    { name: "Server 4", data: [50, 42, 40, 50, 19, 50, 36, 29, 52, 22, 60, 50, 40],},
  ],
};

const errorRateOptions = {
  chart: { type: "line" },
  title: {
    text: "Server Error Rate(%)",
    align: "left",
    style: {
      fontSize: "12px",
    },
  },
  xAxis: {
    categories: Array.from({ length: 13 }, (_, i) => `${i * 2}:00`),
    crosshair: true,
  },
  yAxis: { max: 100, tickInterval: 20 },
  plotOptions: { 
    column: { pointPadding: 0.2, borderWidth: 0, },
  },
  series: [
    { name: "Server 1", data: [10.2, 10.1, 10.3, 10.5, 10.6, 10.4, 10.3, 10.2, 10.1, 10.0, 10.9, 10.8, 50.7], },
    { name: "Server 2", data: [5.0, 6.9, 7.2, 8.1, 1.4, 1.3, 1.5, 1.6, 1.7, 1.8, 1.6, 1.5, 1.4], },
    { name: "Server 3", data: [0.8, 0.7, 0.9, 1.0, 1.2, 6.3, 7.4, 8.5, 9.6, 5.4, 1.3, 1.2, 1.1], },
    { name: "Server 4", data: [0.9, 5.0, 1.1, 5.3, 1.5, 7.6, 1.4, 8.2, 1.0, 0.8, 8.6, 0.5, 0.4], },],
};

서버 응답 시간과 오류율 차트를 생성하기 위해 responseTimeOptions와 errorRateOptions 옵션을 정의합니다. 각 옵션의 설명은 다음과 같습니다.

  • chart: 차트의 유형 설정
  • title: 차트의 제목을 설정하고 정렬과 크기 설정
  • xAxis: X축 설정
  • yAxis: Y축 설정
  • plotOptions: 선형 차트와 막대 차트의 디테일 설정
  • series: 데이터를 시각화하는 여러 개의 데이터 시리즈를 정의

 

ServerMonitoring.js

import React, { Component } from "react";
import ChartComponent from "./ChartComponent";

const responseTimeOptions = {...};
const errorRateOptions = {...};

export default class ServerMonitoring extends Component {
  constructor(props) {
    super(props);
    this.state = {
      chartData: [
        {
          option: responseTimeOptions,
        },
        {
          option: errorRateOptions,
        },
      ],
    };
  }

  render() {
    return (
      <div>
        {this.state.chartData.map((obj, i) => (
          <div className="chart-monitoring-container" key={i}>
             <ChartComponent option={obj.option} />
          </div>
        ))}
      </div>
    );
  }
}

위에서 정의한 responseTimeOptions와 errorRateOptions 옵션을 chartData 상태에 저장을 합니다. 그리고 render 메서드에서 map 함수를 이용해 chartData 배열을 순회하면서, 각 차트를 렌더링 하는 ChartComponent 컴포넌트에 옵션을 전달하여 차트를 생성합니다.

 

 

 
 

1.2 차트 유형 line -> bar 변경


<div>
  {this.state.chartData.map((obj, i) => (
    <div className="chart-monitoring-container" key={i}>
       <ChartComponent option={obj.option} />
         <div className="chart-monitoring-selectBox">
            <select onChange={(e) => this.changeChartType(e, obj, i)}>
              <option value="line">Line</option>
              <option value="column">Bar</option>
            </select>
          </div>
    </div>
  ))}
</div>

차트 유형을 변경하기 위해 드롭다운 메뉴를 통해 사용자가 차트 타입을 변경할 수 있도록 합니다.

changeChartType = (e, obj, index) => {
    let chartOptions = _.cloneDeep(this.state.chartData);
    chartOptions[index].option.chart.type = e.target.value;
    this.setState({ chartData: chartOptions });
};

차트 유형 변경 시 실행되는 이벤트로, 선택된 차트를 찾아서 유형을 변경하는 역할을 수행합니다. _.cloneDeep을 사용하여 상태 데이터를 깊은 복사함으로써 원본 데이터를 유지하고, 선택한 차트의 유형을 변경하여 사용자 인터페이스에 실시간으로 반영되도록 합니다.

 

 

 

2. 마무리


삽질했던 부분

차트가 1개... 10개... 30개가 있을 때 이벤트가 발생하면 setState 실행되면서 재렌더링이 됩니다.
그러면 30개의 차트가 다시 그려지기 위해 애니메이션이 실행되는데 그로 인해, 웹 페이지가 느려지는 문제점이 발생합니다.

해결 방법은 함수형 컴포넌트의 훅을 이용해서 개별 렌더링을 하는 방법이 있지만, 현재 클래스 컴포넌트를 사용하기 때문에
훅을 사용할 수 없었습니다. 그리고 클래스 컴포넌트에서 개별 렌더링을 지원해 주는 훅을 찾아봤지만 없었습니다.

그래서 컴포넌트 사이에서 개별 렌더링할 수 있는 방법을 찾아보았습니다.
일단 방법은 차트를 렌더링 하는 컴포넌트를 분리하여 진행하고 componentDidMount() 훅에서 
Highcharts.merge( ) 이용하여 기본 값 + 동적 데이터를 병합하여 이벤트가 발생한 차트에만 렌더링 할 수 있도록 하였습니다.

componentDidMount() {
  const options = Highcharts.merge(defaultOptions, this.props.option);
  this.chart = Highcharts.chart(this.container, options);
}

이렇게 해서 전체 렌더링 문제를 해결할 수 있었습니다.

 

 

 

[출처]
highcharts - JS API Reference   
Github - highcharts