> ## Content Index
> Fetch the complete content index at: https://www.popit.kr/llms.txt
> Use this file to discover other available public pages before exploring further.

# [좌충우돌 개발기] 앗! 리액트 + 하이차트로 차트를 그렸는데 차트가 갱신이 안되네!?
- URL: https://www.popit.kr/react-highcharts-chart-issue/
- Published: 2019-05-22T22:49:04.000Z
- Updated: 2020-04-11T16:41:53.000Z
- Author: devarchi33
- Tags: #소프트웨어, 리액트, Highchart

안녕하세요, [플랫폼별 QR, 바코드 스캐너 구현기](https://www.popit.kr/qr-barcode-scanner-by-platform/)를 썼던 이동훈입니다.

최근 대시보드 화면 구현 중 기본을 놓쳐서 시간을 낭비한 경험이 있었습니다. 이번 글에서는 삽질기를 돌아보고, **React + Highchart** 조합을 사용하여 대시보드 구성시 주의해야 할 **`데이터 초기 로딩 방법`**과 `**데이터 변경 시 차트 리렌더링**은 어떻게 해야 하는지에 관하여 이야기해보려 합니다.`

##  Highchart

구현에 사용한 기술 중 Highchart에 관해서 [위키](https://en.wikipedia.org/wiki/Highcharts?ref=popit.kr)를 통해 잠시 살펴보면

> **Highcharts** is a [software library](https://en.wikipedia.org/wiki/Software%5Flibrary?ref=popit.kr) for charting written in pure [JavaScript](https://en.wikipedia.org/wiki/JavaScript?ref=popit.kr), first released in 2009.

**pure*라는 단어와 *2009년에 처음 배포*가 되었다는 사실이 눈에 들어옵니다.*

## **구현해야 할 사항들**

- 로그인 시 첫 화면에 당일 기준 매출 금액, 매출 건수, 반품 금액, 반품 건수를 보여주는 차트를 구현한다.
- 1일, 1주, 1달, 1년의 기간을 설정할 수 있는 버튼이 존재하고 버튼을 눌렀을 때, 데이터가 갱신되고 차트도 갱신된다.
- 고객 데모시연용 화면임
  - 데모까지 2일, 구현 후 내부 확인 까지 1일의 기간이 주어짐.

## **삽질기 소개**

최대한 빠르게 React + Highchart [사용 예제](https://github.com/wiesson/react-highcharts-example/blob/master/src/App.js?ref=popit.kr)를 찾아서 확인한 후, 원하는 모양의 정적 json을 차트 컴포넌트에 바인딩 해 보았습니다.

> 나: 잘 나오네 ㅎㅎ React + Highchart 짱짱! API만 만들어서 붙이면 되겠다\~

API를 만들고 **차트 데이터를 state 변수**에 바인딩 한 후, state 가 바뀔 때, 차트도 바뀌길 기대하며 확인해 보았습니다. 하지만... 데이터만 갱신이 되고, 차트는 갱신이 되지 않았습니다.

> 나: (이상하다..., 분명 state 변수는 갱신이 되는데, 왜 차트가 안 바뀌지???)

잠시 삽질후..

> 나: (빨리 돌아가는 화면이 나와야 하는데...) [김이사님](https://www.popit.kr/author/babokim/) 도와주세요\~김: 여기(사내 다른 프로젝트 코드) 보고 하세요.나: 아..잘되네요 ㅎㅎ(일단구현)

## **구현시 겪었던 문제분석**

### 코드분석

시간에 쫓겨 코드를 잘 살피지 못했었기 때문에 주말을 이용해 코드를 다시 살펴보았습니다.

문제는 [copy & paste 했던 코드](https://github.com/wiesson/react-highcharts-example/blob/master/src/App.js?ref=popit.kr) 와  

```js
...
componentDidMount() {
    this.chart = new Highcharts[this.props.type || 'Chart'](
      this.chartContainer.current,
      this.props.options
    );
}
...
```

[HighCharts React Official](https://github.com/highcharts/highcharts-react/blob/master/src/HighchartsReact.js?ref=popit.kr) 코드를 비교해 보니 쉽게 알 수 있었습니다.  

```js
...
componentDidMount() {
    const props = this.props;
    const highcharts = props.highcharts || window.Highcharts;
    // Create chart
    this.chart = highcharts[props.constructorType || "chart"](
      this.container.current,
      props.options,
      props.callback ? props.callback : undefined
    );
}
componentDidUpdate() {
    if (this.props.allowChartUpdate !== false) {
      this.chart.update(
        this.props.options,
        ...(this.props.updateArgs || [true, true])
      );
    }
}
...
```

갱신이 안되었던 코드에는 update 로직이 없었던 것이었습니다. 하지만 조금 이상합니다. **"React는 원래 부모의 state를 갱신하면 자식의 render 도 수행되는 것 아닌가?"** 하는 의문이 드는데요. 아래 내용과 연관이 있습니다.

### Dom 바인딩

HighChart는 차트 생성 시 **container**라고 부르는 **Dom component** 가 필요한데요.  

```js
this.chart = Highcharts.chart('chart', this.state.options); 
<div id="chart"></div>
```

위와 같은 코드입니다. 이 부분을 통해 HighChart는 id를 참조하여 차트가 그려질 공간에 바인딩 되는 것을 알 수 있습니다. 여기서 드디어 첫 코드에서는 **state 가 바뀌었는데 차트가 다시 그려지지 않은 실마리**를 찾을 수 있었습니다. HighChart 가 차트를 그리는 방법은 **그려질 위치, 차트에 관한 옵션** 를 인자로 받아 pure 자바스크립트로 된 차트 객체를 생성하여 dom에 바인딩 하는 방식으로 차트를 그리고 있었습니다. 그런데, 첫 번째 코드에서는 componentDidMount에서 1회만 차트 객체가 생성되기 때문에 state 가 변경이 되어도 차트가 갱신되지 않는 것이었습니다.

2번째 코드에서 차트의 갱신 원리를 그림과 함께 정리해보면 아래와 같습니다.

- 부모 컴포넌트의 state에 차트 데이터에 해당하는 options가 할당이 되어 있습니다.
- API를 통해 options의 데이터가 갱신이 완료되면, 자식 컴포넌트의 componentDidUpdate가 실행됩니다.
- componentDidUpdate 내부에선 HighChart 가 제공하는 update 함수를 이용하여 차트를 갱신합니다.

![Screen Shot 2019-05-22 at 01.54.17](https://www.popit.kr/content/images/wp/2019/05/Screen-Shot-2019-05-22-at-01.54.17-600x544.png)

주목할 점은 차트를 갱신하는데 React에서 제공하는 **state** 변수를 사용한 것이 아니라 **this.chart.update** 즉, pure javascript HighChart에서 제공하는 update 함수를 사용했다는 점입니다. (왜 react인데 react처럼 쓰지를 않는 거니..

![Screen Shot 2019-05-21 at 23.06.17](https://www.popit.kr/content/images/wp/2019/05/Screen-Shot-2019-05-21-at-23.06.17.png)

) (참고: [HighChart Official Blog](https://www.highcharts.com/blog/tutorials/highcharts-react-wrapper/?ref=popit.kr))

## 차트 갱신시 State 는 사용하지 못하는 건가?

이쯤에서 제가 해결했던 코드를 살펴보겠습니다.  

```js
class App extends Component {
  constructor(props) {
    super(props);
    this.chart = "";
    this.state = {
      options: {
        ...
    }
  }
  componentDidMount() {
    this.loadChartData();
  };
  loadChartData = () => {
    this.setState({options: {...this.state.options, series: [{
        ...
      },
    ]}}, () => this.chart = Highcharts.chart('chart', this.state.options));
  };
...
  render() {
    return (
      <div className="App">
        <div>
          <button onClick={this.loadChartData}>change</button>
        </div>
        {this.state.loading
            ? (<div>Loading...</div>)
            : (<div id="chart"></div>)}
      </div>
    );
  }
}
export default App;
```

위 코드를 보면 componentDidMount에서 초기 데이터를 호출했고, event 함수(onClick)에 데이터 갱신 함수(loadChartData)를 바인딩 하여 차트를 갱신했습니다.  

```js
this.chart = Highcharts.chart('chart', this.state.options)
```

꺼림칙한 부분은 위 코드인데요, 데이터가 갱신될 때마다, this.chart 변수에 새로운 Chart 객체가 할당되는 것을 볼 수 있습니다. 여기까지 정리해 보고 나니 현재 코드의 개선점을 찾은 것 같습니다.

개선할 점은 차트를 매번 새로 생성하여 할당하는 부분을 HighChart에서 제공하는 update 함수를 사용하여 아래와 같이 수정하는 것입니다.  

```js
loadChartData = () => {
    this.setState({options: {...this.state.options, series: [{
        ...
      },
    ]}}, () => this.chart.update(this.state.options));
  };
```

## 정리

### 데이터 초기 로딩은 어디서 해야 하나?

- componentDidMount에서 하면 됩니다. (주의할 점: 초기 데이터 로딩전 HighChart 객체를 생성하여 Dom에 바인딩 해주어야 합니다.)

### 데이터 변경 시 차트 리렌더링은 어떻게 하나?

- 차트 데이터는 state에 바인딩 한다.
- Highart 가 제공하는 update 함수를 사용하여 차트를 갱신
  - 부모 + 자식 컴포넌트 방식을 사용했다면, 자식 컴포넌트의 componentDidUpdate 안에서 호출.
  - 1개의 컴포넌트에서 사용했다면, event 함수 안에서 먼저 setState를 통해 차트 데이터를 갱신하고, setState의 콜백에서 호출.

생각해보면[ React Life Cycle](http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/?ref=popit.kr)에 대한 동작원리와 HighChart 가 React 환경에서 어떻게 동작하는지 조금만 생각해 보았으면 삽질을 안 했을 수도 있었을 것 같습니다. 기본에 충실해야 하는 것을 잊지 말고자 회고 성격의 글을 작성해보았습니다. 읽어주셔서 감사합니다.