Frontend Development
Core Web Vitals: LCP, INP, CLS 개념과 개선 방법
Kun Woo Kim
2025. 8. 20. 17:48
728x90
의의와 배경
Core Web Vitals는 구글이 웹 사용자 경험을 정량화하기 위해 정의한 핵심 성능 지표다. 이 지표는 실제 사용자 경험을 직접적으로 반영하며 검색 순위(SEO)에도 영향을 미친다. 프론트엔드 개발자는 필수적으로 이해하고 지속적으로 관리해야 한다.
목차
- 개요: 지표 정의와 목표 기준
- LCP: 의미, 원인, 개선 체크리스트
- INP: 의미, 원인, 개선 체크리스트
- CLS: 의미, 원인, 개선 체크리스트
- 측정 방법: Lab vs Field, 실측 코드 예시
- 실무 팁/흔한 실수
- 체크리스트와 결론
개요
Core Web Vitals는 세 가지 지표로 구성된다.
지표 | 의미 | 권장 기준 | 주의 구간 |
---|---|---|---|
LCP (Largest Contentful Paint) | 가장 큰 콘텐츠가 화면에 보이기까지의 시간 | ≤ 2.5s | 2.5s ~ 4.0s |
INP (Interaction to Next Paint) | 사용자 상호작용 후 다음 페인트까지의 지연 | ≤ 200ms | 200ms ~ 500ms |
CLS (Cumulative Layout Shift) | 예기치 않은 레이아웃 이동의 누적량 | ≤ 0.1 | 0.1 ~ 0.25 |
LCP: Largest Contentful Paint
- 정의: 최초 뷰포트 내 가장 큰 텍스트·이미지·포스터 이미지가 렌더링되기까지의 시간
- 악화 요인: 높은 TTFB, 대용량 히어로 이미지, 차단적인 CSS/JS, 비효율적인 폰트 로드
개선 체크리스트
- 서버/네트워크: CDN 사용, 정적 캐시, 압축(gzip/br) 적용, HTTP/2·3 활성화, 초기 HTML의 TTFB를 낮춘다.
- 리소스 우선순위: 핵심 CSS 인라인, 비핵심 CSS 지연, 크리티컬 이미지
preload
를 적용한다. - 이미지 최적화: 포맷(AVIF/WebP)과 크기를 최적화하고
srcset/sizes
를 사용한다. 히어로 이미지는 지연 로드를 피한다. - 폰트 전략:
font-display: swap
을 적용하고 필요한 글자만 서브셋 처리하며 폰트를preload
한다.
<link rel="preload" as="image" href="/hero.jpg" imagesrcset="/hero.jpg 1x, /hero@2x.jpg 2x" imagesizes="100vw">
<link rel="preload" as="font" href="/fonts/inter.woff2" type="font/woff2" crossorigin>
/* FOIT 방지용 폰트 표시 전략 */
@font-face {
font-family: 'Inter';
src: url('/fonts/inter.woff2') format('woff2');
font-display: swap;
}
INP: Interaction to Next Paint
- 정의: 페이지 체류 동안 발생한 상호작용(클릭·탭·키 입력)의 지연 중 최악값에 가까운 단일 값
- 악화 요인: 메인 스레드 장기 점유(Long Task), 동기 JS, 무거운 이벤트 핸들러, 레이아웃 스래싱
개선 체크리스트
- JS 예산 관리: 번들을 축소하고 코드 분할과 사용 시점 로딩(온디맨드)을 적용한다.
- 이벤트 핸들러 최적화: 작업을 분할하고 우선순위가 낮은 작업은
setTimeout(0)
또는requestIdleCallback
로 위임한다. - 레이아웃 최적화: 읽기·쓰기 연산을 배치하고 스타일 계산을 최소화하며 애니메이션에는
transform/opacity
를 우선 사용한다. - Long Task 감지: DevTools Performance로 50ms 초과 작업을 식별하여 분할한다.
// 긴 배열 처리 시 이벤트 반응성을 확보하기 위한 단순 작업 분할 예시
function processInChunks<T>(items: T[], chunkSize: number, handle: (chunk: T[]) => void) {
for (let i = 0; i < items.length; i += chunkSize) {
const chunk = items.slice(i, i + chunkSize);
setTimeout(() => handle(chunk), 0);
}
}
CLS: Cumulative Layout Shift
- 정의: 예기치 못한 레이아웃 이동의 누적 정도
- 악화 요인: 크기가 지정되지 않은 이미지·영상, 동적 콘텐츠 삽입, 광고·위젯, 지연된 웹폰트 로드로 인한 폰트 스왑
개선 체크리스트
- 고정 공간 확보: 이미지·비디오에
width/height
또는aspect-ratio
를 명시한다. - 동적 요소 자리 확보: 광고·위젯 컨테이너 높이를 예약하고 콘텐츠 로딩 전 스켈레톤을 제공한다.
- 폰트 로드 전략:
font-display: swap
을 적용하고 FOUT를 허용하며 폴백 폰트는 메트릭 유사 폰트를 사용한다. - 애니메이션 가이드: 레이아웃을 변경하는
top/left/width/height
대신transform
을 사용한다.
/* 레이아웃 이동 방지 */
img {
aspect-ratio: 16 / 9;
width: 100%;
height: auto;
display: block;
}
측정 방법: 실험실(Lab) vs 실제(Field)
- 실험실(Lab) 데이터: Lighthouse, DevTools(성능 탭)로 시뮬레이션 측정한다. 재현성이 높다.
- 실제(Field) 데이터, RUM: 실제 사용자 환경에서 수집한다. 사용자 네트워크와 디바이스 특성을 반영한다.
실측 데이터 수집(React 예시)
npm i web-vitals
// vitals.ts
import { onCLS, onINP, onLCP, Metric } from 'web-vitals';
export function initWebVitals(report: (metric: Metric) => void) {
onCLS(report);
onINP(report);
onLCP(report);
}
// App.tsx
import { useEffect } from 'react';
import { initWebVitals } from './vitals';
export default function App() {
useEffect(() => {
initWebVitals((metric) => {
// 서버로 전송하여 대시보드로 집계한다.
navigator.sendBeacon('/analytics', JSON.stringify({
name: metric.name,
value: metric.value,
rating: metric.rating,
}));
});
}, []);
return (
<div>...</div>
);
}
실무 적용 팁
- 이미지: 히어로 이미지는
preload
와 적절한 포맷(AVIF/WebP)을 적용하고, 비핵심 이미지는 지연 로드한다. - 폰트: 핵심 폰트만 선택적으로
preload
하고font-display: swap
을 적용한다. 폰트 종류와 가중치는 최소화한다. - 번들: 라우트 단위 코드 분할과 사용 시점 로딩을 적용하고, 타사 스크립트 예산을 관리한다.
- 캐싱: 정적 에셋은 장기 캐시하고, HTML은 짧은 TTL과 재검증 정책을 사용한다.
- 네트워크 힌트:
preconnect
와dns-prefetch
로 외부 리소스 초기 지연을 줄인다.
<link rel="preconnect" href="https://example-cdn.com" crossorigin>
<link rel="dns-prefetch" href="https://example-cdn.com">
흔한 실수
- 이미지·비디오 크기를 지정하지 않아 CLS가 증가한다.
- 초기 렌더에 불필요한 스크립트를 포함해 INP와 LCP가 악화된다.
- 모든 폰트를 무분별하게
preload
하여 네트워크 병목을 유발한다. - 크리티컬 CSS 없이 거대한 CSS를 한 번에 로드한다.
체크리스트
- 히어로 이미지 포맷·크기를 최적화하고
preload
를 적용했다. - 핵심 CSS는 인라인하고 비핵심 CSS는 지연 로드했다.
- 폰트에
preload
와font-display: swap
을 적용하고 서브셋을 구성했다. - 이미지·동영상에
width/height
또는aspect-ratio
를 지정했다. - 라우트 단위 코드 분할을 적용하고 타사 스크립트 예산을 수립했다.
- RUM 수집(web-vitals)으로 실제 사용자 데이터를 확인했다.
결론: 개념 정리와 적용 가이드
- 핵심: LCP(로딩), INP(응답성), CLS(시각 안정성)는 서로 다른 관점에서 동시에 관리해야 한다.
- 적용 순서:
1) TTFB와 히어로 리소스를 최적화하여 LCP를 개선한다.
2) 코드 분할과 작업 분할로 INP를 개선한다.
3) 크기 예약과 폰트 전략으로 CLS를 개선한다.
4) RUM 수집으로 실제 사용자 기준을 확인하고 회귀를 방지한다.
728x90