requestAnimationFrame: UI 레이그ущ의 해결책
카테고리
프로그래밍/소프트웨어 개발
서브카테고리
웹 개발
대상자
- *프론트엔드 개발자, 애니메이션 및 UI/UX 개발자**
- 난이도: 중간 (기본적인 JavaScript 지식 필요)
핵심 요약
requestAnimationFrame()
는 브라우저의 60fps 렌더링 주기와 동기화되어 animations, DOM 업데이트를 정확히 수행하는 데 사용됨setTimeout(fn, 16)
는 렌더링 주기와 비동기로 실행되어 프레임 떨림 및 레이아웃 썸(layout thrash) 발생 가능- 고주파 디스플레이(예: 120Hz)에서는 프레임 예산이 8ms로 줄어들며,
requestAnimationFrame()
은 자동으로 조정됨
섹션별 세부 요약
1. 브라우저의 렌더링 주기 이해
- 브라우저는 60fps(약 16.66ms/프레임)로 화면을 업데이트
- 프레임 처리 순서: 입력 처리 → JavaScript 실행 → 스타일 적용 → 레이아웃/페인트
- 시간 초과 시 프레임 드롭, 애니메이션 부드러움 저하, UI 레이그ущ 발생
2. `requestAnimationFrame()`의 장점
- 렌더링 직전에 콜백 실행 → DOM 업데이트 및 스타일 조정 가능
timestamp
파라미터는performance.now()
와 동일한 고해상도 시간 제공requestAnimationFrame()
은 렌더링 루프(render loop)의 일부로, 애니메이션, 배치 DOM 업데이트, 스크롤 위치 추적에 적합
3. React에서의 적용 예시
- 스크롤 기반 상태 업데이트:
useEffect
와requestAnimationFrame
을 결합해 프레임 레이트 기반 타rottle
useEffect(() => {
let animationFrameId;
const handleScroll = () => {
if (animationFrameId) return;
animationFrameId = requestAnimationFrame(() => {
const scrollTop = window.scrollY;
setScrollPosition(scrollTop);
animationFrameId = null;
});
};
window.addEventListener('scroll', handleScroll);
return () => {
window.removeEventListener('scroll', handleScroll);
if (animationFrameId) cancelAnimationFrame(animationFrameId);
};
}, []);
useEffect(() => {
const id = requestAnimationFrame(() => setShowTooltip(true));
return () => cancelAnimationFrame(id);
}, [hovered]);
4. `requestAnimationFrame()`과 `setTimeout()`의 조합
- 배경 탭에서의 대체:
document.hidden
을 체크해setTimeout
으로 대체
function loop() {
if (document.hidden) {
setTimeout(loop, 100);
} else {
requestAnimationFrame(loop);
}
}
setTimeout
내부에 requestAnimationFrame
사용setTimeout(() => {
requestAnimationFrame(runAnimation);
}, 250);
5. 주의사항 및 피해야 할 패턴
- 피드백 루프(feedback loop) 생성 시 애니메이션 의도가 흐려짐
requestAnimationFrame()
은 React 렌더링을 중단하지 않음 →useRef
와 함께 사용해 과도한 렌더링 방지- 고주파 디스플레이에서 대량의 상태 업데이트는 성능 저하 유발 가능
결론
requestAnimationFrame()
은 브라우저 렌더링 주기와 동기화하여 부드러운 UI 및 저지연을 달성하는 핵심 도구setTimeout(fn, 16)
보다 렌더링 주기 기반으로 프레임 드롭 방지- React에서 상태 기반 애니메이션, 스크롤 처리, DOM 배치에 반드시 사용하며,
useRef
와 조합해 성능 최적화 - 고주파 디스플레이 사용 시 프레임 예산 조정 및 배경 탭 대체 로직(setTimeout) 적용 필수