junyeokk
Blog
Next.js·2026. 01. 25

next/image 이미지 최적화

Next.js의 next/image 컴포넌트는 이미지를 자동으로 최적화한다. WebP 같은 최신 포맷으로 변환하고, 디바이스 크기에 맞게 리사이징하고, 지연 로딩을 적용한다. 하지만 이 최적화를 받으려면 이미지의 width와 height를 알아야 한다.

width/height가 필요한 이유

브라우저가 이미지를 로드하기 전에 해당 공간을 미리 확보하려면 크기 정보가 필요하다. 크기를 모르면 이미지가 로드되면서 레이아웃이 밀리는 현상(Cumulative Layout Shift)이 발생한다. next/image는 이 문제를 방지하기 위해 width/height를 필수로 요구한다.

로컬 이미지 vs 외부 이미지

로컬 이미지를 import하면 Next.js가 빌드 시점에 파일을 읽어서 크기를 자동으로 추출한다.

tsx
import myImage from '@/public/images/screenshot.png';

// width, height 자동 추론
<Image src={myImage} alt="스크린샷" />

외부 이미지는 빌드 시점에 접근할 수 없으므로 크기를 알 수 없다. 직접 width/height를 명시해야 한다.

tsx
// 외부 이미지는 크기 명시 필수
<Image 
  src="https://example.com/image.png" 
  alt="외부 이미지"
  width={800}
  height={600}
/>

외부 이미지를 사용하려면 next.config.js에 remotePatterns도 설정해야 한다.

javascript
module.exports = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'example.com',
      },
    ],
  },
}

fill 모드

이미지 크기를 모르거나 부모 요소를 꽉 채우고 싶을 때 fill 속성을 사용한다. 이 경우 부모 요소에 position: relative와 크기가 지정되어 있어야 한다.

tsx
<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의 이미지 최적화를 건너뛴다. 원본 이미지를 그대로 사용한다.

tsx
<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 없이 최적화를 적용할 수 있다. 가능하면 이미지를 레포지토리 내에서 관리하는 것이 좋다.