웹 성능에서 이미지는 가장 큰 병목 중 하나다. Next.js는 next/image 컴포넌트 하나로 WebP 변환, 크기 조정, Lazy Loading을 자동으로 처리해 별도의 설정 없이도 Core Web Vitals 점수를 끌어올린다. 원본 PNG를 그대로 서빙하던 방식과 비교하면 체감 성능 차이가 크다.
브라우저 요청
│
▼
Next.js Image Optimization API
│ ├─ 포맷 변환 (PNG/JPG → WebP/AVIF)
│ ├─ 크기 리사이징 (srcset 자동 생성)
│ └─ 캐싱 (CDN or 로컬)
│
▼
최적화된 이미지 응답
next/image는 브라우저가 요청하는 시점에 서버 사이드에서 이미지를 변환한다. 한 번 변환된 이미지는 캐시에 저장되어 재요청 시 즉시 반환된다.
고정 크기 이미지는 width와 height를 명시한다.
import Image from 'next/image'
export default function Avatar() {
return (
<Image
src="/profile.png"
alt="프로필 사진"
width={80}
height={80}
/>
)
}
부모 컨테이너를 채우는 이미지는 fill prop을 사용한다. 이때 부모 요소에 position: relative가 필요하다.
<div style={{ position: 'relative', width: '100%', height: '400px' }}>
<Image
src="/hero.jpg"
alt="히어로 이미지"
fill
style={{ objectFit: 'cover' }}
/>
</div>
fill: 부모 요소의 크기를 기준으로 이미지를 꽉 채우는 모드. width/height 대신 사용한다.
기본적으로 모든 next/image는 Lazy Loading이 적용된다. 뷰포트 밖에 있는 이미지는 스크롤이 가까워질 때 로드된다.
Above-the-fold 이미지(LCP 후보)는 priority prop으로 즉시 로드해야 한다.
<Image
src="/hero.jpg"
alt="히어로"
width={1200}
height={600}
priority
/>
| prop | 동작 | 사용 시점 |
|---|---|---|
| 기본값 | Lazy Loading | 스크롤 아래 이미지 |
priority | Preload + 즉시 로드 | LCP 대상, 첫 화면 이미지 |
LCP(Largest Contentful Paint): 뷰포트에서 가장 큰 콘텐츠가 렌더링되는 시점. Core Web Vitals의 핵심 지표.
별도 설정 없이 next/image는 브라우저가 WebP를 지원하면 자동으로 WebP로 변환해 응답한다. next.config.js에서 포맷 우선순위를 조정할 수 있다.
// next.config.js
module.exports = {
images: {
formats: ['image/avif', 'image/webp'],
},
}
AVIF를 먼저 시도하고 미지원 브라우저는 WebP로 폴백된다.
이미지가 로드되기 전 블러 처리된 미리보기를 보여주면 레이아웃 시프트를 방지하고 UX가 개선된다.
로컬 이미지는 빌드 타임에 자동 생성된다.
import profilePic from '/public/profile.png'
<Image
src={profilePic}
alt="프로필"
placeholder="blur"
/>
외부 이미지는 blurDataURL을 직접 제공해야 한다.
<Image
src="https://example.com/photo.jpg"
alt="외부 이미지"
width={800}
height={600}
placeholder="blur"
blurDataURL="data:image/png;base64,iVBORw0KGgo..."
/>
반응형 레이아웃에서 sizes를 지정하면 브라우저가 현재 뷰포트에 맞는 최소 크기의 이미지를 선택한다.
<Image
src="/article-thumbnail.jpg"
alt="썸네일"
fill
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
/>
sizes를 생략하면 Next.js는 항상 최대 크기 이미지를 내려보내 불필요한 데이터를 낭비한다.