"사이트가 느려요"라는 피드백은 세 가지 다른 문제를 의미할 수 있습니다. 페이지를 열었는데 주요 콘텐츠가 한참 후에야 보인다면 로딩이 느린 것이고, 버튼을 클릭했는데 반응이 없거나 지연된다면 상호작용이 느린 것이며, 스크롤하다가 광고나 이미지가 갑자기 나타나면서 읽던 부분이 밀린다면 시각적으로 불안정한 것입니다.
Google은 이 세 가지 문제를 측정하기 위해 Core Web Vitals를 정의했습니다. 각각 LCP, INP, CLS라는 지표로, 로딩 성능, 상호작용 응답성, 시각적 안정성을 측정합니다.
Core Web Vitals란?
Google이 정의한 핵심 사용자 경험 지표입니다. 검색 순위에 반영되며, PageSpeed Insights와 Chrome UX Report에서 실제 사용자 데이터를 기반으로 측정됩니다. 2024년 3월부터 LCP, INP, CLS 세 가지가 Core Web Vitals입니다.
Core Web Vitals 개요
Core Web Vitals는 사용자 경험의 세 가지 핵심 측면을 측정합니다.
| 지표 | 측정 대상 | Good 기준 | 의미 |
|---|---|---|---|
| LCP (Largest Contentful Paint) | 로딩 성능 | 2.5초 이하 | 주요 콘텐츠가 보이는 시점 |
| INP (Interaction to Next Paint) | 상호작용 응답성 | 200ms 이하 | 클릭/탭에 얼마나 빨리 반응하는가 |
| CLS (Cumulative Layout Shift) | 시각적 안정성 | 0.1 이하 | 페이지가 얼마나 덜컹거리는가 |
2024년 3월, INP가 기존의 FID(First Input Delay)를 대체하며 Core Web Vitals로 정식 채택되었습니다. FID는 첫 번째 상호작용의 입력 지연만 측정했지만, INP는 페이지 생명주기 전체에서 발생하는 모든 상호작용의 응답성을 측정합니다.
Core Web Vitals는 Google 검색 순위에 영향을 미칩니다. 2021년부터 페이지 경험 시그널의 일부로 포함되었으며, 동일한 콘텐츠 품질이라면 Core Web Vitals가 좋은 페이지가 더 높은 순위를 받을 수 있습니다.
75th percentile 기준
Core Web Vitals는 75번째 백분위수를 기준으로 평가됩니다. 즉, 방문자의 75%가 해당 기준을 충족해야 "Good"으로 평가됩니다. 상위 25%의 느린 사용자도 허용 가능한 경험을 제공해야 한다는 의미입니다.
LCP (Largest Contentful Paint)
LCP란 무엇인가
LCP는 뷰포트 내에서 가장 큰 콘텐츠 요소가 렌더링되는 시점을 측정합니다. 사용자가 "페이지가 로드되었다"고 느끼는 순간이 바로 이 시점인데, 로고나 텍스트 조각 같은 작은 요소가 아니라 화면의 주요 콘텐츠가 눈에 들어오는 순간이 핵심입니다.
LCP 후보가 되는 요소는 다음과 같습니다:
<img>요소<svg>내부의<image>요소<video>요소의 포스터 이미지- CSS
background-image로 로드된 이미지 - 텍스트 노드를 포함하는 블록 레벨 요소
브라우저는 페이지 로드 과정에서 가장 큰 요소를 계속 추적하다가, 사용자가 첫 상호작용(클릭, 스크롤, 키 입력)을 하면 그 시점의 가장 큰 요소를 LCP로 확정합니다.
권장 기준
| 등급 | 시간 |
|---|---|
| Good | 2.5초 이하 |
| Needs Improvement | 2.5초 ~ 4.0초 |
| Poor | 4.0초 초과 |
LCP 최적화 전략
LCP는 TTFB(서버 응답), 리소스 로드 시간, 렌더링 차단, 클라이언트 렌더링이라는 네 가지 요소로 구성되며, 각 요소를 개별적으로 최적화해야 전체 LCP가 개선됩니다.
LCP 요소가 이미지라면, 해당 이미지의 다운로드 시간이 LCP에 직접적으로 영향을 미칩니다.
preload로 LCP 이미지 우선 로드
브라우저는 HTML을 파싱하면서 이미지를 발견하지만, CSS나 JavaScript를 먼저 처리하느라 이미지 다운로드가 지연될 수 있습니다. preload를 사용하면 이미지를 조기에 발견하고 높은 우선순위로 다운로드합니다.
<head>
<!-- LCP 이미지를 가장 높은 우선순위로 로드 -->
<link rel="preload" as="image" href="/hero-image.webp" />
</head>CDN 활용
사용자와 물리적으로 가까운 서버에서 이미지를 제공하면 다운로드 시간이 단축됩니다. Cloudflare, Vercel Edge, AWS CloudFront 같은 CDN을 사용하세요.
최신 이미지 포맷 사용
WebP는 JPEG 대비 25-35% 작고, AVIF는 50%까지 작습니다. <picture> 태그로 브라우저 지원에 따라 최적의 포맷을 제공합니다.
<picture>
<source srcset="/hero.avif" type="image/avif" />
<source srcset="/hero.webp" type="image/webp" />
<img src="/hero.jpg" alt="Hero" fetchpriority="high" />
</picture>fetchpriority="high"는 브라우저에게 이 리소스를 높은 우선순위로 가져오라고 지시합니다.
INP (Interaction to Next Paint)
INP란 무엇인가
INP는 사용자가 페이지와 상호작용할 때 화면이 얼마나 빨리 반응하는지를 측정합니다. 클릭, 탭, 키 입력 등 모든 상호작용에서 입력 시점부터 다음 화면 업데이트까지 걸리는 시간을 추적하여, 페이지의 전반적인 반응성을 평가합니다.
INP는 상호작용의 세 단계를 측정합니다:
Input Delay
클릭 → 핸들러 시작
Processing Time
핸들러 실행
Presentation Delay
핸들러 종료 → 화면 반영
Input Delay
클릭 → 핸들러 시작
Processing Time
핸들러 실행
Presentation Delay
핸들러 종료 → 화면 반영
Input Delay는 사용자가 클릭한 시점부터 이벤트 핸들러가 실행되기까지의 지연입니다. 메인 스레드가 다른 작업에 묶여 있을수록 이 시간이 늘어납니다. Processing Time은 이벤트 핸들러의 실행 시간으로, 핸들러 코드가 복잡할수록 오래 걸립니다. Presentation Delay는 핸들러 완료 후 다음 프레임이 화면에 그려지기까지의 시간이며, 렌더링 작업이 무거우면 지연됩니다.
페이지 생명주기 동안 발생한 모든 상호작용을 측정하고, 가장 느린 것(또는 상호작용이 많을 경우 98번째 백분위수)을 INP로 보고합니다.
권장 기준
| 등급 | 시간 |
|---|---|
| Good | 200ms 이하 |
| Needs Improvement | 200ms ~ 500ms |
| Poor | 500ms 초과 |
INP 최적화 전략
INP를 개선하려면 Input Delay, Processing Time, Presentation Delay 세 단계를 각각 최적화해야 하며, 아래에서 단계별 전략을 살펴봅니다.
메인 스레드가 50ms를 초과하는 작업(Long Task)에 묶여 있으면, 그동안 사용자 입력을 처리할 수 없습니다. Long Task를 작은 조각으로 분할하면 Input Delay가 감소합니다.
// ❌ 메인 스레드를 오래 점유: Long Task 발생
function processLargeArray(items) {
items.forEach(item => heavyProcessing(item));
}
// ✅ 작업 분할로 메인 스레드 양보
async function processLargeArray(items) {
for (const item of items) {
heavyProcessing(item);
// 주기적으로 메인 스레드에 제어권 반환
if ('scheduler' in window && 'yield' in scheduler) {
await scheduler.yield();
} else {
await new Promise(resolve => setTimeout(resolve, 0));
}
}
}scheduler.yield()는 Chrome 115+(2023년)에서 지원하는 API로, 메인 스레드에 제어권을 양보하면서도 다른 작업보다 높은 우선순위로 재개됩니다.
React에서의 Long Task 분할
React 18의 startTransition을 사용하면 상태 업데이트를 낮은 우선순위로 처리하여 사용자 상호작용을 차단하지 않습니다.
import { startTransition } from 'react';
function handleSearch(query) {
// 긴급하지 않은 업데이트를 transition으로 표시
startTransition(() => {
setSearchResults(computeResults(query));
});
}CLS (Cumulative Layout Shift)
CLS란 무엇인가
CLS는 페이지 로드 중에 예기치 않게 레이아웃이 이동한 정도를 측정합니다. 기사를 읽다가 갑자기 광고가 삽입되면서 읽던 부분이 밀려나거나, 버튼을 클릭하려는 순간 위치가 바뀌는 경험을 수치화한 것입니다.
각 레이아웃 이동의 점수는 다음과 같이 계산됩니다:
Layout Shift Score = Impact Fraction × Distance Fraction
- Impact Fraction: 이동 전후로 영향받은 뷰포트 영역의 비율
- Distance Fraction: 요소가 이동한 거리 / 뷰포트 높이
예를 들어, 뷰포트의 50%를 차지하는 요소가 뷰포트 높이의 25%만큼 아래로 밀리면:
0.5 × 0.25 = 0.125
CLS는 페이지 생명주기 동안 발생한 모든 레이아웃 이동 중, 세션 윈도우 내 최대 점수를 보고합니다. 세션 윈도우는 1초 미만의 간격으로 발생한 레이아웃 이동들의 묶음입니다.
사용자 입력에 의한 이동은 제외
클릭이나 키 입력 후 500ms 이내에 발생한 레이아웃 이동은 CLS에 포함되지 않습니다. 사용자가 예상할 수 있는 변화이기 때문입니다. 문제가 되는 것은 예기치 않은 이동입니다.
권장 기준
| 등급 | 점수 |
|---|---|
| Good | 0.1 이하 |
| Needs Improvement | 0.1 ~ 0.25 |
| Poor | 0.25 초과 |
CLS 발생 원인
CLS가 발생하는 주요 원인을 살펴보면, 먼저 크기가 지정되지 않은 이미지나 비디오가 있습니다. 이미지가 로드되면서 갑자기 공간을 차지하기 때문입니다. 동적으로 삽입되는 콘텐츠(광고, 배너, 동의 팝업 등)도 흔한 원인이며, 웹폰트 로드로 인해 텍스트 크기가 변하는 FOUT 현상도 레이아웃 이동을 유발합니다. 마지막으로 top, left, width, height 같은 레이아웃 속성을 직접 조작하는 애니메이션도 CLS의 원인이 됩니다.
CLS 최적화 전략
이미지나 비디오의 크기를 명시하지 않으면, 브라우저는 리소스가 로드된 후에야 크기를 알 수 있습니다. 로드 전에는 0×0으로 렌더링하다가, 로드 후 실제 크기로 확장되면서 레이아웃 이동이 발생합니다.
<!-- ❌ 크기 미지정: 로드 후 레이아웃 이동 -->
<img src="hero.jpg" alt="Hero" />
<!-- ✅ 크기 지정: 공간 예약됨 -->
<img src="hero.jpg" alt="Hero" width="800" height="400" />width와 height 속성을 지정하면 브라우저가 이미지의 aspect ratio를 계산하여 공간을 미리 예약합니다. CSS에서 max-width: 100%를 적용해도 비율이 유지됩니다.
img {
max-width: 100%;
height: auto;
}CSS aspect-ratio 활용
동적인 크기의 이미지에는 CSS aspect-ratio를 사용합니다.
.video-container {
aspect-ratio: 16 / 9;
width: 100%;
}보조 지표
Core Web Vitals만으로는 전체 성능을 파악하기 어려울 때가 있습니다. 다음 보조 지표들을 함께 모니터링하면 문제의 원인을 더 정확히 진단할 수 있습니다.
FCP (First Contentful Paint)
FCP는 브라우저가 DOM의 첫 번째 콘텐츠를 화면에 렌더링하는 시점입니다. 빈 화면에서 무언가가 나타나는 전환점으로, 체감 로딩 속도에 큰 영향을 미칩니다.
| 등급 | 시간 |
|---|---|
| Good | 1.8초 이하 |
| Needs Improvement | 1.8초 ~ 3.0초 |
| Poor | 3.0초 초과 |
FCP는 LCP의 하한선입니다. LCP가 발생하려면 FCP가 먼저 발생해야 하고, FCP가 느리면 LCP도 느릴 수밖에 없습니다. FCP를 개선하면 LCP도 함께 개선되는 경우가 많습니다.
TBT (Total Blocking Time)
TBT는 FCP와 TTI 사이에 메인 스레드가 차단된 총 시간을 측정합니다. 50ms를 초과하는 Long Task에서 초과분만 합산하는 방식으로 계산됩니다.
Long Task 1: 80ms → 차단 시간: 80 - 50 = 30ms
Long Task 2: 120ms → 차단 시간: 120 - 50 = 70ms
TBT = 30 + 70 = 100ms
| 등급 | 시간 |
|---|---|
| Good | 200ms 이하 |
| Needs Improvement | 200ms ~ 600ms |
| Poor | 600ms 초과 |
TBT는 Lab에서 INP를 예측하는 프록시
INP는 실제 사용자 상호작용이 필요하므로 Lab 환경에서 직접 측정하기 어렵습니다. TBT는 메인 스레드 차단 상태를 측정하므로, TBT가 좋으면 INP도 좋을 가능성이 높습니다. 개발 중에는 TBT를 최적화하고, 배포 후에는 INP를 모니터링하는 전략이 효과적입니다.
TTFB (Time To First Byte)
TTFB는 브라우저가 서버에 요청을 보낸 후 첫 번째 바이트를 받기까지의 시간으로, 서버 응답 속도를 직접적으로 나타냅니다. 모든 지표의 출발점이기 때문에 TTFB가 느리면 나머지 지표도 연쇄적으로 영향을 받습니다.
| 등급 | 시간 |
|---|---|
| Good | 800ms 이하 |
| Needs Improvement | 800ms ~ 1800ms |
| Poor | 1800ms 초과 |
TTFB가 느리면 LCP와 FCP를 비롯한 모든 지표가 그만큼 지연되므로, CDN 활용, 서버 캐싱, 데이터베이스 쿼리 최적화에 집중해야 합니다.
레거시 지표
일부 지표는 더 이상 주요 성능 평가에 사용되지 않습니다.
더 이상 사용되지 않는 지표
TTI (Time To Interactive): Lighthouse 10.0(2022년)부터 점수 계산에서 제외되었습니다. 아웃라이어에 민감하고 Lab 환경에서만 측정 가능하다는 한계가 있었습니다. TBT와 INP가 상호작용 성능을 측정하는 데 사용됩니다.
FID (First Input Delay): 2024년 3월 INP로 대체되었습니다. FID는 첫 번째 상호작용만 측정했지만, INP는 페이지 전체 생명주기의 상호작용을 측정합니다.
TTI와 FID의 최적화 원칙(메인 스레드 부담 줄이기, Long Task 분할)은 여전히 유효합니다. 이 원칙들은 TBT와 INP 개선에도 동일하게 적용됩니다.
측정 도구
Lab 데이터 (개발 환경)
Lab 데이터는 제어된 환경에서 측정한 성능 지표입니다. 네트워크 속도와 디바이스 성능을 일정하게 유지하기 때문에 재현 가능한 결과를 얻을 수 있어 개발 중 최적화 작업에 적합합니다. Lighthouse는 Chrome DevTools에 내장되어 있고 CLI나 CI/CD에도 통합할 수 있어 가장 널리 사용됩니다. WebPageTest는 다양한 네트워크와 디바이스 조건을 시뮬레이션하며 필름스트립 분석 기능을 제공합니다. Chrome DevTools Performance 탭에서는 상세한 타임라인을 분석하고 Long Task를 식별할 수 있습니다.
Field 데이터 (실사용자)
Field 데이터는 실제 사용자의 브라우저에서 측정한 성능 지표로, 다양한 네트워크 환경과 디바이스 조건을 반영하여 실제 사용자 경험을 파악하는 데 적합합니다. Chrome UX Report(CrUX)는 실제 Chrome 사용자 데이터를 집계하며 BigQuery로 상세 분석이 가능합니다. PageSpeed Insights는 Lab 데이터와 Field 데이터를 통합하여 보여주므로 Core Web Vitals 현황을 빠르게 확인할 수 있습니다.
자체 서비스에서 직접 Field 데이터를 수집하고 싶다면, Google Chrome 팀에서 제공하는 web-vitals 라이브러리를 활용할 수 있습니다. 이어지는 섹션에서 이 라이브러리의 사용법과 실제 구현 예시를 살펴봅니다.
web-vitals로 직접 측정하기
외부 도구에 의존하지 않고 자체적으로 성능 지표를 수집하고 분석하고 싶을 때가 있습니다. Google Analytics나 PageSpeed Insights는 집계된 데이터만 제공하기 때문에, 특정 페이지나 사용자 세그먼트별로 세밀하게 분석하거나 실시간으로 모니터링하려면 직접 측정 시스템을 구축해야 합니다.
Google Chrome 팀에서 제공하는 web-vitals 라이브러리는 이러한 직접 측정을 위한 공식 솔루션입니다. 브라우저의 Performance API를 추상화하여 Core Web Vitals와 보조 지표를 간편하게 수집할 수 있으며, gzip 압축 후 약 1.5KB 정도로 매우 가볍습니다.
기본 사용법
라이브러리는 각 지표별로 콜백 함수를 등록하는 방식으로 동작합니다. 지표가 측정되면 해당 콜백이 호출되며, 이때 측정값과 함께 다양한 메타데이터를 받을 수 있습니다.
import { onLCP, onINP, onCLS, onFCP, onTTFB } from 'web-vitals';
// 각 지표가 측정될 때 콜백 실행
onLCP((metric) => {
console.log('LCP:', metric.value, 'ms');
// 자체 분석 서버로 전송
sendToAnalytics({ name: metric.name, value: metric.value, rating: metric.rating });
});
onINP((metric) => {
console.log('INP:', metric.value, 'ms');
sendToAnalytics({ name: metric.name, value: metric.value, rating: metric.rating });
});
onCLS((metric) => {
console.log('CLS:', metric.value);
sendToAnalytics({ name: metric.name, value: metric.value, rating: metric.rating });
});
onFCP((metric) => sendToAnalytics({ name: metric.name, value: metric.value }));
onTTFB((metric) => sendToAnalytics({ name: metric.name, value: metric.value }));콜백으로 전달되는 metric 객체에는 value(측정값), rating(good/needs-improvement/poor), delta(이전 값과의 차이), navigationType(페이지 진입 방식) 등의 정보가 포함됩니다. rating은 Google의 권장 기준에 따라 자동으로 계산되므로, 별도의 임계값 로직 없이도 지표의 상태를 바로 파악할 수 있습니다.
Attribution 빌드로 원인 분석하기
기본 빌드는 측정값만 제공하지만, web-vitals/attribution 빌드를 사용하면 왜 그 값이 나왔는지에 대한 상세 정보를 얻을 수 있습니다. 번들 사이즈가 약간 증가하지만, 성능 문제의 근본 원인을 파악하는 데 매우 유용합니다.
import { onLCP, onINP, onCLS } from 'web-vitals/attribution';
onLCP(({ value, attribution }) => {
console.log('LCP:', value, 'ms');
// LCP 요소가 무엇인지, 로드에 얼마나 걸렸는지 확인
console.log('LCP 요소:', attribution.element);
console.log('리소스 URL:', attribution.url);
console.log('리소스 로드 시간:', attribution.resourceLoadDuration);
console.log('렌더링 지연:', attribution.elementRenderDelay);
});
onINP(({ value, attribution }) => {
console.log('INP:', value, 'ms');
// 어떤 상호작용이 느렸는지 분석
console.log('상호작용 대상:', attribution.interactionTarget);
console.log('이벤트 타입:', attribution.interactionType);
console.log('Input Delay:', attribution.inputDelay);
console.log('Processing Time:', attribution.processingDuration);
console.log('Presentation Delay:', attribution.presentationDelay);
});
onCLS(({ value, attribution }) => {
console.log('CLS:', value);
// 어떤 요소가 가장 많이 이동했는지 확인
console.log('가장 큰 이동 요소:', attribution.largestShiftTarget);
console.log('이동 원인:', attribution.largestShiftSource);
});예를 들어, LCP가 느린 것을 발견했을 때 attribution 정보를 통해 "히어로 이미지가 LCP 요소이고, 리소스 로드에 2초가 걸렸다"는 것을 알 수 있습니다. 이러한 정보가 있어야 이미지 최적화나 preload 적용 같은 구체적인 개선 작업을 진행할 수 있습니다.
React/Next.js에서 사용하기
React 애플리케이션에서 web-vitals를 사용할 때는 컴포넌트 마운트 시점에 한 번만 초기화해야 합니다. 여러 번 초기화하면 중복 측정이 발생할 수 있으므로, 루트 레이아웃이나 전용 컴포넌트에서 관리하는 것이 좋습니다.
// components/vitals-collector.tsx
"use client";
import { useEffect } from "react";
import { onLCP, onINP, onCLS, onFCP, onTTFB } from "web-vitals";
export function VitalsCollector() {
useEffect(() => {
// 클라이언트 사이드에서만 실행
onLCP((metric) => sendToAnalytics(metric));
onINP((metric) => sendToAnalytics(metric));
onCLS((metric) => sendToAnalytics(metric));
onFCP((metric) => sendToAnalytics(metric));
onTTFB((metric) => sendToAnalytics(metric));
}, []);
return null;
}
// app/layout.tsx
import { VitalsCollector } from "@/components/vitals-collector";
export default function RootLayout({ children }) {
return (
<html>
<body>
<VitalsCollector />
{children}
</body>
</html>
);
}Next.js의 Pages Router에서는 reportWebVitals 함수를 _app.tsx에서 export하는 방식을 제공하지만, App Router에서는 이 방식이 지원되지 않습니다. 위와 같이 클라이언트 컴포넌트에서 직접 web-vitals를 사용하면 두 라우터 모두에서 일관된 방식으로 지표를 수집할 수 있습니다.
SPA에서의 측정 주의점
Next.js나 React Router를 사용하는 SPA에서는 클라이언트 사이드 네비게이션 시 실제 페이지 로드가 발생하지 않습니다. 따라서 LCP, FCP, TTFB 같은 로딩 관련 지표는 첫 페이지 로드나 새로고침 시에만 측정됩니다. INP와 CLS는 페이지 생명주기 동안 계속 측정되므로 SPA에서도 의미 있는 데이터를 얻을 수 있습니다.
실시간 대시보드 구현하기
수집한 지표를 저장하고 시각화하면 자체적인 성능 모니터링 대시보드를 구축할 수 있습니다. 외부 서비스 없이도 실시간으로 성능을 확인하고, 히스토리를 추적하며, rating 분포를 분석할 수 있습니다.
// lib/vitals-collector.ts
import { onLCP, onINP, onCLS, onFCP, onTTFB } from "web-vitals";
import type { Metric } from "web-vitals";
const STORAGE_KEY = "web-vitals-history";
const MAX_HISTORY = 100;
class VitalsCollector {
private metrics: Metric[] = [];
init() {
this.loadFromStorage();
onLCP((m) => this.addMetric(m));
onINP((m) => this.addMetric(m));
onCLS((m) => this.addMetric(m));
onFCP((m) => this.addMetric(m));
onTTFB((m) => this.addMetric(m));
}
private addMetric(metric: Metric) {
this.metrics.push({
...metric,
timestamp: Date.now(),
});
// 최대 개수 유지
if (this.metrics.length > MAX_HISTORY) {
this.metrics = this.metrics.slice(-MAX_HISTORY);
}
this.saveToStorage();
}
private loadFromStorage() {
const stored = localStorage.getItem(STORAGE_KEY);
if (stored) {
this.metrics = JSON.parse(stored);
}
}
private saveToStorage() {
localStorage.setItem(STORAGE_KEY, JSON.stringify(this.metrics));
}
getMetrics() {
return this.metrics;
}
}
export const vitalsCollector = new VitalsCollector();이렇게 수집된 데이터를 React 컴포넌트에서 시각화하면 됩니다. 메트릭별 히스토리 차트, rating 분포 도넛 차트, Navigation Type별 통계 등 다양한 방식으로 성능 데이터를 표현할 수 있습니다.
실제 구현 예시
이 사이트에도 web-vitals 라이브러리를 활용한 실시간 Web Vitals 대시보드가 구현되어 있습니다. 페이지를 탐색하면서 LCP, INP, CLS 등의 지표가 어떻게 수집되고 시각화되는지 직접 확인해 보세요.
정리
Core Web Vitals는 로딩, 상호작용, 시각적 안정성이라는 사용자 경험의 세 가지 핵심 측면을 측정하며, 이 지표들을 개선하면 검색 순위와 사용자 만족도 모두에 긍정적인 영향을 미칩니다.
핵심 요약
-
LCP (2.5초 이하): 주요 콘텐츠가 보이는 시점. 이미지 최적화, preload, SSR/SSG가 핵심입니다.
-
INP (200ms 이하): 상호작용 응답성. Long Task 분할, Web Worker 활용, 강제 리플로우 방지가 핵심입니다.
-
CLS (0.1 이하): 시각적 안정성. 이미지/비디오 크기 지정, 동적 콘텐츠 공간 예약, transform 애니메이션이 핵심입니다.
-
보조 지표: FCP(1.8초), TBT(200ms), TTFB(800ms)도 함께 모니터링하세요.
-
측정: 개발 중에는 Lighthouse로 Lab 데이터를 확인하고, 배포 후에는 web-vitals 라이브러리로 실제 사용자의 Field 데이터를 수집하세요. 이 사이트의 대시보드처럼 직접 모니터링 시스템을 구축할 수도 있습니다.
참고 자료
- web.dev - Core Web Vitals
- web.dev - Largest Contentful Paint (LCP)
- web.dev - Optimize LCP
- web.dev - Interaction to Next Paint (INP)
- web.dev - Optimize INP
- web.dev - Cumulative Layout Shift (CLS)
- web.dev - Optimize CLS
- web.dev - First Contentful Paint (FCP)
- web.dev - Total Blocking Time (TBT)
- web.dev - Time to First Byte (TTFB)
- web.dev - INP is a Core Web Vital
- GitHub - web-vitals library