레이아웃 스래싱: 한 프레임에서 반복되는 리플로우와 멈추는 방법
무슨 일이 일어나는가
브라우저는 프레임마다 한 번 레이아웃을 재계산하려 합니다. 하지만 코드가 스타일을 쓰고, 기하 속성(offsetWidth, getBoundingClientRect)을 읽고, 다시 쓰기를 — 루프에서 — 반복하면, 읽을 때마다 페이지의 새로운 동기 레이아웃이 강제됩니다. 100개 요소의 루프는 한 프레임에서 100번의 완전한 리플로우를 일으킬 수 있습니다.
비용은 DOM 크기에 비례하므로, 작은 페이지에서 문제없던 같은 코드가 수천 개 노드의 페이지에서는 INP를 망칩니다.
알아보는 법
- INP 분해에서 presentation delay(핸들러 이후 렌더링 작업)가 지배적.
- DevTools Performance에서 한 프레임 안에 보라색 Layout 블록이 많이 보이고 자주 “Forced reflow”로 표시됨.
- 코드가 루프 안에서 읽기(offsetTop, clientHeight, getComputedStyle)와 쓰기(style, classList)를 번갈아 함.
해결 방법
- 먼저 모든 읽기를 묶고, 그다음 모든 쓰기를 — 루프에서 절대 섞지 마세요.
- 쓰기를 requestAnimationFrame으로 옮겨 다음 페인트 직전에 적용되게 하세요.
- 요소를 JS로 측정·배치하기보다 CSS 해결책(클래스, transform, flex/grid)을 선호하세요.
문제
items.forEach((el) => {
el.style.width = base + "px" // write
const h = el.offsetHeight // read → forced reflow, every iteration
el.style.height = h * 2 + "px" // write
})
해결
const heights = items.map((el) => el.offsetHeight) // all reads
requestAnimationFrame(() => {
items.forEach((el, i) => { // all writes
el.style.width = base + "px"
el.style.height = heights[i] * 2 + "px"
})
})
사이트 무료로 스캔하기 →