웹 개발·4분 읽기

웹 성능 최적화 완벽 가이드: 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 포맷을 사용합니다
  • widthheight 속성을 반드시 지정합니다 (CLS 방지)
  • 뷰포트 밖 이미지는 loading="lazy"를 적용합니다
  • LCP 이미지는 fetchpriority="high"를 적용합니다
  • srcsetsizes로 반응형 이미지를 제공합니다

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 수익에도 직접적인 영향을 미치므로, 성능 지표를 항상 좋은 범위에 유지하는 것이 수익 극대화의 기본입니다.
#웹 성능#Core Web Vitals#LCP#SEO#최적화