Next.js 교재 · 25편 · 성능

성능 측정과 최적화 — Lighthouse·Vitals

"느려요" 라는 추측 말고 숫자로 보자. 세 지표·세 도구·다섯 처방.

Lighthouse 성능 점수와 Web Vitals 컨셉 일러스트

"왜 느려요?" 라는 질문에 정확히 답하려면 — 측정부터. 그 다음 어디서 늦어졌는지 분리. 그 다음 해당 영역 처방. 측정 없는 최적화는 추측이고 추측은 보통 틀린다.

마지막에서 두 번째 챕터. Next.js 성능의 표준 측정 도구와 흔한 처방 5가지를 한 번에. 21편 Analytics 는 사용자 환경 측정, 이번은 개발 중 측정·최적화.

1. Core Web Vitals — 세 지표만 외우면 끝

구글이 정한 검색 순위에도 영향을 주는 3가지. 다른 30개 지표보다 이 셋이 우선.

지표의미좋음나쁨
LCP가장 큰 요소 표시까지 시간≤ 2.5초≥ 4초
CLS레이아웃 시프트 누적≤ 0.1≥ 0.25
INP상호작용 응답 시간≤ 200ms≥ 500ms

2024 년부터 FID 가 INP 로 바뀜. 셋 다 모바일 75th percentile 기준 — 모바일에서 4명 중 3명이 좋은 점수여야 'good'.

2. 측정 도구 — Lighthouse·DevTools·실사용자

Chrome DevTools Lighthouse

가장 쉬운 시작. F12 → Lighthouse 탭 → Analyze. 모바일 + Performance 옵션 체크 후 측정. 4G 시뮬레이션 + 4배 느린 CPU 환경에서 실행되어 진짜 모바일 상황에 가깝다.

한계 — lab 측정 — Lighthouse 는 깨끗한 환경에서 한 번 측정한 lab 점수. 진짜 사용자는 광고 차단 확장·느린 네트워크·구형 기기 — 실측이 다르다. 21편의 Vercel Speed Insights(RUM) 가 진짜 사용자 환경 측정.

PageSpeed Insights

pagespeed.web.dev 에서 URL 입력 — Lighthouse 와 같은 측정 + CrUX (Chrome User Experience Report) 라는 진짜 사용자 데이터 둘 다. 무료 + 가입 없음. PR 머지 전 체크 표준.

WebPageTest

webpagetest.org — 가장 정교한 분석. 다양한 지역·기기·네트워크·waterfall 차트. 상세한 디버깅 필요 시.

3. 번들 분석 — 큰 패키지 찾기

"왜 JS 가 1MB?" 의 답을 찾는 도구.

$ npm install -D @next/bundle-analyzer
// next.config.mjs import withBundleAnalyzer from '@next/bundle-analyzer'; const analyze = withBundleAnalyzer({ enabled: process.env.ANALYZE === 'true', }); export default analyze({ // 기존 설정 });
$ ANALYZE=true npm run build

빌드 후 브라우저에 인터랙티브 treemap 자동 표시. 가장 큰 박스가 가장 큰 패키지. 의외로 moment.js·lodash·chart.js 같은 게 100KB+ 차지하고 있음을 발견.

흔한 비효율 — full lodash importimport _ from 'lodash' 는 70KB. import debounce from 'lodash/debounce' 같이 개별 import 하면 5KB. 또는 lodash-es + tree-shaking. moment 는 date-fns 또는 dayjs 로 교체 (1/10 크기).

4. 흔한 처방 5가지

증상처방참조 챕터
LCP 높음hero 이미지에 priority, next/image 사용15편
CLS 높음이미지 width/height 명시, 광고/iframe 자리 예약15편
INP 높음메인 스레드 무거운 작업 분리, Suspense·dynamic import12편
JS 번들 큼개별 import·dynamic import·tree-shaking15편
폰트 깜빡임next/font 셀프 호스팅 + display: swap15편

dynamic import — 무거운 컴포넌트 지연 로딩

// 차트 라이브러리가 200KB — 첫 화면에 안 쓰면 지연 import dynamic from 'next/dynamic'; const HeavyChart = dynamic(() => import('./HeavyChart'), { loading: () => <p>차트 로딩...</p>, ssr: false, // 클라이언트에서만 }); export default function Dashboard() { return ( <> <Header /> <HeavyChart /> {/* 필요할 때만 로드 */} </> ); }

5. 측정 → 처방 → 재측정 사이클

최적화는 한 번에 끝내지 말고 사이클로.

  1. 측정 — Lighthouse 모바일로 현재 점수 기록.
  2. 가장 큰 문제 1개 찾기 — LCP 3.5초가 가장 가까운 적이면 그것부터.
  3. 처방 1개 적용 — hero 이미지에 priority 추가.
  4. 재측정 — LCP 3.5 → 2.0초로? 다른 지표 망가지지 않았는지.
  5. 다음 문제 — INP 가 적이면 그 다음 사이클.

한 번에 5가지 바꾸면 어느 게 효과 있었는지 모름. 한 번에 1개. 5분 사이클.

최적화 전 — 정말 필요한가? — LCP 1.8초가 1.6초 되는 거에 한 주 들이지 말 것. 사용자가 못 느낌. 3초 → 1.5초 가는 큰 점프만 가치 있음. 측정한 적도 없는데 "최적화" 시작은 시간 낭비.

요약 — 25편 좌표

여기까지 정리. Core Web Vitals 3 — LCP(2.5초)·CLS(0.1)·INP(200ms), 모바일 75th percentile. 측정은 Lighthouse(lab) + PageSpeed Insights(CrUX) + Speed Insights(RUM, 21편). 번들 분석은 @next/bundle-analyzer — lodash·moment 발견 자주. 처방 5가지는 15편 image/font·12편 Suspense·dynamic import 가 99%. 측정 → 1개 처방 → 재측정 사이클. 마지막 챕터에서 실전 미니 프로젝트로 25편을 연결.

다음 편 예고 — 실전 미니 프로젝트

블로그 사이트 처음부터 끝까지. 26편(완결).

© 2026 주나이테크(주) @JUNAITECH