---
id: wiki-2026-0508-critical-rendering-path
title: Critical Rendering Path (CRP)
category: 10_Wiki/Topics
status: verified
canonical_id: self
aliases: [CRP, critical rendering path, browser pipeline, DOM, CSSOM, render tree, reflow, repaint, compositing]
duplicate_of: none
source_trust_level: A
confidence_score: 0.95
verification_status: applied
tags: [web-performance, browser, dom, cssom, render-tree, reflow, repaint, compositing, frontend-engineering]
raw_sources: []
last_reinforced: 2026-05-10
github_commit: pending
tech_stack:
  language: web standards
  framework: any browser
---

# Critical Rendering Path

## 매 한 줄
> **"매 byte → 매 pixel 의 pipeline"**. 매 DOM + CSSOM → 매 Render Tree → 매 Layout → 매 Paint → 매 Composite. 매 web performance 의 fundamental. 매 each step 의 understand → 매 optimize 의 right tool.

## 매 핵심 step

### 1. DOM (Document Object Model)
- 매 HTML 의 incremental parse.
- 매 byte → token → node → tree.
- 매 partial 도 OK.

### 2. CSSOM (CSS Object Model)
- 매 CSS 의 parse.
- 매 render-blocking (vs DOM 의 incremental).
- 매 FOUC 방지.

### 3. Render Tree
- 매 DOM + CSSOM 의 결합.
- 매 visible element 만 (display:none 의 X).

### 4. Layout (Reflow)
- 매 geometry 계산 (position, size).
- 매 expensive — 매 entire subtree 의 trigger.

### 5. Paint (Repaint)
- 매 pixel 의 color / shadow.
- 매 layer per layer.

### 6. Compositing
- 매 layer 의 GPU 의 combine.
- 매 transform / opacity 의 composite-only.

### Render-blocking resources
- **CSS** (default).
- **`<script>` (sync)**.
- → 매 inline / async / defer 로 매 mitigate.

### 매 optimization

#### CSS
- **Inline critical CSS**: 매 above-fold.
- **`media="print"`** for print CSS.
- **`async` for non-critical**: deferred.
- **PurgeCSS / Tree-shake**: unused 제거.

#### JavaScript
- **`async`**: 매 download parallel + 매 execute when ready.
- **`defer`**: 매 download parallel + 매 execute after DOM.
- **Module split**: code splitting.
- **Inline critical**: small.

#### Image
- **Lazy loading**: `loading="lazy"`.
- **Modern format**: AVIF, WebP.
- **Priority hint**: `fetchpriority="high"`.
- **Responsive**: srcset, sizes.

#### Font
- **`font-display: swap`** (or optional).
- **Preload**: `<link rel="preload">`.
- **Subset**: 매 unused glyph drop.

#### Network
- **HTTP/2 push** (deprecated, use 103 Early Hints).
- **HTTP/3**: 매 quic.
- **CDN**: 매 edge.
- **Preconnect**: 매 third-party.

### 매 anti-pattern (avoid)
- 매 layout property animate (top, left, width, height).
- 매 sync DOM measurement after write (forced reflow).
- 매 `document.write` (legacy).
- 매 huge inline CSS / JS.

### 매 measurement
- DevTools Performance tab.
- Lighthouse CRP report.
- web-vitals (LCP, FCP).

## 💻 패턴

### Async / defer JS
```html
<!-- 매 critical inline -->
<script>
  // 매 inline + small + critical
  document.documentElement.classList.add('js-enabled');
</script>

<!-- 매 third-party analytics: async -->
<script async src="/analytics.js"></script>

<!-- 매 main app JS: defer -->
<script defer src="/app.js"></script>

<!-- 매 ❌ Avoid sync (default) -->
<script src="/legacy.js"></script>
```

### Critical CSS inline
```html
<head>
  <style>
    /* 매 above-fold critical */
    body { font-family: system-ui; }
    .hero { display: flex; height: 60vh; }
  </style>
  
  <!-- 매 rest async -->
  <link rel="preload" href="/main.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
  <noscript><link rel="stylesheet" href="/main.css"></noscript>
</head>
```

### Preload + preconnect
```html
<head>
  <!-- 매 critical asset preload -->
  <link rel="preload" href="/hero.avif" as="image" fetchpriority="high">
  <link rel="preload" href="/inter.woff2" as="font" type="font/woff2" crossorigin>
  
  <!-- 매 third-party preconnect -->
  <link rel="preconnect" href="https://api.example.com">
  <link rel="dns-prefetch" href="https://cdn.example.com">
</head>
```

### Avoid forced reflow
```js
// 매 ❌ Bad — 매 reflow per iteration
for (const el of items) {
  el.style.width = el.offsetWidth + 10 + 'px';  // 매 read + write
}

// 매 ✅ Good — 매 batch read, then batch write
const widths = items.map(el => el.offsetWidth);  // 매 read all
items.forEach((el, i) => {
  el.style.width = widths[i] + 10 + 'px';  // 매 write all
});
```

### Use transform / opacity (composite only)
```css
/* 매 ❌ reflow */
.modal-bad { transition: top 200ms, width 200ms; }

/* 매 ✅ composite only */
.modal-good { transition: transform 200ms, opacity 200ms; }
.modal-good.show { transform: translateY(0); opacity: 1; }
.modal-good.hide { transform: translateY(-20px); opacity: 0; }
```

### will-change hint (sparingly)
```css
.about-to-animate { will-change: transform; }

/* 매 매 animation 완료 시 의 remove */
```

### Image priority + lazy
```html
<!-- 매 LCP candidate (above-fold) -->
<img src="/hero.avif" alt="Hero" 
     width="1200" height="600"
     fetchpriority="high">

<!-- 매 below-fold -->
<img src="/below.avif" alt="Below" 
     width="800" height="600"
     loading="lazy"
     decoding="async">
```

### Resource hint priority
```html
<!-- 매 preload (high priority) -->
<link rel="preload" href="/critical.css" as="style">

<!-- 매 prefetch (next page hint) -->
<link rel="prefetch" href="/next-page.html">

<!-- 매 modulepreload -->
<link rel="modulepreload" href="/module.js">
```

### Lighthouse CRP analysis
```bash
npx lighthouse https://example.com --only-categories=performance \
  --output=json --output-path=./report.json

# 매 매 critical-request-chains 의 review
node -e "
  const r = require('./report.json');
  console.log(JSON.stringify(r.audits['critical-request-chains'], null, 2));
"
```

### DevTools Performance trace
```
1. Open DevTools → Performance tab.
2. Click record + reload page.
3. Stop after page loaded.
4. Look at Main thread:
   - Long tasks (>50ms).
   - Layout thrashing (purple bars).
   - Paint cost (green).
5. Identify blocking resources.
```

### Server-Timing (server contribution)
```js
// 매 Express middleware
app.use((req, res, next) => {
  const start = Date.now();
  res.on('finish', () => {
    res.set('Server-Timing', `total;dur=${Date.now() - start}`);
  });
  next();
});

// 매 client 의 visible
performance.getEntriesByType('navigation')[0].serverTiming;
```

### HTTP 103 Early Hints
```http
HTTP/2 103 Early Hints
Link: </critical.css>; rel=preload; as=style
Link: </main.js>; rel=preload; as=script

HTTP/2 200 OK
Content-Type: text/html
...
```

→ 매 server response 전 의 hint.

## 🤔 결정 기준
| 상황 | Approach |
|---|---|
| Above-fold | Inline critical CSS + preload hero image |
| Below-fold | Lazy load |
| Animations | transform + opacity only |
| Third-party | Async + preconnect |
| Heavy JS | Defer + code split |
| LCP slow | Preload + SSR + CDN |
| Layout shift | Image dimensions + aspect-ratio |

**기본값**: 매 inline critical + 매 defer rest + 매 modern image + 매 transform animations.

## 🔗 Graph
- 부모: [[Web-Performance]] · [[Browser]] · [[Frontend]]
- 변형: [[Reflow]] · [[Repaint]] · [[Compositing]] · [[Render Tree]]
- 응용: [[Core Web Vitals Optimization (INP, LCP 개선)]] · [[CSS Animations]] · [[Container_Queries]]
- Adjacent: [[Bottlenecks]] · [[Chrome DevTools 메모리 프로파일링]] · [[Baseline (Web Platform Features)]] · [[Case-Study-Allbirds-PWA-Redesign]]

## 🤖 LLM 활용
**언제**: 매 perf audit. 매 frontend optimization. 매 LCP / FCP 의 fix.
**언제 X**: 매 backend (다른 model).

## ❌ 안티패턴
- **Sync `<script>` in head**: 매 render block.
- **Many CSS files**: 매 multiple round-trip.
- **Layout property animate**: 매 jank.
- **Forced reflow loop** (read-write-read): 매 layout thrashing.
- **No image dimension**: 매 CLS.
- **`document.write`**: 매 deprecated, blocking.
- **Block on font** (no swap): 매 invisible text.

## 🧪 검증 / 중복
- Verified (Ilya Grigorik "High Performance Browser Networking", web.dev CRP).
- 신뢰도 A.
- Related: [[Core Web Vitals Optimization (INP, LCP 개선)]] · [[CSS Animations]] · [[Container_Queries]] · [[Baseline (Web Platform Features)]] · [[Bottlenecks]].

## 🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — 6 step + 매 inline / preload / async / defer / 103 code |
