핵심 요약
2024년 3월부터 INP가 FID를 대체. 2026년 시점 INP 최적화는 SEO·UX의 baseline. Speculation Rules는 사용자가 클릭하기 전에 다음 페이지를 미리 받아 거의 0초 전환을 만든다.
- INP 좋음: ≤ 200ms / 개선 필요: 200~500ms / 나쁨: > 500ms
- Speculation Rules: Chrome 121+ (2024-01)부터 안정
- 최신 트렌드: View Transitions API + INP + 이미지 최적화 종합
1. INP 측정·디버깅
// web-vitals 라이브러리
import { onINP } from 'web-vitals'
onINP((metric) => {
console.log('INP:', metric.value, 'ms')
// 분석 도구로 전송
})Chrome DevTools Performance
- Interaction 트랙으로 가장 느린 인터랙션 식별
- script 작업이 50ms 넘는 것 찾기 (Long Tasks)
- main thread blocking 추적
2. INP 주요 원인 5가지
1) 무거운 JS 핸들러
// ❌ 동기 무거운 작업
button.addEventListener('click', () => {
const result = heavyComputation(data) // 200ms
render(result)
})
// ✅ scheduler.yield() 또는 setTimeout으로 yield
button.addEventListener('click', async () => {
await scheduler.yield() // 메인 스레드 양보
const result = await heavyComputation(data)
render(result)
})2) 큰 React 트리 재렌더
// ❌ 모든 리스트 재렌더
function List({ items }) {
return items.map(i => <Item data={i} />)
}
// ✅ memoization + virtualization
import { memo } from 'react'
import { FixedSizeList } from 'react-window'
const Item = memo(({ data }) => ...)
function List({ items }) {
return (
<FixedSizeList height={600} itemCount={items.length} itemSize={50}>
{({ index }) => <Item data={items[index]} />}
</FixedSizeList>
)
}3) 동기 네트워크 호출
// ❌ navigator.sendBeacon에 큰 동기 데이터
// ✅ 작은 단위로 분할 + Background Tasks API
async function trackEvent(data) {
if ('requestIdleCallback' in window) {
requestIdleCallback(() => {
navigator.sendBeacon('/track', JSON.stringify(data))
})
}
}4) Layout Thrashing
// ❌ read-write-read-write
for (const el of elements) {
const h = el.offsetHeight // read (forces layout)
el.style.height = (h + 10) + 'px' // write
// 다음 read가 layout을 다시 트리거
}
// ✅ batch read first, then write
const heights = elements.map(el => el.offsetHeight)
elements.forEach((el, i) => {
el.style.height = (heights[i] + 10) + 'px'
})5) 큰 이미지 디코딩
<img
src="photo.jpg"
decoding="async"
loading="lazy"
width="800" height="600"
/>3. Speculation Rules — 사전 로드
<script type="speculationrules">
{
"prerender": [
{
"source": "list",
"urls": ["/popular", "/about"]
},
{
"source": "document",
"where": { "href_matches": "/article/*" },
"eagerness": "moderate"
}
]
}
</script>eagerness 옵션:
- immediate: 페이지 로드 즉시
- eager: 호버·근접 시
- moderate: 약간의 호버·터치 후
- conservative: 거의 클릭 직전만
실측: 대부분 클릭이 거의 0초 전환. LCP 평균 70% 감소.
4. View Transitions API
// 페이지 전환 애니메이션
document.startViewTransition(() => {
updateDOMSomehow()
})/* CSS */
@view-transition {
navigation: auto;
}
::view-transition-old(root),
::view-transition-new(root) {
animation-duration: 0.3s;
}
.hero {
view-transition-name: hero-image;
}5. 이미지 최적화 체크리스트
- WebP·AVIF 사용 (PNG·JPG 대비 30~50% 작음)
- responsive: srcset + sizes 명시
- LCP 이미지에는 fetchpriority="high" + preload
- 나머지는 loading="lazy" + decoding="async"
- width·height 명시 (CLS 방지)
- placeholder (blur·shimmer)
<link rel="preload" as="image" href="hero.webp" fetchpriority="high" />
<img
src="hero.webp"
srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w"
sizes="(max-width: 600px) 100vw, 50vw"
width="1200" height="675"
fetchpriority="high"
alt="..."
/>6. JS 분할 — Dynamic Import
// 라우트 단위
const Heavy = lazy(() => import('./HeavyChart'))
// 인터랙션 시점
button.addEventListener('click', async () => {
const { generatePDF } = await import('./pdf-gen')
generatePDF()
})7. 폰트 최적화
<link rel="preload" href="Pretendard.woff2" as="font" type="font/woff2" crossorigin />
<style>
@font-face {
font-family: 'Pretendard';
src: url('Pretendard.woff2') format('woff2');
font-display: swap; /* 폰트 로드 전 fallback 즉시 표시 */
}
</style>8. 측정·모니터링
| 도구 | 용도 |
|---|---|
| PageSpeed Insights | 실측 + 진단 |
| WebPageTest | 네트워크별 시뮬레이션 |
| Lighthouse CI | PR 단위 회귀 감지 |
| Real User Monitoring (Sentry·Datadog) | 실제 사용자 INP·LCP |
9. 종합 효과 사례
| 지표 | before | after |
|---|---|---|
| INP | 340ms | 120ms |
| LCP | 2.4초 | 0.8초 |
| 전환율 | baseline | +18% |
| SEO 트래픽 | baseline | +24% (3개월 후) |
자주 묻는 질문
모든 페이지에 Speculation Rules 적용?고트래픽 링크에만. 모든 링크 prerender하면 서버 부하·낭비 큼.
INP 측정이 정확한 도구?
Real User Monitoring (실제 사용자 데이터). Lighthouse는 lab 환경이라 실측과 다를 수 있음.

댓글 0