상황
최근 회사 프로젝트(react + python)에서 프론트엔드 작업 중 사이드바를 열고 닫을 때 간헐적으로 대시보드 상의 차트 오른쪽에 여백이 생기는 현상을 발견했다.
해당 증상은 간헐적으로 발생하였고, 사이드바의 너비(width)만큼 생긴다는 것을 확인하였다.
증상 분석
- 사이드바를 열면 메인 화면(차트 포함)이 오른쪽으로 밀림
- 사이드바를 닫으면 다시 원위치 되어야 함
- 하지만 간헐적으로 차트의 오른쪽에 여백이 남는 현상 발생
- 아래 이미지처럼 차트 오른쪽이 padding된 것처럼 비어 있는 증상이 발생함
보안을 위해 이미지의 내용은 masking 처리하였음을 참고


원인 분석
프로젝트의 레이아웃 구조는 대략적으로 다음과 같다.
<상위 레이아웃>
<사이드바 />
<메인페이지 /> // 이 안에서 CustomECharts 컴포넌트 사용
</상위 레이아웃>
상위 컴포넌트인 layout.tsx에서 사이드바 컴포넌트와 메인페이지(대시보드: 차트 출력) 컴포넌트를 제어하는 형태인데,
사이드바는 햄버거 버튼을 눌러 열고 닫는 구조이며,
닫힐 때는 CSS transition으로 width를 0으로 애니메이션 처리한다.
예를 들면,
.layout-form {
&.hide {
.sidebar {
width: 0px;
min-width: 0px;
max-width: 0px;
}
}
}
와 같이 css적으로 처리하고 있었다.
즉, 실제로 DOM의 폭이 줄어들지만, 브라우저의 viewport 자체 크기는 변하지 않는다.
이 부분이 문제의 시작점이었다.
메인 페이지(대시보드)에서는 차트 구현을 echarts 라이브러리를 커스텀한 컴포넌트(CustomECharts)로 구현하고 있었는데,
해당 코드 내부에서는 다음과 같이 리사이즈를 처리하고 있었다.
const chartInstance = useRef(null); // chart 인스턴스를 유지하기 위해 사용
...
window.addEventListener('resize', () => {
chartInstance.current?.resize();
});
...
코드를 보면 window의 크기가 변할 때만 차트를 다시 그리도록(resize) 되어 있었던 것.
그런데 나의 사이드바의 열림/닫힘 기능은 CSS transition으로 width가 변경되는 구조였고,
이는 브라우저 창 크기가 변하는 게 아니기 때문에 window.resize 이벤트가 아예 발생하지 않는다.
현재 코드 상으로는 브라우저 창 크기 변경만 감지하고, CSS로 인한 DOM 크기 변경은 감지하지 못한 것.
결과적으로 그로 인해 차트는 레이아웃이 바뀐 걸 인식하지 못한 채, 이전 width 상태 그대로 남아 있는 증상이 발생한 것.
간헐적으로 발생한 이유는 사이드의 transition 도중(약0.3초 정도의 이벤트)에 리렌더링이 발생하면서 타이밍에 따라서 정상 작동하거나 버그가 발생한 것이다.
해결 방법: ResizeObserver 도입
이 문제를 해결하기 위해 ResizeObserver API를 사용했다.
이 API는 특정 DOM 요소의 크기 변화를 감지(직접 관찰)할 수 있기 때문에
사이드바 열림/닫힘 시 메인 컨테이너가 미묘하게 변하는 순간에도 반응할 수 있다.
또한, 모든 현대 브라우저에서 지원하는 현대 표준이라는 거...
const chartInstance = useRef(null); // chart 인스턴스를 유지하기 위해 사용
...
useEffect(() => {
if (!chartRef.current || !chartInstance.current) return;
// 1. ResizeObserver로 요소 크기 변화를 감지
const resizeObserver = new ResizeObserver(() => {
if (chartInstance.current) {
chartInstance.current.resize();
}
});
resizeObserver.observe(chartRef.current);
// 2. window 크기 변경도 병행 지원
const handleWindowResize = () => {
chartInstance.current?.resize();
};
window.addEventListener('resize', handleWindowResize);
// 3. 사용한 이벤트 정리(clean-up)
return () => {
resizeObserver.disconnect();
window.removeEventListener('resize', handleWindowResize);
};
}, [{적절한 의존성 배열}]);

ResizeObserver란?
ResizeObserver는 브라우저에서 제공하는 DOM 크기 감시 API다.
요소의 width, height가 바뀔 때마다 콜백을 호출해준다.
즉,
* window 전체 크기 변경(resize)
→ window.resize 이벤트로 감지
* 특정 요소의 크기만 변경 (예: 사이드바 width 변화)
→ ResizeObserver로 감지
덕분에 transition 기반 UI에서도 실시간으로 요소의 변경을 인식해 차트를 리사이즈할 수 있다는 거...
참고로, 좀 더 최적화 한다면, requestAnimationFrame을 활용하면 된다고 한다.
ResizeObserver 콜백은 레이아웃 계산 직후 호출되기 때문에,
동일 프레임 안에서 바로 resize()를 호출하면 경고가 뜨거나 깜빡일 수 있다.(동일 프레임 순환 이슈)
이를 방지하려면 다음처럼 한 프레임 늦춰 처리하는 것이 좋다.
const resizeObserver = new ResizeObserver(() => {
requestAnimationFrame(() => {
chartInstance.current?.resize();
});
});
정리: window.resize vs ResizeObserver
| 구분 | 방식 | 감지 대상 | 사이드바 대응 |
|---|---|---|---|
| 기존 방식 | window.addEventListener('resize') |
브라우저 viewport | 감지 불가 |
| 개선 방식 | ResizeObserver |
특정 요소의 width/height | 대응 |
'개발 일상' 카테고리의 다른 글
| [Critical Issue] React Server Components (RSC) 관련 치명적 보안 취약점 조치법 (0) | 2025.12.12 |
|---|---|
| Windows 서버에서 FastAPI 자동 실행 시 발생한 서비스 오류 대응기 (0) | 2025.11.11 |
| 서로 다른 서버 간 MongoDB 컬렉션 데이터 마이그레이션(ubuntu) (2) | 2025.07.03 |
| [ubuntu 22.04] 최신 chrome 브라우저에서 키보드 입력 문제 해결법 (1) | 2025.06.23 |
| YOLO 객체 감지 후 위험 영역 판단 로직 개선기 (1) | 2025.06.10 |
