next/image 이미지 최적화
Next.js의 next/image 컴포넌트는 이미지를 자동으로 최적화한다. WebP 같은 최신 포맷으로 변환하고, 디바이스 크기에 맞게 리사이징하고, 지연 로딩을 적용한다. 하지만 이 최적화를 받으려면 이미지의 width와 height를 알아야 한다.
width/height가 필요한 이유
브라우저가 이미지를 로드하기 전에 해당 공간을 미리 확보하려면 크기 정보가 필요하다. 크기를 모르면 이미지가 로드되면서 레이아웃이 밀리는 현상(Cumulative Layout Shift)이 발생한다. next/image는 이 문제를 방지하기 위해 width/height를 필수로 요구한다.
로컬 이미지 vs 외부 이미지
로컬 이미지를 import하면 Next.js가 빌드 시점에 파일을 읽어서 크기를 자동으로 추출한다.
import myImage from '@/public/images/screenshot.png';
// width, height 자동 추론
<Image src={myImage} alt="스크린샷" />
외부 이미지는 빌드 시점에 접근할 수 없으므로 크기를 알 수 없다. 직접 width/height를 명시해야 한다.
// 외부 이미지는 크기 명시 필수
<Image
src="https://example.com/image.png"
alt="외부 이미지"
width={800}
height={600}
/>
외부 이미지를 사용하려면 next.config.js에 remotePatterns도 설정해야 한다.
module.exports = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'example.com',
},
],
},
}
fill 모드
이미지 크기를 모르거나 부모 요소를 꽉 채우고 싶을 때 fill 속성을 사용한다. 이 경우 부모 요소에 position: relative와 크기가 지정되어 있어야 한다.
<div style={{ position: 'relative', width: '100%', aspectRatio: '16/9' }}>
<Image
src="/image.png"
alt="이미지"
fill
style={{ objectFit: 'cover' }}
/>
</div>
fill 모드의 문제는 컨테이너 비율과 이미지 원본 비율이 다르면 잘리거나 여백이 생긴다는 것이다. 16:9 컨테이너에 세로로 긴 이미지를 넣으면 object-fit: cover는 이미지를 자르고, object-fit: contain은 양옆에 여백을 만든다.
unoptimized 옵션
unoptimized를 true로 설정하면 Next.js의 이미지 최적화를 건너뛴다. 원본 이미지를 그대로 사용한다.
<Image
src="https://example.com/image.png"
alt="이미지"
width={800}
height={600}
unoptimized
/>
이 옵션은 외부 이미지의 실제 크기를 모를 때 유용하다. width/height에 임의의 값을 넣고 CSS로 width: auto, height: auto를 적용하면 원본 비율을 유지하면서 표시할 수 있다. 대신 WebP 변환, 리사이징 같은 최적화 혜택은 받지 못한다.
언제 unoptimized를 쓰는가
외부 이미지 URL의 실제 크기를 알 수 없고, fill 모드로 고정 비율을 강제하면 레이아웃이 깨지는 경우에 사용한다. CMS나 외부 서비스에서 가져오는 이미지가 다양한 비율을 가질 때 이런 상황이 생긴다.
로컬 이미지로 전환하면 빌드 시 크기를 자동 추출할 수 있으므로 unoptimized 없이 최적화를 적용할 수 있다. 가능하면 이미지를 레포지토리 내에서 관리하는 것이 좋다.