Next.js Analytics 정리
Next.js Docs – App Router Guides: Analytics 페이지를 기반으로, Next.js 애플리케이션에 성능 분석 및 웹 바이탈(Web Vitals) 분석을 추가하는 방법을 정리한 문서입니다.
1. Next.js Analytics 개요
Next.js는 기본적으로 웹 성능 측정 및 보고(Analytics) 를 지원합니다.
useReportWebVitals훅을 사용하여 직접 성능 지표를 수집·보고할 수 있습니다.- 또는 Vercel에서 제공하는 관리형 Analytics 서비스를 사용하여, 별도 코드 작성 없이도 성능 지표를 수집하고 대시보드에서 시각화할 수 있습니다.
이 가이드에서는 주로 직접 구현하는 방식(“Build Your Own”) 에 초점을 맞추어 정리합니다.
2. Client Instrumentation
보다 고급 분석 및 모니터링을 위해, Next.js는 클라이언트 인스트루멘테이션 파일을 지원합니다.
- 파일 이름:
instrumentation-client.js또는instrumentation-client.ts - 위치: 애플리케이션의 루트 디렉터리 (예:
app과 같은 최상위 수준) - 실행 시점: 애플리케이션의 프런트엔드 코드가 실행되기 이전
이를 통해 다음과 같은 작업을 전역적으로 설정할 수 있습니다.
- 분석 도구 초기화 (예: Sentry, Datadog, 자체 Analytics 등)
- 전역 에러 추적 로직 설정
- 성능 모니터링 스크립트 주입
2.1 기본 예시
// instrumentation-client.js
// 애플리케이션 시작 전에 Analytics 초기화
console.log('Analytics initialized')
// 전역 에러 추적 설정
window.addEventListener('error', (event) => {
// 에러 추적 서비스로 전송
reportError(event.error)
})
이 파일은 Next.js에 의해 자동으로 불러와지며,
별도 import 없이도 애플리케이션 전역에 적용되는 클라이언트 초기화 코드로 동작합니다.
3. 직접 구현하는 Analytics (Build Your Own)
Next.js는 성능 측정을 위해 next/web-vitals 모듈에서 useReportWebVitals 훅을 제공합니다.
// app/_components/web-vitals.js
'use client'
import { useReportWebVitals } from 'next/web-vitals'
export function WebVitals() {
useReportWebVitals((metric) => {
console.log(metric)
})
}
그리고 이 컴포넌트를 루트 레이아웃에서 렌더링하면, 페이지 전역에 대해 웹 바이탈 측정이 활성화됩니다.
// app/layout.js
import { WebVitals } from './_components/web-vitals'
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<WebVitals />
{children}
</body>
</html>
)
}
useReportWebVitals훅은'use client'지시문이 필요하므로,
가장 좋은 방법은 이 훅을 사용하는 작은 클라이언트 컴포넌트 하나(WebVitals) 를 만들고,
루트 레이아웃에서 import 하여 사용하는 것입니다.
이렇게 하면 클라이언트 경계가WebVitals컴포넌트에만 한정되어, 불필요한 클라이언트 번들 확대를 피할 수 있습니다.
4. Web Vitals란 무엇인가
Web Vitals 는 사용자 경험을 잘 반영하는 핵심 웹 성능 지표들의 모음입니다.
Next.js의 useReportWebVitals 훅은 다음과 같은 지표들을 모두 수집합니다.
- 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)
상호작용 이후 다음 화면 업데이트까지의 응답성을 측정하는 지표
각 측정 결과는 metric.name 속성으로 구분할 수 있습니다.
4.1 특정 지표별로 분기 처리
// app/_components/web-vitals.tsx
'use client'
import { useReportWebVitals } from 'next/web-vitals'
export function WebVitals() {
useReportWebVitals((metric) => {
switch (metric.name) {
case 'FCP': {
// FCP 결과 처리
break
}
case 'LCP': {
// LCP 결과 처리
break
}
case 'CLS': {
// CLS 결과 처리
break
}
// 나머지 지표들도 필요에 따라 분기
default: {
// 공통 처리
}
}
})
}
이처럼 switch (metric.name) 패턴을 사용하면,
각 지표를 원하는 방식으로 분류하여 저장·전송·로그 등을 수행할 수 있습니다.
5. 외부 시스템으로 지표 전송하기
실제 서비스에서는 Web Vitals 결과를 외부 분석 시스템으로 보내어 저장·시각화하는 경우가 많습니다.
예시: navigator.sendBeacon 또는 fetch를 사용한 전송
useReportWebVitals((metric) => {
const body = JSON.stringify(metric)
const url = 'https://example.com/analytics'
// sendBeacon이 지원되면 이를 우선 사용
if (navigator.sendBeacon) {
navigator.sendBeacon(url, body)
} else {
fetch(url, {
body,
method: 'POST',
keepalive: true,
})
}
})
navigator.sendBeacon은 페이지 언로드 시점에도 데이터를 안정적으로 전송할 수 있어,- 성능 측정 이벤트 전송에 적합합니다.
keepalive: true옵션은 브라우저가 요청을 백그라운드에서도 계속 처리하도록 돕습니다.
5.1 Google Analytics 예시
Google Analytics를 사용하는 경우, metric.id 값 등을 활용하여 분포(퍼센타일 등) 를 직접 계산할 수도 있습니다.
useReportWebVitals((metric) => {
// window.gtag가 초기화되어 있다고 가정
// (공식 예제: examples/with-google-analytics 참고)
window.gtag('event', metric.name, {
value: Math.round(
metric.name === 'CLS' ? metric.value * 1000 : metric.value
), // GA 이벤트 값은 정수여야 함
event_label: metric.id, // 특정 세션/측정 구분에 활용
})
})
- CLS는 매우 작은 소수값이기 때문에, GA에서 정수로 다루기 위해
* 1000을 곱한 뒤 반올림합니다. metric.id를 활용하면 메트릭 분포를 직접 계산하거나 특정 세션/페이지와 연결할 수 있습니다.