DOM을 바꾸고 곧바로 레이아웃 값을 읽기를 반복해서, 브라우저가 매번 리플로우를 강제로 실행하게 되는 현상. 쓰기 → 읽기 → 쓰기 → 읽기가 반복문 안에서 일어나면 요소 수만큼 레이아웃을 계산한다.
왜 생기나
브라우저는 스타일 변경을 바로 계산하지 않고 "레이아웃이 낡았다"는 표시만 해 둔 뒤 프레임 끝에 한꺼번에 처리한다. 그런데 정확한 기하 값을 요구하는 API를 부르면 미뤄 둔 계산을 그 자리에서 해야 한다.
레이아웃을 강제하는 대표 API: offsetWidth/Height/Top, clientWidth/Height, scrollTop/Height, getBoundingClientRect(), 레이아웃에 의존하는 값을 읽는 getComputedStyle()
// 나쁨: 루프마다 강제 레이아웃
items.forEach(el => {
el.style.width = '200px' // 쓰기
console.log(el.offsetWidth) // 읽기 → 즉시 레이아웃
})
// 좋음: 읽기를 모두 끝낸 뒤 쓰기
const widths = [...items].map(el => el.offsetWidth)
items.forEach(el => { el.style.width = '200px' })피하는 법
- 읽기 단계와 쓰기 단계를 나눈다(배치 처리)
- 쓰기는 requestAnimationFrame 안에 모아 다음 프레임에 한 번에 반영한다
- 위치 이동은
transform으로 해서 레이아웃을 건드리지 않는다 → 합성 단계와 transform·opacity - 영향 범위를
contain이나 content-visibility로 가둔다 - React에서 측정이 필요하면 useLayoutEffect에서 한 번 읽는다
60fps(frames per second)라면 한 프레임 예산은 약 16ms인데, DOM이 크면 강제 레이아웃 몇 번으로 금방 넘친다. DevTools Performance 패널에서 보라색 Layout 블록에 "Forced reflow" 경고가 붙으면 이 패턴을 의심하면 된다 → 크롬 개발자 도구 팁