본문으로 건너뛰기

안동민 개발노트

본문 시작

이미지 최적화

next/image의 크기·우선순위·반응형 속성과 외부 호스트 설정으로 레이아웃 이동과 이미지 전송량을 줄입니다.

웹 애플리케이션의 성능 최적화는 사용자 경험과 SEO에 모두 영향을 주는 중요한 작업입니다.

특히 이미지 최적화는 로딩 속도 저하의 가장 흔한 원인 중 하나라 반드시 신경 써야 합니다.

Next.js는 내장 컴포넌트 next/image를 통해 고성능 이미지 처리를 쉽게 구현할 수 있게 해줍니다.

이 절에서는 이미지 최적화의 중요성부터 next/image 컴포넌트의 사용법, 그리고 이미지 최적화를 위한 다양한 기법들을 상세히 알아보겠습니다.

이미지 최적화는 전송량, 레이아웃, 첫 화면 우선순위를 함께 줄인다

큰 이미지를 그대로 보내면 다운로드가 늦고, 크기를 예약하지 않으면 화면이 밀리며, 첫 화면 이미지를 늦게 보내면 LCP가 나빠진다. `next/image`는 이 세 비용을 한 번에 관리한다.

  1. LCP

    첫 화면 대표 이미지가 얼마나 빨리 보이는지

  2. CLS

    미지 로딩 중 레이아웃이 밀리지 않는지

  3. 전송량

    기기와 브라우저에 맞는 크기와 포맷을 보내는지

  4. 1

    공간 예약 `width/height` 또는 `fill`로 이미지 자리를 먼저 잡는다.

  5. 2

    후보 폭 제한 `sizes`로 뷰포트별 실제 표시 폭을 알려 준다.

  6. 3

    첫 화면 우선 LCP 후보에만 `preload`를 붙여 로딩 순서를 당긴다.

  7. 4

    원격 출처 제한 외부 이미지는 `remotePatterns`로 허용 범위를 좁힌다.

문제원인next/image 대응확인 지표
느린 첫 화면hero 이미지가 lazy로 밀림preload 를 LCP 후보에만 적용LCP 시간
화면 흔들림이미지 자리 크기 미정width/height 또는 fillCLS
불필요한 다운로드모바일에도 큰 원본 전송sizes 와 최신 포맷 변환이미지 KB

왜 이미지를 최적화해야 하나요?

이미지는 웹 페이지에서 시각적인 매력을 더하고 정보를 전달하는 데 필수적이지만, 동시에 페이지 로딩 속도를 늦추는 주범이 되기도 합니다.

  • 느린 로딩 속도: 용량이 큰 이미지는 네트워크를 통해 전송되는 데 시간이 오래 걸려 페이지 로딩을 지연시킵니다. 이는 사용자 이탈률 증가로 이어질 수 있습니다.
  • 데이터 사용량 증가: 모바일 환경에서는 사용자의 데이터 요금 부담을 가중시킬 수 있습니다.
  • Core Web Vitals 저하: Google의 Core Web Vitals 지표(Largest Contentful Paint, Cumulative Layout Shift 등)에 부정적인 영향을 미쳐 SEO 점수를 낮출 수 있습니다.
  • 불필요한 리소스 낭비: 사용자 기기의 성능에 맞지 않는 고해상도 이미지를 전송하는 것은 불필요한 리소스 낭비입니다.

이미지 최적화는 이러한 문제들을 해결하여 웹 성능을 크게 개선하는 데 기여합니다.


next/image 컴포넌트 사용하기

Next.js는 이미지 최적화를 위해 특별히 설계된 <Image> 컴포넌트를 next/image에서 제공합니다.

이 컴포넌트는 단순히 <img> 태그를 대체하는 것을 넘어, 빌드 시점과 요청 시점에 이미지 최적화를 자동으로 수행합니다.

주요 최적화 기능
  • 반응형 이미지: 기기 크기에 따라 최적화된 크기의 이미지를 자동으로 제공합니다.
  • 지연 로딩(Lazy Loading): 뷰포트(Viewport)에 들어올 때까지 이미지 로딩을 지연시켜 초기 로딩 속도를 향상시킵니다.
  • 이미지 형식 최적화: 기본 설정은 브라우저가 지원하면 WebP로 변환합니다. AVIF까지 제공하려면 images.formatsimage/avif를 추가합니다.
  • 이미지 품질 최적화: 지정된 품질 설정에 따라 이미지 압축을 수행합니다.
  • Cumulative Layout Shift (CLS) 방지: 이미지의 widthheight를 미리 정의하여 이미지 로딩 시 레이아웃이 밀리는 현상을 방지합니다.
  • 이미지 호스팅 최적화: Next.js 서버 또는 CDN을 통해 이미지를 효율적으로 제공합니다.

next/image 설치

Next.js 13 이상을 사용하는 App Router 프로젝트라면 별도의 설치 없이 바로 사용할 수 있습니다.

기본 사용법

<Image> 컴포넌트에는 src와 의미에 맞는 alt가 필요합니다.

public 문자열 경로나 원격 URL을 사용하면 widthheight로 비율을 알려야 하지만, 정적 import가 크기를 제공하거나 fill로 부모 영역을 채우는 경우에는 두 속성을 생략합니다.

src/app/page.tsx 또는 다른 컴포넌트 파일
import Image from 'next/image';
import styles from './page.module.css'; // CSS 모듈 사용 예시

export default function HomePage() {
  return (
    <div className={styles.container}>
      <h1>Next.js 이미지 최적화</h1>

      {/* 로컬 이미지 사용 */}
      <div className={styles.imageWrapper}>
        <h2>로컬 이미지</h2>
        <Image
          src="/logo.png"
          alt="Next.js 로고"
          width={500}
          height={300}
          placeholder="blur"
          blurDataURL="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII="
          style={{ maxWidth: '100%', height: 'auto' }}
        />
        <p>이 이미지는 Next.js 로고입니다.</p>
      </div>

      {/* 외부 이미지 사용 */}
      <div className={styles.imageWrapper}>
        <h2>외부 이미지</h2>
        <Image
          src="https://images.unsplash.com/photo-1617424699564-90f7a77e9b40?q=80&w=2940&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
          alt="아름다운 자연 풍경"
          width={800}
          height={600}
          preload
          style={{ maxWidth: '100%', height: 'auto' }}
        />
        <p>이 이미지는 Unsplash에서 가져온 풍경 이미지입니다.</p>
      </div>

      {/* 레이아웃 채우기 (fill) 예시 */}
      <div className={styles.fillImageWrapper}>
        <h2>레이아웃 채우기 (fill)</h2>
        <div style={{ position: 'relative', width: '100%', height: '300px' }}>
          <Image
            src="https://images.unsplash.com/photo-1610214644596-f94d3f572a8c?q=80&w=2835&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
            alt="도시 야경"
            fill
            sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
            style={{ objectFit: 'cover' }}
          />
        </div>
        <p>이 이미지는 부모 요소의 공간을 채웁니다.</p>
      </div>
    </div>
  );
}

// src/app/page.module.css
/*
.container {
  padding: 2rem;
  max-width: 900px;
  margin: 0 auto;
  font-family: Arial, sans-serif;
}

.imageWrapper {
  margin-bottom: 3rem;
  border: 1px solid #ddd;
  padding: 1rem;
  text-align: center;
  background-color: #f9f9f9;
}

.imageWrapper h2 {
  margin-top: 0;
  color: #333;
}

.imageWrapper p {
  font-size: 0.9em;
  color: #666;
  margin-top: 1rem;
}

.fillImageWrapper {
  margin-bottom: 3rem;
  border: 1px solid #007bff;
  padding: 1rem;
  text-align: center;
  background-color: #eaf6ff;
}

.fillImageWrapper h2 {
  margin-top: 0;
  color: #007bff;
}

.fillImageWrapper p {
  font-size: 0.9em;
  color: #666;
  margin-top: 1rem;
}
*/

<Image> 컴포넌트의 주요 속성

  • src: 이미지 파일의 경로 (로컬 또는 외부 URL).
  • alt: 이미지에 대한 대체 텍스트. 접근성 및 SEO에 필수적입니다.
  • width, height: 이미지의 고유한 너비와 높이(픽셀). 정적 임포트나 fill을 사용하지 않는 경우 필수이며, 브라우저가 비율을 미리 확보해 CLS를 막도록 돕습니다.
  • preload: 실제 LCP 후보가 명확할 때 <Image preload />로 이미지 발견 시점을 앞당깁니다. Next.js 16에서는 기존 priority 대신 이 속성을 사용합니다. 일반적인 상단 이미지는 loading="eager"fetchPriority="high"가 더 적합할 수 있습니다.
  • fill: (불리언) 부모 요소의 크기에 맞춰 이미지를 채웁니다. 이 경우 widthheight 속성은 필요 없으며, 부모 요소에 position: relative 등의 스타일이 적용되어야 합니다.
  • sizes: fill을 사용하거나 CSS로 반응형 크기를 정할 때 실제 표시 폭을 브라우저에 알려 적절한 srcset 후보를 선택하게 합니다. (예: sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw")
  • quality: 이미지 압축 품질을 1에서 100 사이의 숫자로 지정합니다. Next.js 16은 next.configimages.qualities 허용 목록을 적용하며 기본 허용값은 75입니다. 다른 값을 사용하려면 허용 목록에도 추가합니다.
  • placeholder: 이미지가 로딩되는 동안 표시될 자리표시자.
    • blur: 작은 blurDataURL을 사용하여 흐릿한 이미지 효과를 보여줍니다. 지원 형식의 이미지를 정적으로 import하면 자동으로 적용됩니다.
    • empty: 투명한 이미지 자리표시자를 보여줍니다.
  • blurDataURL: placeholder="blur"와 함께 사용할 작은 Base64 인코딩 이미지 데이터 URL. import hero from "./hero.jpg"처럼 지원 형식의 이미지를 정적으로 import하면 Next.js가 자동 생성합니다. src="/logo.png" 같은 public 문자열 경로나 외부 이미지는 직접 제공해야 합니다.
  • loading: lazy (기본값, 지연 로딩) 또는 eager (즉시 로딩)를 지정합니다. preload와 동시에 지정하지 않습니다.
  • unoptimized: (불리언) Next.js의 이미지 최적화 기능을 사용하지 않고 순수 <img> 태그처럼 동작하게 합니다. 특수한 경우에만 사용합니다.

아래 보드는 실제 화면에 이미지를 배치할 때 width/height, fill, sizes, preload를 어떤 순서로 결정할지 정리한 것입니다.

이미지 속성은 레이아웃 안정성과 LCP 우선순위로 결정한다

먼저 이미지가 차지할 공간을 확정하고, 실제 표시 폭을 `sizes`로 알려 준 뒤, 첫 화면 대표 이미지에만 `preload`를 적용한다.

  1. 1. 공간을 먼저 고정

    고정 비율이면 width height , 부모 영역을 채우면 fill 을 쓴다.

  2. 2. 표시 폭을 알려 줌

    반응형 레이아웃에서는 sizes 가 후보 이미지 폭을 줄이는 핵심이다.

  3. 3. LCP 후보만 당김

    첫 화면 대표 이미지만 preload 를 사용한다. 목록 이미지에 남발하지 않는다.

  4. 4. 원격 출처를 제한

    외부 이미지는 remotePatterns 로 protocol, host, path를 좁힌다.

상황먼저 묻는 질문선택틀리면 생기는 문제
이미지 비율이 고정원본 또는 표시 비율을 아는가?width + height공간 예약 실패로 CLS가 늘어난다.
부모 영역 채우기부모가 position과 높이를 갖는가?fill + object-fit부모 높이가 없으면 이미지가 보이지 않는다.
반응형 폭뷰포트별 표시 폭이 다른가?sizes모바일에도 큰 후보 이미지를 받을 수 있다.
첫 화면 대표 이미지LCP 후보인가?preload남발하면 lazy loading 이점이 줄어든다.

아래 판단표는 next/image 속성을 의미, 자리, 전송량, 출처 순서로 결정하는 기준을 정리합니다.

next/image 속성은 의미, 자리, 전송량, 출처 순서로 결정한다

`src`만 채우면 최적화가 끝난 것처럼 보이지만, 실제 품질은 접근성, CLS 방지, 후보 이미지 크기, 외부 출처 제한을 함께 정할 때 나온다.

  1. 무슨 이미지인가

    src 와 alt 로 정보 이미지인지 장식인지 정한다.

  2. 자리를 예약했나

    width/height 또는 fill 로 CLS를 막는다.

  3. 얼마나 보낼까

    sizes 와 quality 로 화면 폭에 맞는 후보를 보낸다.

  4. 어디서 가져오나

    remotePatterns 와 preload 를 필요할 때만 좁게 쓴다.

속성 묶음결정 기준잘못 쓰면 보이는 신호판정
src alt이미지를 못 보는 사용자에게도 같은 정보가 전달되는가?외부 URL 로딩 실패, 장식 이미지에 장황한 설명항상 먼저 확정
width height fill브라우저가 이미지 영역을 로딩 전에 계산할 수 있는가?이미지가 늦게 뜨며 본문이 밀리고 CLS가 증가레이아웃 계약
sizes quality모바일과 데스크톱 표시 폭 차이를 후보 이미지에 반영했는가?모바일에서 필요 이상으로 큰 이미지를 다운로드전송량 조절
preload첫 화면 LCP 후보 이미지가 맞는가?여러 이미지에 우선순위를 줘 네트워크 순서가 흐려짐대표 이미지에만
remotePatterns외부 이미지의 protocol, hostname, pathname을 좁게 제한했는가?도메인이 너무 넓거나 누락되어 보안/로딩 문제가 생김출처 경계

외부 이미지 도메인 설정

외부 이미지 URL을 사용하는 경우, Next.js가 해당 이미지를 최적화할 수 있도록 next.config.ts에 프로토콜·호스트·경로를 구체적으로 허용합니다.

next.config.ts
import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'images.unsplash.com',
        pathname: '/**',
      },
      {
        protocol: 'https',
        hostname: 'www.example.com',
        pathname: '/images/**',
      },
    ],
  },
};

export default nextConfig;

next.config.ts 파일을 수정한 후에는 개발 서버를 다시 시작해야 변경 사항이 적용됩니다.


이미지 최적화를 위한 추가 팁

이미지 최적화는 원본 축소, 속성 계약, 렌더 검증 순서로 점검한다

좋은 결과는 <Image> 하나로 끝나지 않는다. 원본을 줄이고, 화면 조건에 맞게 속성을 정한 뒤, 실제 전송량과 Core Web Vitals를 다시 확인해야 한다.

  1. 1

    원본 정리 표시 크기보다 큰 원본을 줄이고 사진, 로고, 아이콘 성격을 나눈다.

  2. 2

    속성 계약 공간, 후보 폭, 우선순위, 원격 출처를 명시한다.

  3. 3

    실측 확인 LCP, CLS, 전송 KB가 실제로 줄었는지 본다.

단계해야 할 일결정 값통과 기준
원본최대 표시 크기보다 과한 이미지를 줄인다.사진은 WebP/AVIF, 로고는 SVG/PNG불필요한 원본 KB 감소
레이아웃이미지 자리를 먼저 예약한다.`width/height` 또는 `fill`CLS가 안정적
전송실제 표시 폭에 맞는 후보 이미지를 고른다.`sizes`, `quality`모바일에서 큰 후보 미전송
검증첫 화면과 스크롤 후 이미지를 나눠 측정한다.Lighthouse, Network, Core Web VitalsLCP·CLS·KB가 함께 개선

아래 다이어그램은 next/image를 사용할 때 이미지 출처, 크기, 우선순위, 접근성 속성을 함께 점검하는 기준입니다.

next/image 최적화 결정 기준

이미지 하나를 넣을 때마다 아래 다섯 질문을 통과해야 한다. 어느 하나가 빠지면 로딩은 되더라도 성능이나 접근성 문제가 남는다.

  1. 출처
    로컬인가, 외부인가?

    외부 URL이면 `remotePatterns`로 허용 범위를 좁힌다. 임의 도메인을 열어 두지 않는다.

  2. 공간
    이미지 자리가 먼저 잡히는가?

    `width/height` 또는 `fill`로 레이아웃 공간을 확정해 CLS를 줄인다.

  3. 후보 폭
    브라우저가 필요한 크기를 고를 수 있는가?

    `sizes`가 없으면 모바일에서도 큰 후보 이미지를 받을 수 있다.

  4. 우선순위
    정말 첫 화면 대표 이미지인가?

    LCP 후보가 아니면 `preload`를 붙이지 않는다. 여러 이미지를 동시에 당기지 않는다.

  5. 대체 텍스트
    이미지가 의미를 전달하는가?

    정보 이미지에는 구체적인 `alt`, 장식 이미지는 빈 `alt`로 스크린리더 부담을 줄인다.

아래 다이어그램은 next/image의 크기 속성, preload, 외부 경로 설정이 이미지 최적화에 연결되는 지점을 보여줍니다.

크기 속성, preload, 원격 도메인 설정은 서로 다른 비용을 줄인다

`next/image` 속성은 하나의 묶음처럼 보이지만 레이아웃, 전송량, 보안 경계를 각각 다룬다.

  1. 1
    핵심 1

    크기 속성, preload, 원격 도메인 설정은 서로 다른 비용을 줄인다

  2. 2
    핵심 2

    크기 속성, preload, 원격 도메인 설정은 서로 다른 비용을 줄인다

  3. 3
    핵심 3

    `next/image` 속성은 하나의 묶음처럼 보이지만 레이아웃, 전송량, 보안 경계를 각각 다룬다.

  4. 4
    핵심 4

    width/height 자리 예약 CLS 방지 sizes 필요한 후보 선택 과한 다운로드 방지 preload 첫 화면 이미지 LCP 개선 remotePatterns…

  • 적절한 이미지 크기: 이미지를 업로드하기 전에 사용될 최대 크기에 맞춰 적절히 리사이즈하는 것이 좋습니다. next/image가 반응형 이미지를 처리하지만, 너무 큰 원본 이미지를 업로드하는 것은 여전히 비효율적일 수 있습니다.
  • 파일 형식 선택
    • JPEG: 사진과 같이 색상이 풍부한 이미지에 적합하며, 손실 압축을 통해 파일 크기를 줄일 수 있습니다.
    • PNG: 투명도가 필요한 이미지나 로고, 아이콘 등 색상 수가 적고 선명도가 중요한 이미지에 적합합니다. (비손실 압축)
    • WebP / AVIF: 최신 이미지 형식으로 JPEG나 PNG보다 효율적인 압축을 제공할 수 있습니다. next/image의 기본 출력 형식은 WebP이며, AVIF를 함께 사용하려면 next.configimages.formats['image/avif', 'image/webp']처럼 설정합니다.
    • SVG: 로고, 아이콘 등 벡터 기반의 그래픽에 적합합니다. 해상도에 구애받지 않고 확대/축소해도 깨지지 않으며, 파일 크기가 매우 작습니다. <Image> 컴포넌트가 아닌 일반 <img> 태그로 사용하거나 React 컴포넌트로 직접 임포트할 수 있습니다.
  • Placeholder 사용: placeholder="blur" 또는 placeholder="empty"를 사용하여 이미지가 로딩되는 동안 사용자 경험을 개선합니다. LCP 이미지가 명확하다면 preload와 함께 사용할 수 있지만, 작은 Base64 placeholder가 실제 전송 비용을 늘리지 않는지도 확인합니다.
  • 캐싱 전략: CDN(Content Delivery Network)을 사용하여 사용자에게 물리적으로 가장 가까운 서버에서 이미지를 제공하여 로딩 속도를 단축합니다. Next.js는 기본적으로 Vercel에 배포할 때 내장된 이미지 최적화 서비스를 활용합니다.
  • Lazy Loading 활용: 기본적으로 next/image는 지연 로딩을 사용하므로, 뷰포트 바깥의 이미지는 스크롤될 때까지 로딩되지 않습니다. 이는 초기 페이지 로딩 속도를 크게 개선합니다.

이미지 최적화는 단순히 이미지를 웹 페이지에 넣는 것을 넘어, 사용자 경험과 웹 성능을 극대화하기 위한 필수적인 과정입니다.

Next.js의 next/image 컴포넌트를 올바르게 활용하면 개발자가 복잡한 최적화 기법을 직접 구현할 필요 없이 뛰어난 이미지 성능을 얻을 수 있습니다.

아래 다이어그램은 이미지 최적화를 렌더링 비용, 전송 비용, 측정 지표 순서로 좁혀 봅니다.

next/image 최적화는 LCP, CLS, 원격 이미지 정책으로 검증한다

최종 점검은 “속성을 넣었는가”가 아니라 실제 비용이 줄었는가입니다. 첫 화면 시간, 레이아웃 안정성, 외부 이미지 설정을 분리해서 본다.

  1. LCP

    첫 화면 대표 이미지만 빠르게 요청되어야 한다. preload sizes

  2. CLS

    미지가 늦게 와도 문서 흐름이 밀리지 않아야 한다. width/height fill

  3. Remote

    외부 이미지는 허용된 출처와 경로만 최적화한다. remotePatterns

검증 축좋은 상태나쁜 신호다음 조치
LCPhero 이미지가 첫 요청 묶음에 포함된다.스크롤 아래 이미지도 함께 preload 처리됨첫 화면 대표 이미지 하나만 남긴다.
CLS이미지 공간이 로딩 전에 예약된다.이미지가 뜨며 본문이 아래로 밀림크기나 부모 높이, aspect ratio를 고정한다.
Remote허용된 host와 path만 통과한다.외부 이미지가 400/403으로 실패`remotePatterns`를 좁고 명확하게 둔다.
KB모바일에서 작은 후보 이미지를 받는다.모바일도 데스크톱급 원본 다운로드`sizes`와 원본 크기를 다시 맞춘다.