Layout (Reflow)
위치·크기 계산
렌더 트리의 각 요소에 대해 정확한 좌표와 크기를 계산하는 단계(리플로우).
레이아웃(Layout), 또는 리플로우(Reflow)는 렌더 트리의 각 노드에 대해 뷰포트(viewport) 안에서의 정확한 위치와 크기를 기하학적으로 계산하는 단계로, 결과는 상대값(%, em, vw 등)까지 모두 픽셀 좌표로 확정된다. 계산의 기본 단위는 박스 모델(box model)로, 안쪽부터 content → padding → border → margin 순의 영역과 box-sizing(content-box/border-box)에 따라 크기가 결정된다. 각 요소는 자신이 속한 포매팅 컨텍스트(formatting context)—블록(BFC), 인라인(IFC), 플렉스, 그리드—의 규칙에 따라 배치되며, 이 컨텍스트가 자식들의 흐름·정렬·마진 병합 여부를 지배한다. 레이아웃은 렌더 트리 크기에 비례하는 비용이 큰 작업이라 브라우저는 변경된 부분만 다시 계산하는 증분 레이아웃(incremental/dirty-bit layout)으로 최적화한다. 하지만 JS가 offsetTop, getBoundingClientRect(), getComputedStyle() 등 레이아웃 의존 값을 스타일 변경 직후 읽으면 브라우저가 큐에 쌓인 변경을 즉시 반영하려 강제 동기 레이아웃(forced synchronous layout)을 유발하고, 이를 읽기/쓰기가 번갈아 반복되면 레이아웃 스래싱(layout thrashing)이 되어 프레임을 떨어뜨린다.
내부 구성
핵심 포인트
- 레이아웃/리플로우는 각 노드의 위치·크기를 뷰포트 기준 픽셀로 확정하는 단계
- 박스 모델: content → padding → border → margin, box-sizing이 크기 해석을 바꿈
- 포매팅 컨텍스트가 배치 규칙 결정: BFC(블록), IFC(인라인), Flex, Grid
- 리플로우 트리거: DOM 추가/삭제, 크기·폰트·위치 변경, 창 리사이즈, 콘텐츠 변경 등
- 증분 레이아웃(dirty bit): 변경된 서브트리만 다시 계산해 비용 절감
- 강제 동기 레이아웃: 레이아웃 의존 속성(offsetWidth, getBoundingClientRect 등)을 읽으면 즉시 재계산
- 레이아웃 스래싱: 읽기↔쓰기 반복으로 한 프레임에 리플로우가 여러 번 → 읽기 batch로 해결(FastDOM 등)
심화
레이아웃 스래싱의 본질은 '읽기가 이전 쓰기를 무효화된 레이아웃 위에서 강제 계산'하게 만든다는 데 있다. 브라우저는 스타일 변경을 큐에 모아 프레임 끝에 한 번에 처리(batch)하려 하지만, JS가 중간에 offsetHeight 같은 값을 읽으면 '최신 값을 주려면 지금 계산해야 하므로' 동기 리플로우가 일어난다. for 루프 안에서 el.style.height를 쓰고 바로 el.offsetHeight를 읽는 코드는 매 반복마다 리플로우를 유발한다. 해결책은 모든 읽기를 먼저 몰아서 하고(캐싱) 그 다음 쓰기를 몰아 하는 read-then-write 분리이며, requestAnimationFrame으로 쓰기를 다음 프레임에 배치하거나 FastDOM 같은 라이브러리로 read/write 페이즈를 스케줄링한다. Chrome DevTools Performance 패널은 30ms를 넘는 강제 리플로우를 'Forced reflow' 경고로 표시한다. 심화로 알아둘 것: getBoundingClientRect()가 반환하는 좌표는 뷰포트 기준이고 스크롤에 영향을 받으며, 여러 요소의 사각형을 한 프레임에 읽어야 한다면 IntersectionObserver나 ResizeObserver로 대체하면 강제 동기 레이아웃 없이 비동기로 정보를 얻을 수 있다. 또한 현대 flexbox의 레이아웃 성능은 float와 대체로 비슷하며(단일 패스인 일반 블록 레이아웃이 오히려 더 빠를 수 있다) — 흔히 인용되는 벤치마크는 구형 flexbox(display:box)가 현대 flexbox(display:flex)보다 약 2.3배 느리다는 것이지 flexbox가 float보다 빠르다는 게 아니다 — contain: layout이나 content-visibility로 레이아웃 범위를 격리하면 서브트리 변경이 전체로 번지는 것을 막을 수 있다. 이런 격리·관측 API 활용이 대규모 앱의 INP(Interaction to Next Paint) 개선의 핵심이다.
쉽게 말하면 무대 위 배우들의 정확한 위치와 동선을 좌표로 정하는 리허설.