하이차트 (Highcharts) 사용하기
1. Highcharts 개념과 사용
[ Highcharts 이란? ]
자바스크립트만으로 다양한 차트를 그려낼 수 있는 차트 라이브러리로, 웹 페이지에서 실시간으로 변경되는 다양한 차트를 그려줄 때 매우 유용한 라이브러리입니다.
[ 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: 시리즈에 대한 데이터 배열을 설정합니다.
지금까지 기본적인 사용법에 대해 알아보았습니다.
이제 차트를 통해 서버 모니터링 시스템에 적용시켜보겠습니다.
요구사항은 다음과 같습니다.
- 시간별로 서버 응답 시간과 오류율 차트 그리기
- 차트 유형 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);
}
이렇게 해서 전체 렌더링 문제를 해결할 수 있었습니다.