웹 성능 최적화 완벽 가이드: Core Web Vitals
Core Web Vitals를 중심으로 웹 성능을 측정하고 개선하는 방법을 알아봅니다. LCP, FID, CLS 지표와 실전 최적화 기법을 다룹니다.
웹 성능 최적화 완벽 가이드: Core Web Vitals
웹 성능은 사용자 경험과 비즈니스 성과에 직접적인 영향을 미칩니다. Google은 Core Web Vitals를 검색 순위 요인으로 포함시켰으며, 페이지 로딩 시간이 1초에서 3초로 증가하면 이탈률이 32% 높아진다는 연구 결과도 있습니다. 이 글에서는 웹 성능의 핵심 지표와 실전 최적화 기법을 살펴보겠습니다.
Core Web Vitals 이해하기
Core Web Vitals는 Google이 정의한 세 가지 핵심 사용자 경험 지표입니다.
LCP (Largest Contentful Paint)
LCP는 페이지의 주요 콘텐츠가 화면에 렌더링되기까지의 시간을 측정합니다.
| 등급 | 시간 | 의미 |
|---|---|---|
| 좋음 | 2.5초 이하 | 빠른 로딩 |
| 개선 필요 | 2.5~4.0초 | 보통 |
| 나쁨 | 4.0초 초과 | 느린 로딩 |
LCP에 영향을 미치는 요소는 큰 이미지, 비디오, 텍스트 블록입니다. 가장 큰 콘텐츠 요소가 빠르게 로드되도록 최적화해야 합니다.
INP (Interaction to Next Paint)
INP는 사용자 상호작용(클릭, 탭, 키 입력)에 대한 응답 속도를 측정합니다. 기존 FID(First Input Delay)를 대체한 지표로, 페이지 전체 수명 동안의 응답성을 평가합니다.
| 등급 | 시간 | 의미 |
|---|---|---|
| 좋음 | 200ms 이하 | 빠른 응답 |
| 개선 필요 | 200~500ms | 느리게 느껴짐 |
| 나쁨 | 500ms 초과 | 반응 없음 |
CLS (Cumulative Layout Shift)
CLS는 페이지 로딩 중 예상치 못한 레이아웃 변동을 측정합니다. 광고, 이미지, 동적 콘텐츠가 뒤늦게 로드되면서 화면이 밀리는 현상이 여기에 해당합니다.
| 등급 | 점수 | 의미 |
|---|---|---|
| 좋음 | 0.1 이하 | 안정적 레이아웃 |
| 개선 필요 | 0.1~0.25 | 약간의 변동 |
| 나쁨 | 0.25 초과 | 심한 변동 |
이미지 최적화
이미지는 웹 페이지 용량의 대부분을 차지합니다. 이미지 최적화만으로도 큰 성능 개선을 달성할 수 있습니다.
차세대 이미지 포맷 사용
WebP와 AVIF는 JPEG/PNG 대비 25-50% 더 작은 파일 크기를 제공합니다.
<!-- picture 태그로 포맷 폴백 -->
<picture>
<source srcset="image.avif" type="image/avif" />
<source srcset="image.webp" type="image/webp" />
<img src="image.jpg" alt="설명" width="800" height="600" loading="lazy" />
</picture>
Next.js Image 컴포넌트
Next.js의 Image 컴포넌트는 자동으로 이미지를 최적화합니다.
import Image from 'next/image';
export default function Hero() {
return (
<Image
src="/hero.jpg"
alt="히어로 이미지"
width={1200}
height={600}
priority // LCP 이미지는 priority 추가
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
/>
);
}
이미지 최적화 체크리스트
- 적절한 크기로 리사이징합니다 (실제 표시 크기의 2배 이하)
- WebP 또는 AVIF 포맷을 사용합니다
width와height속성을 반드시 지정합니다 (CLS 방지)- 뷰포트 밖 이미지는
loading="lazy"를 적용합니다 - LCP 이미지는
fetchpriority="high"를 적용합니다 srcset과sizes로 반응형 이미지를 제공합니다
JavaScript 번들 최적화
큰 JavaScript 번들은 파싱과 실행에 시간이 걸려 INP에 악영향을 미칩니다.
코드 스플리팅
// Next.js dynamic import
import dynamic from 'next/dynamic';
// 무거운 컴포넌트를 지연 로딩
const HeavyChart = dynamic(() => import('../components/Chart'), {
loading: () => <div className="h-64 bg-gray-100 animate-pulse rounded" />,
ssr: false,
});
// React.lazy 사용
const LazyModal = React.lazy(() => import('./Modal'));
트리 쉐이킹
사용하지 않는 코드를 번들에서 제거하려면 named import를 사용합니다.
// 나쁜 예 - 전체 라이브러리 번들링
import _ from 'lodash';
_.debounce(fn, 300);
// 좋은 예 - 필요한 함수만 임포트
import debounce from 'lodash/debounce';
debounce(fn, 300);
번들 분석
# Next.js 번들 분석
npm install @next/bundle-analyzer번들 크기 확인
ANALYZE=true npm run build
## CSS 최적화
### 사용하지 않는 CSS 제거
Tailwind CSS를 사용하면 JIT 컴파일러가 자동으로 사용하지 않는 스타일을 제거합니다. 전통적 CSS의 경우 PurgeCSS를 활용합니다.
### 크리티컬 CSS 인라인
페이지 렌더링에 즉시 필요한 CSS를 인라인으로 포함하면 렌더 블로킹을 방지할 수 있습니다.
## 폰트 최적화
웹 폰트는 FOUT(Flash of Unstyled Text)이나 FOIT(Flash of Invisible Text)를 유발할 수 있습니다.
### 최적화 전략
/ 1. font-display 설정 / @font-face { font-family: 'Noto Sans KR'; src: url('/fonts/NotoSansKR-Regular.woff2') format('woff2'); font-weight: 400; font-display: swap; / 텍스트를 먼저 보여주고 폰트 로드 후 교체 / }
// Next.js 내장 폰트 최적화 import { Noto_Sans_KR } from 'next/font/google';
const notoSansKR = Noto_Sans_KR({ subsets: ['latin'], weight: ['400', '700'], display: 'swap', });
## 캐싱 전략
적절한 캐싱은 재방문 사용자의 로딩 속도를 극적으로 개선합니다.
### HTTP 캐시 헤더
정적 에셋 (이미지, CSS, JS) - 1년 캐시
Cache-Control: public, max-age=31536000, immutable
HTML 페이지 - 재검증 필요
Cache-Control: no-cache
API 응답 - 짧은 캐시
Cache-Control: public, max-age=60, s-maxage=300
### Next.js에서의 캐싱
// next.config.ts const nextConfig = { async headers() { return [ { source: '/_next/static/:path*', headers: [ { key: 'Cache-Control', value: 'public, max-age=31536000, immutable', }, ], }, ]; }, };
## 렌더링 전략 선택
### SSG vs SSR vs ISR vs CSR
| 전략 | 빌드 시 | 요청 시 | 장점 | 단점 |
|------|---------|---------|------|------|
| SSG | HTML 생성 | 정적 서빙 | 가장 빠름 | 데이터 갱신 어려움 |
| SSR | - | HTML 생성 | 항상 최신 데이터 | 서버 부하 |
| ISR | HTML 생성 | 주기적 재생성 | 빠르고 신선함 | 복잡한 설정 |
| CSR | 빈 HTML | JS로 렌더링 | 인터랙티브 | 초기 로딩 느림 |
블로그나 콘텐츠 사이트는 SSG 또는 ISR이 가장 적합합니다. 사용자별 데이터가 필요한 대시보드는 SSR이나 CSR을 고려합니다.
## 성능 측정 도구
### 추천 도구
- **Google PageSpeed Insights**: Core Web Vitals 점수 확인
- **Lighthouse**: Chrome DevTools 내장 성능 감사
- **WebPageTest**: 상세한 폭포수 차트와 비디오 분석
- **Chrome DevTools Performance 탭**: 실시간 성능 프로파일링
### 자동 모니터링
// Web Vitals 자동 수집 import { onCLS, onINP, onLCP } from 'web-vitals';
function sendToAnalytics(metric) { const body = JSON.stringify({ name: metric.name, value: metric.value, id: metric.id, }); navigator.sendBeacon('/api/analytics', body); }
onCLS(sendToAnalytics); onINP(sendToAnalytics); onLCP(sendToAnalytics);
## 성능 최적화 체크리스트
1. 이미지를 WebP/AVIF 포맷으로 변환하고 적절한 크기로 제공합니다
2. JavaScript 번들을 코드 스플리팅으로 분할합니다
3. 중요하지 않은 리소스는 지연 로딩합니다
4. 정적 에셋에 적절한 캐시 헤더를 설정합니다
5. 웹 폰트에 font-display: swap을 적용합니다
6. 이미지와 iframe에 width/height를 명시하여 CLS를 방지합니다
7. 크리티컬 CSS를 인라인으로 포함합니다
8. 서드파티 스크립트를 비동기로 로드합니다
## 마무리
웹 성능 최적화는 일회성 작업이 아닌 지속적인 프로세스입니다. Core Web Vitals를 정기적으로 모니터링하고, 새로운 기능 추가 시 성능 영향을 평가하는 습관이 중요합니다. 특히 SEO와 AdSense 수익에도 직접적인 영향을 미치므로, 성능 지표를 항상 좋은 범위에 유지하는 것이 수익 극대화의 기본입니다.관련 게시글
JavaScript 비동기 프로그래밍 완벽 가이드
JavaScript의 비동기 프로그래밍을 콜백부터 Promise, async/await, 이벤트 루프, 에러 처리, 동시성 패턴까지 단계별로 완벽하게 정리합니다.
Tailwind CSS 실전 활용법: 모던 웹 스타일링
Tailwind CSS를 활용한 효율적인 웹 스타일링 방법을 알아봅니다. 유틸리티 퍼스트 철학부터 반응형 디자인, 다크 모드, 컴포넌트 패턴까지 다룹니다.
CSS Grid와 Flexbox 완벽 가이드: 모던 레이아웃의 모든 것
CSS Grid와 Flexbox의 핵심 개념, 차이점, 실전 활용법을 상세히 비교하고 반응형 레이아웃을 만드는 방법을 예제와 함께 알아봅니다.