브라우저 내장 메서드 - ResizeObserver
ResizeObserver란?
ResizeObserver
는 DOM 요소의 크기 변화를 감지하는 웹 API입니다. 주로 레이아웃 변경이나 반응형 UI를 구현할 때, 특정 엘리먼트의 너비나 높이가 변경되는 것을 추적할 수 있습니다.비슷한 개념으로
MutationObserver
가 있지만, MutationObserver
는 DOM 트리(요소의 추가/삭제/속성 변화 등)를 추적하는 반면, ResizeObserver
는 엘리먼트의 시각적 크기 변화만을 감지합니다.감지할 수 있는 Box 종류
ResizeObserver
는 감지 대상 박스를 지정할 수 있도록 options
객체를 받으며, box
속성에는 다음 중 하나를 설정할 수 있습니다.content-box
(기본값):padding
을 제외한 콘텐츠 영역 크기
border-box
:padding
,border
를 포함한 전체 영역 크기
device-pixel-content-box
: 고해상도 디바이스에서의 실제 픽셀 단위 크기 (성능 비용이 있으므로 주의)
사용법
const callback = (entries, observer) => { entries.forEach(entry => { const { contentBoxSize, borderBoxSize, contentRect, target } = entry; console.log('관찰 대상:', target); console.log('contentBoxSize:', contentBoxSize); console.log('borderBoxSize:', borderBoxSize); console.log('contentRect:', contentRect); // legacy }); }; const observer = new ResizeObserver(callback); const el = document.getElementById('root'); // box 옵션: 'content-box' | 'border-box' | 'device-pixel-content-box' observer.observe(el, { box: 'border-box' });
콜백 함수는 ResizeObserverEntry[] 배열과 observer 인스턴스를 인자로 받습니다.각entry
는 감지 대상의 상세한 크기 정보를 포함하고 있습니다.
ResizeObserverEntry 속성 정리
각
ResizeObserverEntry
객체에는 다음 속성들이 포함됩니다.속성명 | 설명 |
target | 관찰 중인 DOM 요소 |
contentRect | 관찰 대상의 DOMRectReadOnly (기존 방식) |
contentBoxSize | 콘텐츠 영역(content-box)의 크기 정보 |
borderBoxSize | 전체 영역(border-box)의 크기 정보 |
contentBoxSize와 borderBoxSize는 브라우저에 따라 배열로 반환되기도 하니, entry.contentBoxSize[0].inlineSize와 같은 방식으로 접근하는 것이 안전합니다.
🚨 주의사항: IE 지원 불가
ResizeObserver
는 Internet Explorer를 지원하지 않습니다.만약 IE에서도 사용해야 한다면, 다음과 같은 폴리필을 적용해야 합니다:
npm install @juggle/resize-observer
import ResizeObserver from '@juggle/resize-observer'; const observer = new ResizeObserver(callback);
언제 사용하면 좋을까?
- 반응형 UI에서 엘리먼트 크기 기반으로 조건 분기할 때
- 특정 요소가 축소되거나 확장될 때 애니메이션 또는 스타일을 적용하고 싶을 때
- 외부 라이브러리로부터 스타일 영향을 받을 수 있는 경우
window.resize
이벤트로 커버할 수 없는 컴포넌트 내부의 레이아웃 변경 감지가 필요할 때
실전 예제: 반응형 콘텐츠 트리거
import { useEffect, useRef, useState } from 'react'; function ResponsiveBox() { const ref = useRef(); const [isSmall, setIsSmall] = useState(false); useEffect(() => { const observer = new ResizeObserver(entries => { for (let entry of entries) { const width = entry.contentRect.width; setIsSmall(width < 400); } }); if (ref.current) { observer.observe(ref.current); } return () => { observer.disconnect(); }; }, []); return ( <div ref={ref} style={{ width: '100%', padding: '2rem', backgroundColor: isSmall ? 'lightcoral' : 'lightgreen', }} > {isSmall ? '좁은 화면입니다' : '넓은 화면입니다'} </div> ); }