[Trouble Shooting] React Strict Mode에서 ECharts 인스턴스 중복 생성 방지
React Strict Mode의 Double Mount로 인한 ECharts disposed 경고 해결
문제 상황
React 프로젝트에서 ECharts를 사용할 때 다음과 같은 경고가 콘솔에 반복적으로 출력됨:
1
[ECharts] Instance ec_1234567890 has been disposed
차트는 정상적으로 렌더링되지만, 경고 메시지가 계속 발생하여 메모리 누수 가능성이 우려됨.
원인
React Strict Mode의 Double Mount
React 18의 Strict Mode는 개발 환경에서 컴포넌트를 두 번 마운트/언마운트하여 부수 효과(side effect)를 감지함.
1
2
3
4
1. 컴포넌트 마운트
2. useEffect cleanup 실행
3. 컴포넌트 다시 마운트
4. useEffect 다시 실행
기존 코드의 문제점
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// EChartsWrapper.tsx (문제가 있는 코드)
useEffect(() => {
if (!chartRef.current) return;
// 기존 인스턴스 dispose
if (chartInstanceRef.current) {
chartInstanceRef.current.dispose(); // ❌ 이미 dispose된 인스턴스를 또 dispose
}
// 새 인스턴스 생성
const chartInstance = echarts.init(chartRef.current);
chartInstanceRef.current = chartInstance;
return () => {
chartInstance.dispose();
};
}, []);
문제점:
- Strict Mode에서 첫 번째 마운트 시 인스턴스 생성
- cleanup 함수에서
dispose()호출 - 두 번째 마운트 시 이미 dispose된 인스턴스에 다시
dispose()시도 - “Instance has been disposed” 경고 발생
해결 방법
getInstanceByDom으로 인스턴스 재사용
ECharts의 getInstanceByDom() 메서드를 사용하여 기존 인스턴스를 재사용함.
Before (문제가 있는 코드)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
useEffect(() => {
if (!chartRef.current) return;
// ❌ 매번 새 인스턴스 생성
if (chartInstanceRef.current) {
chartInstanceRef.current.dispose();
}
const chartInstance = echarts.init(chartRef.current);
chartInstanceRef.current = chartInstance;
return () => {
chartInstance.dispose();
};
}, []);
After (개선된 코드)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
useEffect(() => {
if (!chartRef.current) return;
// ✅ 기존 인스턴스가 있으면 재사용
let chartInstance = echarts.getInstanceByDom(chartRef.current);
if (!chartInstance) {
chartInstance = echarts.init(chartRef.current);
}
chartInstanceRef.current = chartInstance;
// 차트 옵션 설정
if (option) {
chartInstance.setOption(option);
}
// cleanup: dispose 전에 isDisposed() 체크
return () => {
if (chartInstance && !chartInstance.isDisposed()) {
chartInstance.dispose();
}
};
}, [option]);
핵심 변경 사항
getInstanceByDom()사용: DOM에 이미 연결된 인스턴스가 있는지 확인- 조건부 생성: 인스턴스가 없을 때만 새로 생성
isDisposed()체크: dispose 전에 이미 dispose되었는지 확인
결과
Before
- 콘솔에
[ECharts] Instance ec_xxx has been disposed경고 반복 출력 - 메모리 누수 위험 존재
- Strict Mode에서 불안정한 동작
After
- 경고 메시지 완전히 제거됨
- 인스턴스가 효율적으로 재사용됨
- Strict Mode에서도 안정적으로 동작
- 메모리 관리 개선
주의할 점
1. Strict Mode는 개발 환경에서만 동작
- 프로덕션 빌드에서는 Double Mount가 발생하지 않음.
- 하지만 개발 환경에서 경고가 발생하면 메모리 관리 문제가 있을 가능성이 높으므로 반드시 수정해야 함.
2. isDisposed() 체크 필수
1
2
3
4
5
6
7
8
9
10
11
// ❌ 나쁜 예
return () => {
chartInstance.dispose(); // 이미 dispose된 상태면 에러
};
// ✅ 좋은 예
return () => {
if (chartInstance && !chartInstance.isDisposed()) {
chartInstance.dispose();
}
};
3. 의존성 배열 관리
option을 의존성 배열에 포함시키면 옵션 변경 시 차트가 업데이트됨.- 빈 배열(
[])을 사용하면 최초 마운트 시에만 실행됨.
4. 차트 리사이즈 처리
1
2
3
4
5
6
7
8
9
10
useEffect(() => {
const handleResize = () => {
if (chartInstanceRef.current && !chartInstanceRef.current.isDisposed()) {
chartInstanceRef.current.resize();
}
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
전체 코드 예시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// EChartsWrapper.tsx
import { useEffect, useRef } from 'react';
import * as echarts from 'echarts';
import type { EChartsOption } from 'echarts';
interface EChartsWrapperProps {
option: EChartsOption;
height?: number;
}
export const EChartsWrapper: React.FC<EChartsWrapperProps> = ({
option,
height = 300,
}) => {
const chartRef = useRef<HTMLDivElement>(null);
const chartInstanceRef = useRef<echarts.ECharts | null>(null);
useEffect(() => {
if (!chartRef.current) return;
// 기존 인스턴스 재사용
let chartInstance = echarts.getInstanceByDom(chartRef.current);
if (!chartInstance) {
chartInstance = echarts.init(chartRef.current);
}
chartInstanceRef.current = chartInstance;
// 차트 옵션 설정
if (option) {
chartInstance.setOption(option);
}
// cleanup
return () => {
if (chartInstance && !chartInstance.isDisposed()) {
chartInstance.dispose();
}
};
}, [option]);
// 리사이즈 처리
useEffect(() => {
const handleResize = () => {
if (chartInstanceRef.current && !chartInstanceRef.current.isDisposed()) {
chartInstanceRef.current.resize();
}
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return <div ref={chartRef} style={{ width: '100%', height: `${height}px` }} />;
};
React Strict Mode 환경에서 ECharts를 안전하게 사용하려면 getInstanceByDom()과 isDisposed()를 활용하여 인스턴스를 재사용해야 함. 이를 통해 메모리 누수를 방지하고 경고 메시지를 제거할 수 있음.
도움이 되셨길 바랍니다! 😀
This post is licensed under CC BY 4.0 by the author.