Core Web Vitals는 구글이 정한 실사용자 경험 지표다. 로딩(LCP)·반응성(INP)·시각 안정성(CLS) 세 축으로, 검색 순위에도 영향을 준다. 각 지표를 의미·기준·개선법으로 정리한다.
1. 세 지표 한눈에
각 지표는 Good / 개선 필요 / Poor 구간으로 평가되며, 보통 75 퍼센타일(하위 사용자 75%) 기준으로 측정한다.
2. LCP — Largest Contentful Paint (로딩)
뷰포트에서 가장 큰 콘텐츠 요소(이미지·비디오·큰 텍스트 블록)가 그려지는 시점. "주요 콘텐츠가 떴다"고 느끼는 순간을 잰다.
- 기준: Good ≤ 2.5초 / Poor > 4초
주요 원인
- 느린 서버 응답(TTFB)
- 렌더링 차단 리소스(CSS·JS)
- 느린 리소스 로딩(큰 이미지·폰트)
- 클라이언트 렌더링 지연
개선법
<!-- LCP 이미지에 우선순위 부여 -->
<link rel="preload" as="image" href="hero.webp" />
<img src="hero.webp" fetchpriority="high" />
- 서버 응답 단축(CDN·캐시), 렌더 차단 리소스 줄이기(critical CSS,
defer) - LCP 이미지 최적화: 포맷(WebP/AVIF), 적정 크기(
srcset),preload·fetchpriority="high" - 폰트 최적화(
font-display: swap, preload)
3. INP — Interaction to Next Paint (반응성)
사용자 상호작용(클릭·탭·키 입력)부터 그 결과가 화면에 그려질 때까지의 지연. 페이지 생애 동안 발생한 상호작용 중 가장 느린 축을 대표값으로 쓴다.
2024년 3월, 기존 FID(First Input Delay)를 공식 대체했다. FID는 "첫 입력의 지연"만 봤지만, INP는 모든 상호작용의 응답성을 보므로 더 엄격하다.
- 기준: Good ≤ 200ms / Poor > 500ms
주요 원인: 무거운 JavaScript가 메인 스레드를 오래 점유 → 입력 처리가 밀림(long task)
개선법
- 긴 작업을 쪼개기(time slicing), 불필요한 리렌더 줄이기
- 급하지 않은 업데이트를 낮은 우선순위로 — React라면
useTransition·useDeferredValue(Concurrent 글) - 무거운 연산은 Web Worker로 메인 스레드에서 분리
- 큰 리스트는 가상화(windowing)
// 무거운 목록 갱신을 낮은 우선순위로 → 입력 INP 보호
startTransition(() => setList(filterHugeList(query)));
4. CLS — Cumulative Layout Shift (시각 안정성)
예기치 않은 레이아웃 이동의 누적 점수. 로딩 중 콘텐츠가 갑자기 밀려 "버튼을 잘못 누르는" 경험을 수치화한다. 점수 = 이동 비율 × 영향 면적.
- 기준: Good ≤ 0.1 / Poor > 0.25 (시간이 아니라 점수)
주요 원인
- 크기 미지정 이미지·광고·iframe → 로드되며 아래 콘텐츠를 밀어냄
- 늦게 주입되는 콘텐츠(배너·동적 위젯)
- 웹폰트 교체 시 글자 크기 변화(FOUT)
개선법
<!-- 크기를 미리 예약해 공간 확보 -->
<img src="thumb.webp" width="320" height="180" />
- 이미지·비디오·광고 영역에 width/height 또는 aspect-ratio로 공간 예약
- 동적 콘텐츠는 자리(placeholder)를 미리 확보
- 폰트 교체 레이아웃 이동 최소화(
size-adjust, 적절한 fallback) - 애니메이션은 레이아웃을 바꾸는 속성 대신
transform사용 (렌더링 파이프라인 글)
5. 어떻게 측정하나 — Lab vs 필드
| 구분 | 도구 | 특징 |
|---|---|---|
| Lab 데이터 | Lighthouse, DevTools | 통제된 환경에서 1회 측정. 디버깅용 (단 INP는 상호작용이 필요해 lab에서 부정확) |
| 필드 데이터(RUM) | CrUX, web-vitals 라이브러리 | 실사용자의 실제 측정. 순위에 반영되는 진짜 값 |
import { onLCP, onINP, onCLS } from "web-vitals";
onLCP(console.log);
onINP(console.log);
onCLS(console.log); // 실제 사용자 값을 수집해 분석 도구로 전송
핵심: 개선은 lab으로 디버깅하되, 판단은 필드 데이터로 한다. 특히 INP는 실제 상호작용에서만 제대로 측정된다.
6. React / Next.js 관점
| 지표 | React/Next 도구 |
|---|---|
| LCP | next/image(자동 최적화·priority), next/font(폰트 preload), SSR/RSC로 초기 콘텐츠 빠르게 |
| INP | useTransition·useDeferredValue, 리렌더 최소화(불변성·구조적 공유), 코드 스플리팅 |
| CLS | next/image의 width/height 자동 지정, 스켈레톤 placeholder |
7. 정리
LCP(로딩, ≤2.5s)는 큰 콘텐츠를 빨리 — 서버·이미지·폰트 최적화. INP(반응성, ≤200ms)는 메인 스레드를 비워 — JS 쪼개기·
useTransition. CLS(안정성, ≤0.1)는 레이아웃을 흔들지 않게 — 크기 예약·transform. 측정은 lab으로 디버깅, 필드 데이터로 판단한다.