ResizeObserver는 DOM 요소의 크기 변화를 감지하는 웹 API입니다. 주로 레이아웃 변경이나 반응형 UI를 구현할 때, 특정 엘리먼트의 너비나 높이가 변경되는 것을 추적할 수 있습니다.
비슷한 개념으로 MutationObserver가 있지만, MutationObserver는 DOM 트리(요소의 추가/삭제/속성 변화 등)를 추적하는 반면, ResizeObserver는 엘리먼트의 시각적 크기 변화만을 감지합니다.
ResizeObserver는 감지 대상 박스를 지정할 수 있도록 options 객체를 받으며, box 속성에는 다음 중 하나를 설정할 수 있습니다.
content-box (기본값): padding을 제외한 콘텐츠 영역 크기border-box: padding, border를 포함한 전체 영역 크기device-pixel-content-box: 고해상도 디바이스에서의 실제 픽셀 단위 크기 (성능 비용이 있으므로 주의)1const callback = (entries, observer) => {
2 entries.forEach(entry => {
3 const { contentBoxSize, borderBoxSize, contentRect, target } = entry;
4
5 console.log('관찰 대상:', target);
6 console.log('contentBoxSize:', contentBoxSize);
7 console.log('borderBoxSize:', borderBoxSize);
8 console.log('contentRect:', contentRect); // legacy
9 });
10};
11
12const observer = new ResizeObserver(callback);
13const el = document.getElementById('root');
14
15// box 옵션: 'content-box' | 'border-box' | 'device-pixel-content-box'
16observer.observe(el, { box: 'border-box' });
17콜백 함수는 ResizeObserverEntry[] 배열과 observer 인스턴스를 인자로 받습니다.
각 ResizeObserverEntry 객체에는 다음 속성들이 포함됩니다.
| 속성명 | 설명 |
|---|---|
target | 관찰 중인 DOM 요소 |
contentRect | 관찰 대상의 DOMRectReadOnly (기존 방식) |
contentBoxSize | 콘텐츠 영역(content-box)의 크기 정보 |
borderBoxSize | 전체 영역(border-box)의 크기 정보 |
contentBoxSize와 borderBoxSize는 브라우저에 따라 배열로 반환되기도 하니, entry.contentBoxSize[0].inlineSize와 같은 방식으로 접근하는 것이 안전합니다.
ResizeObserver는 Internet Explorer를 지원하지 않습니다.
만약 IE에서도 사용해야 한다면, 다음과 같은 폴리필을 적용해야 합니다:
@juggle/resize-observerresize-observer-polyfillnpm install @juggle/resize-observerimport ResizeObserver from '@juggle/resize-observer';
const observer = new ResizeObserver(callback);
window.resize 이벤트로 커버할 수 없는 컴포넌트 내부의 레이아웃 변경 감지가 필요할 때1import { useEffect, useRef, useState } from 'react';
2
3function ResponsiveBox() {
4 const ref = useRef();
5 const [isSmall, setIsSmall] = useState(false);
6
7 useEffect(() => {
8 const observer = new ResizeObserver(entries => {
9 for (let entry of entries) {
10 const width = entry.contentRect.width;
11 setIsSmall(width < 400);
12 }
13 });
14
15 if (ref.current) {
16 observer.observe(ref.current);
17 }
18
19 return () => {
20 observer.disconnect();
21 };
22 }, []);
23
24 return (
25 <div
26 ref={ref}
27 style={{
28 width: '100%',
29 padding: '2rem',
30 backgroundColor: isSmall ? 'lightcoral' : 'lightgreen',
31 }}
32 >
33 {isSmall ? '좁은 화면입니다' : '넓은 화면입니다'}
34 </div>
35 );
36}
37