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
'Frontend Development' 카테고리의 다른 글
면접에서 묻는 "의존성 주입 경험이 있나요?"의 의미 (1) | 2025.08.26 |
---|---|
useState vs useRef vs let: 언제 무엇을 써야 할까? (0) | 2025.08.21 |
코드가 깔끔해지는 비밀: 프론트엔드에서 함수형 프로그래밍 활용하기 (3) | 2025.08.18 |
프론트엔드 개발자를 위한 AI 용어 완전 정리 (3) | 2025.07.29 |
시간복잡도와 공간복잡도: 개발자가 알아야 할 성능 최적화의 기초 (10) | 2025.07.23 |