frontend

Next.js Image Optimization 완벽 가이드 — next/image로 LCP 점수 올리기

Next.js next/image 컴포넌트의 작동 원리와 fill 모드, priority 설정, sizes 속성, 외부 도메인 허용 방법으로 Core Web Vitals LCP를 개선하는 방법을 정리하였다.

★★★★★적극 추천
Next.jsImage성능최적화CoreWebVitalsLCP
Next.js Image Optimization 완벽 가이드 — next/image로 LCP 점수 올리기
  • ·next/image는 빌드 타임이 아닌 요청 시점에 이미지를 최적화하고 CDN에 캐시한다
  • ·priority 속성을 추가하면 해당 이미지에 fetchpriority='high'와 preload link 태그가 자동으로 삽입된다
  • ·fill 모드에서는 부모 요소가 position: relative여야 하고 width/height가 필요 없다
  • ·next.config.js의 images.remotePatterns로 외부 도메인 이미지를 허용해야 하며 와일드카드를 지원한다
블로그 페이지의 LCP 점수가 4초를 넘었을 때 Lighthouse를 돌려보니 히어로 이미지에 priority가 없는 것이 주요 원인이었다. priority 추가 하나로 LCP가 1.8초로 줄었고, sizes 속성으로 뷰포트별 이미지 크기를 지정하니 모바일에서 불필요하게 큰 이미지를 내려받는 문제도 해결됐다. next/image 설정을 제대로 이해하기 전에는 그냥 img 태그보다 느린 것 같다는 느낌이 들 정도로 기본값의 함정이 있었다.

next/image 기본 동작 원리와 필수 속성

Next.js next/image 요청 시 최적화 원리와 width, height 필수 지정 이유

next/image는 이미지를 빌드 타임이 아닌 사용자가 처음 요청하는 시점에 최적화한다. /_next/image 엔드포인트가 원본 이미지를 받아 지정한 크기로 리사이즈하고 WebP 또는 AVIF로 변환한 뒤 캐시에 저장한다. width와 height를 필수로 지정하는 이유는 브라우저가 이미지 공간을 미리 확보해 레이아웃 이동(CLS)을 방지하기 위함이다. 값을 모르거나 콘텐츠에 따라 달라지는 경우라면 fill 모드를 사용한다. fill 모드는 부모 요소 크기에 맞게 이미지를 채우며 object-fit 스타일로 표시 방식을 조정한다. 실제 사용 시 부모 요소에 position: relative와 명확한 높이가 없으면 이미지가 화면에 보이지 않는 문제가 자주 발생하는데 이 CSS 요구사항을 처음에 놓치기 쉽다.

// 고정 크기
<Image src={post.thumbnail} width={1200} height={630} alt={post.title} />

// fill 모드
<div style={{ position: 'relative', height: '200px' }}>
  <Image src={post.thumbnail} fill alt={post.title} style={{ objectFit: 'cover' }} />
</div>

Next.js Image priority와 sizes 속성으로 LCP 점수 최적화하기

페이지에서 뷰포트 내 첫 번째로 보이는 이미지, 즉 LCP 후보 이미지에는 반드시 priority 속성을 추가해야 한다. priority를 지정하면 Next.js가 해당 이미지에 fetchpriority='high' 속성과 link rel='preload' 태그를 HTML head에 삽입해 브라우저가 다른 리소스보다 먼저 이미지를 가져오도록 유도한다. priority 없이 LCP 이미지를 방치하면 브라우저가 HTML 파싱 후에야 이미지 존재를 알게 되어 LCP가 1~2초 이상 지연된다. sizes 속성은 뷰포트 너비별로 실제 렌더링 크기를 브라우저에 알려주는 힌트로, 모바일에서 데스크탑 크기 이미지를 내려받는 낭비를 막는다. sizes='(max-width: 768px) 100vw, 50vw'처럼 미디어 조건을 지정하면 Next.js가 srcset을 자동 생성해 적절한 해상도를 선택한다.

외부 이미지와 도메인 설정

Next.js next/image 외부 도메인 이미지 허용 설정과 보안 고려사항

외부 URL의 이미지를 next/image로 최적화하려면 next.config.js의 images.remotePatterns에 허용할 도메인을 등록해야 한다. hostname, protocol, pathname 패턴을 조합해 허용 범위를 세밀하게 지정할 수 있으며 hostname에 와일드카드를 쓸 수 있다. 너무 넓은 와일드카드를 설정하면 서버가 임의 이미지를 프록시·최적화하는 Open Redirect 취약점이 될 수 있으므로 실제 사용하는 도메인만 명시하는 것이 좋다. S3나 Cloudflare R2 같은 오브젝트 스토리지를 사용할 때는 버킷 도메인 전체를 허용하기보다 특정 경로 패턴을 지정해 허용 범위를 최소화한다. 개발 환경에서만 사용하는 도메인은 process.env.NODE_ENV 조건으로 분리해 프로덕션 빌드에 포함되지 않도록 관리하는 것이 안전하다.

// next.config.js
module.exports = {
  images: {
    remotePatterns: [
      { protocol: 'https', hostname: 'cdn.example.com', pathname: '/posts/**' }
    ]
  }
}

next/image 고급 활용과 트러블슈팅

Next.js Static Export에서 next/image 사용 시 주의사항과 unoptimized 설정

output: 'export'로 정적 빌드를 할 때 next/image의 서버 사이드 최적화 기능은 동작하지 않는다. Next.js 이미지 최적화 API가 서버 런타임 없이는 실행될 수 없기 때문이다. 정적 배포 환경에서는 두 가지 선택이 있다. 첫 번째는 images.unoptimized = true를 설정해 next/image를 단순 img 태그처럼 동작시키는 방법이다. 두 번째는 Cloudflare Images나 Imgix 같은 외부 이미지 CDN을 loader로 연결하는 방법으로, 서버 없이도 온디맨드 이미지 최적화가 가능해진다. 이 블로그에도 output: 'export'를 사용해 정적 배포를 하고 있어서 unoptimized 모드를 쓰고 있다. WebP 변환은 Squoosh나 sharp 스크립트로 빌드 전에 미리 처리해 보완했다.

자주 묻는 질문

next/image 사용 시 Vercel이 아닌 자체 서버에서도 이미지 최적화가 동작하나요?+

네, 동작합니다. Node.js 서버로 배포하면 /_next/image 라우트가 자동 활성화됩니다. sharp 패키지를 설치하면 최적화 속도가 더 빠릅니다.

next/image의 blurDataURL은 어떻게 생성하나요?+

로컬 이미지는 Next.js가 자동으로 blur placeholder를 생성합니다. 외부 이미지는 plaiceholder 라이브러리로 base64 blur 이미지를 빌드 타임에 생성해 blurDataURL에 전달합니다.

next/image와 img 태그 중 무엇이 더 성능이 좋은가요?+

priority와 sizes를 올바르게 설정한 next/image가 WebP 변환, 지연 로딩 자동 적용, srcset 생성 덕분에 일반 img보다 성능이 유리합니다. 단 정적 배포에서는 unoptimized 모드라면 차이가 없습니다.

관련 글