useReportWebVitals - Next.js

백동우·2025년 8월 26일
post-thumbnail

용어 정리

TTFB (Time to First Byte): 서버가 첫 번째 응답 바이트를 보내기까지 걸리는 시간.

FCP (First Contentful Paint): 화면에 처음으로 텍스트나 이미지 같은 콘텐츠가 표시되는 시점.

LCP (Largest Contentful Paint): 화면에서 가장 큰 요소(텍스트 블록, 이미지 등)가 표시되는 시점.

FID (First Input Delay): 사용자가 처음으로 페이지와 상호작용(클릭, 탭 등)했을 때 응답하기까지 걸린 시간.

CLS (Cumulative Layout Shift): 페이지 로딩 중 레이아웃이 예기치 않게 움직이는 정도를 측정하는 지표.

INP (Interaction to Next Paint): 사용자가 입력(클릭, 키 입력 등)한 후 다음 화면 업데이트가 완료되기까지 걸린 시간.

결과값

정상 케이스 결과 값

  useReportWebVitals((m) => {
    // m.name: 'FCP' | 'LCP' | 'CLS' | 'INP' | 'TTFB' | 'FID' 등
    console.log(`[WebVitals] ${m.name}`, m.value, m);
  });
  <div>123</div>;
  
 ---- 콘솔 값  ---
{
    "name": "FCP",
    "value": 472,
    "rating": "good",
    "delta": 472,
    "entries": [
        {
            "name": "first-contentful-paint",
            "entryType": "paint",
            "startTime": 472,
            "duration": 0
        }
    ],
    "id": "v4-1756182372462-2445471802253",
    "navigationType": "reload"
}

에러케이스(수정 필요)

  const [show, setShow] = useState(false);
  useReportWebVitals((m) => {
    // m.name: 'FCP' | 'LCP' | 'CLS' | 'INP' | 'TTFB' | 'FID' 등
    console.log(`[WebVitals] ${m.name}`, m.value, m);
  });
  useEffect(() => {
    // 2초 뒤에야 첫 콘텐츠 노출
    const end = performance.now() + 2000;
    while (performance.now() < end) {} 
    setShow(true);
  }, []);

  return <div>{show ? <h1>첫 콘텐츠</h1> : null}</div>;
  
  
  
  {
    "name": "FCP",
    "value": 3460,
    "rating": "poor",
    "delta": 3460,
    "entries": [
        {
            "name": "first-contentful-paint",
            "entryType": "paint",
            "startTime": 3460,
            "duration": 0
        }
    ],
    "id": "v4-1756182424710-9327242888893",
    "navigationType": "reload"
}

타입 설명

interface Metric {
  //메트릭의 이름(약어 형태).
  name: "CLS" | "FCP" | "INP" | "LCP" | "TTFB";

  //메트릭의 현재 값.
  value: number;

  /**
   * 메트릭 값이 "좋음(good)", "개선 필요(needs improvement)",
   * 또는 "나쁨(poor)" 임계치 범위에 해당하는지를 나타내는 평가.
   */
  rating: "good" | "needs-improvement" | "poor";

  /**
   * 현재 값과 마지막 보고 값의 차이(델타).
   * 첫 번째 보고에서는 `delta`와 `value`가 항상 동일함.
   */
  delta: number;

  id: string;

  /**
   * 메트릭 값 계산과 관련된 성능 항목들.
   * 항목이 없을 수도 있으며, 이는 메트릭 값이 특정 항목에 기반하지 않은 경우임.
   * (예: 레이아웃 시프트가 없어서 CLS 값이 0인 경우)
   */
  entries: PerformanceEntry[];

  /**
   * 네비게이션 타입.
   *
   * 이는 Navigation Timing API가 반환하는 값(브라우저가 지원하지 않으면 `undefined`)과 같음.
   * 단, 다음 예외 사항이 있음:
   * - 'back_forward'는 일관성을 위해 'back-forward'로 이름이 바뀜.
   * - 'prerender': 미리 렌더링된 페이지.
   */
  navigationType:
    | "navigate"
    | "reload"
    | "back-forward"
    | "prerender"
}

샘플

최적화 전

기본 최적화 코드(Suspense)

  • StaticTest와 Loading...을 빠르게 확인할 수 있으므로 FCP 개선 효과가 있습니다.

  • 대규모 데이터 로딩/SSR에 유리: 서버/클라이언트에서 일부 데이터를 비동기로 가져오는 상황에서 유용합니다.

결론

  • 개발 환경에서는 Rating이 Good이 아닐 경우 콘솔에 표시되도록 해서, 원인을 바로 확인하면서 개선하는 게 좋습니다.

  • 운영 환경에서는 Google Analytics(GA)를 활용해 웹 추적을 진행하면 효율적으로 관리할 수 있습니다.

참고

공식문서

0개의 댓글