--- id: wiki-2026-0508-리플로우-및-리페인트-reflow-and-repaint title: 리플로우 및 리페인트 (Reflow and Repaint) category: 10_Wiki/Topics status: verified canonical_id: self aliases: [Reflow, Repaint, Layout, Paint, 브라우저 렌더링] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [browser, rendering, performance, frontend] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: javascript framework: browser --- # 리플로우 및 리페인트 (Reflow and Repaint) ## 매 한 줄 > **"매 Reflow = geometry 재계산, Repaint = pixel 재그리기"**. Reflow 매 layout phase — DOM/CSS geometry 변화 시 발생 — 매 expensive. Repaint 매 paint phase — color/visibility 변화 — 매 cheaper. 매 60fps 의 핵심 = 매 둘 다 피하기 + 매 compositor-only 속성 사용. ## 매 핵심 ### 매 Pixel Pipeline 1. JS — DOM 변경. 2. **Style** — selector 매칭, computed style. 3. **Layout** (= Reflow) — geometry 계산. 4. **Paint** (= Repaint) — pixel 채우기. 5. **Composite** — GPU layer 합성. ### 매 Reflow trigger - DOM 추가/제거 (block-level). - `width`, `height`, `top`, `left`, `padding`, `margin`, `border`. - `font-size`, `text-align`. - `offsetWidth`, `getBoundingClientRect()` 읽기 — 매 force sync layout. - viewport resize. ### 매 Repaint-only trigger - `color`, `background-color`, `visibility`, `outline`. ### 매 Compositor-only (best) - `transform`, `opacity`, `filter` (with `will-change`). - 매 main thread 의 X — 매 GPU layer 만. - 매 60fps 의 핵심. ### 매 Layout Thrashing - 읽기/쓰기 alternate — 매 매번 force reflow — 매 worst. - batch 매 read 다음 batch 매 write — 매 1회 reflow. ## 💻 패턴 ### Compositor-only animation ```css /* 매 GOOD — transform/opacity 만 — GPU */ .box { transition: transform 0.3s, opacity 0.3s; will-change: transform; /* 매 hint — 매 layer 미리 promote */ } .box:hover { transform: translateX(20px) scale(1.1); opacity: 0.8; } /* 매 BAD — top/left — 매 reflow */ .box-bad:hover { top: 20px; left: 20px; } ``` ### Read-write batching ```javascript // 매 BAD — thrashing items.forEach(el => { el.style.width = el.offsetWidth + 10 + 'px'; // read + write }); // 매 GOOD — batch const widths = items.map(el => el.offsetWidth); // all reads items.forEach((el, i) => { el.style.width = widths[i] + 10 + 'px'; // all writes }); ``` ### DocumentFragment for bulk insert ```javascript const frag = document.createDocumentFragment(); for (let i = 0; i < 1000; i++) { const li = document.createElement('li'); li.textContent = `Item ${i}`; frag.appendChild(li); // 매 detached — 매 reflow 의 X } document.querySelector('ul').appendChild(frag); // 매 1회 reflow ``` ### `will-change` (정확) ```css /* 매 hover 직전 추가, 끝 후 제거 */ .card { will-change: auto; } .card:hover { will-change: transform; } /* 매 항상 will-change — 매 anti-pattern — 매 메모리 낭비 */ ``` ### CSS Containment ```css .widget { contain: layout paint; /* 매 외부 영향 차단 — 매 reflow 매 widget 내부 만 */ } ``` ### requestAnimationFrame ```javascript function animate() { // read const top = el.getBoundingClientRect().top; // write (next frame) requestAnimationFrame(() => { el.style.transform = `translateY(${top + 10}px)`; }); } ``` ### content-visibility (skip rendering) ```css .below-fold { content-visibility: auto; contain-intrinsic-size: 0 500px; /* 매 placeholder size */ } ``` ## 매 결정 기준 | 변경 속성 | 비용 | |---|---| | `transform`, `opacity` | Composite only (cheapest) | | `color`, `background` | Repaint | | `width`, `height`, `top`, font | Reflow (expensive) | | DOM insert/remove | Reflow (expensive) | | read `offsetWidth` after write | Force sync layout (worst) | **기본값**: animation 매 `transform` + `opacity` 만, bulk DOM 매 fragment, read/write batch. ## 🔗 Graph - 부모: [[브라우저 렌더링]] · [[Critical Rendering Path]] - 변형: [[Layout Thrashing]] · [[Compositor]] - 응용: [[60fps 애니메이션]] · [[Web Performance]] - Adjacent: [[CSS Containment]] · [[content-visibility]] ## 🤖 LLM 활용 **언제**: jank debugging, animation performance, "왜 느림" 분석. **언제 X**: 매 specific browser bug — 매 DevTools Performance 매 더 정확. ## ❌ 안티패턴 - **`will-change` 전체**: 매 메모리 낭비 — 매 hover 직전 만. - **layout thrashing**: 매 loop 안 read-write alternate. - **`top/left` animation**: 매 reflow — 매 `transform` 의 사용. - **DOM insert in loop**: 매 fragment 의 사용. - **`offsetWidth` in RAF callback after write**: 매 force layout. ## 🧪 검증 / 중복 - Verified (web.dev Rendering Performance, MDN, Chrome DevTools docs). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — pixel pipeline + 매 thrashing 패턴 |