이미지 최적화
next/image의 크기·우선순위·반응형 속성과 외부 호스트 설정으로 레이아웃 이동과 이미지 전송량을 줄입니다.
웹 애플리케이션의 성능 최적화는 사용자 경험과 SEO에 모두 영향을 주는 중요한 작업입니다.
특히 이미지 최적화는 로딩 속도 저하의 가장 흔한 원인 중 하나라 반드시 신경 써야 합니다.
Next.js는 내장 컴포넌트 next/image를 통해
고성능 이미지 처리를 쉽게 구현할 수 있게 해줍니다.
이 절에서는 이미지 최적화의 중요성부터 next/image 컴포넌트의 사용법, 그리고 이미지 최적화를 위한 다양한 기법들을 상세히 알아보겠습니다.
큰 이미지를 그대로 보내면 다운로드가 늦고, 크기를 예약하지 않으면 화면이 밀리며, 첫 화면 이미지를 늦게 보내면 LCP가 나빠진다. `next/image`는 이 세 비용을 한 번에 관리한다.
- LCP
첫 화면 대표 이미지가 얼마나 빨리 보이는지
- CLS
미지 로딩 중 레이아웃이 밀리지 않는지
- 전송량
기기와 브라우저에 맞는 크기와 포맷을 보내는지
- 1
공간 예약 `width/height` 또는 `fill`로 이미지 자리를 먼저 잡는다.
- 2
후보 폭 제한 `sizes`로 뷰포트별 실제 표시 폭을 알려 준다.
- 3
첫 화면 우선 LCP 후보에만 `preload`를 붙여 로딩 순서를 당긴다.
- 4
원격 출처 제한 외부 이미지는 `remotePatterns`로 허용 범위를 좁힌다.
| 문제 | 원인 | next/image 대응 | 확인 지표 |
|---|---|---|---|
| 느린 첫 화면 | hero 이미지가 lazy로 밀림 | preload 를 LCP 후보에만 적용 | LCP 시간 |
| 화면 흔들림 | 이미지 자리 크기 미정 | width/height 또는 fill | CLS |
| 불필요한 다운로드 | 모바일에도 큰 원본 전송 | 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.formats에image/avif를 추가합니다. - 이미지 품질 최적화: 지정된 품질 설정에 따라 이미지 압축을 수행합니다.
- Cumulative Layout Shift (CLS) 방지: 이미지의
width와height를 미리 정의하여 이미지 로딩 시 레이아웃이 밀리는 현상을 방지합니다. - 이미지 호스팅 최적화: Next.js 서버 또는 CDN을 통해 이미지를 효율적으로 제공합니다.
next/image 설치
Next.js 13 이상을 사용하는 App Router 프로젝트라면 별도의 설치 없이 바로 사용할 수 있습니다.
기본 사용법
<Image> 컴포넌트에는 src와 의미에 맞는 alt가 필요합니다.
public 문자열 경로나 원격 URL을 사용하면 width와 height로 비율을 알려야 하지만, 정적 import가 크기를 제공하거나 fill로 부모 영역을 채우는 경우에는 두 속성을 생략합니다.
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: (불리언) 부모 요소의 크기에 맞춰 이미지를 채웁니다. 이 경우width와height속성은 필요 없으며, 부모 요소에position: relative등의 스타일이 적용되어야 합니다.sizes:fill을 사용하거나 CSS로 반응형 크기를 정할 때 실제 표시 폭을 브라우저에 알려 적절한srcset후보를 선택하게 합니다. (예:sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw")quality: 이미지 압축 품질을 1에서 100 사이의 숫자로 지정합니다. Next.js 16은next.config의images.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를 어떤 순서로 결정할지 정리한 것입니다.
먼저 이미지가 차지할 공간을 확정하고, 실제 표시 폭을 `sizes`로 알려 준 뒤, 첫 화면 대표 이미지에만 `preload`를 적용한다.
- 1. 공간을 먼저 고정
고정 비율이면 width height , 부모 영역을 채우면 fill 을 쓴다.
- 2. 표시 폭을 알려 줌
반응형 레이아웃에서는 sizes 가 후보 이미지 폭을 줄이는 핵심이다.
- 3. LCP 후보만 당김
첫 화면 대표 이미지만 preload 를 사용한다. 목록 이미지에 남발하지 않는다.
- 4. 원격 출처를 제한
외부 이미지는 remotePatterns 로 protocol, host, path를 좁힌다.
| 상황 | 먼저 묻는 질문 | 선택 | 틀리면 생기는 문제 |
|---|---|---|---|
| 이미지 비율이 고정 | 원본 또는 표시 비율을 아는가? | width + height | 공간 예약 실패로 CLS가 늘어난다. |
| 부모 영역 채우기 | 부모가 position과 높이를 갖는가? | fill + object-fit | 부모 높이가 없으면 이미지가 보이지 않는다. |
| 반응형 폭 | 뷰포트별 표시 폭이 다른가? | sizes | 모바일에도 큰 후보 이미지를 받을 수 있다. |
| 첫 화면 대표 이미지 | LCP 후보인가? | preload | 남발하면 lazy loading 이점이 줄어든다. |
아래 판단표는 next/image 속성을 의미, 자리, 전송량, 출처 순서로 결정하는 기준을 정리합니다.
`src`만 채우면 최적화가 끝난 것처럼 보이지만, 실제 품질은 접근성, CLS 방지, 후보 이미지 크기, 외부 출처 제한을 함께 정할 때 나온다.
- 무슨 이미지인가
src 와 alt 로 정보 이미지인지 장식인지 정한다.
- 자리를 예약했나
width/height 또는 fill 로 CLS를 막는다.
- 얼마나 보낼까
sizes 와 quality 로 화면 폭에 맞는 후보를 보낸다.
- 어디서 가져오나
remotePatterns 와 preload 를 필요할 때만 좁게 쓴다.
| 속성 묶음 | 결정 기준 | 잘못 쓰면 보이는 신호 | 판정 |
|---|---|---|---|
| src alt | 이미지를 못 보는 사용자에게도 같은 정보가 전달되는가? | 외부 URL 로딩 실패, 장식 이미지에 장황한 설명 | 항상 먼저 확정 |
| width height fill | 브라우저가 이미지 영역을 로딩 전에 계산할 수 있는가? | 이미지가 늦게 뜨며 본문이 밀리고 CLS가 증가 | 레이아웃 계약 |
| sizes quality | 모바일과 데스크톱 표시 폭 차이를 후보 이미지에 반영했는가? | 모바일에서 필요 이상으로 큰 이미지를 다운로드 | 전송량 조절 |
| preload | 첫 화면 LCP 후보 이미지가 맞는가? | 여러 이미지에 우선순위를 줘 네트워크 순서가 흐려짐 | 대표 이미지에만 |
| remotePatterns | 외부 이미지의 protocol, hostname, pathname을 좁게 제한했는가? | 도메인이 너무 넓거나 누락되어 보안/로딩 문제가 생김 | 출처 경계 |
외부 이미지 도메인 설정
외부 이미지 URL을 사용하는 경우, Next.js가 해당 이미지를 최적화할 수 있도록 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
원본 정리 표시 크기보다 큰 원본을 줄이고 사진, 로고, 아이콘 성격을 나눈다.
- 2
속성 계약 공간, 후보 폭, 우선순위, 원격 출처를 명시한다.
- 3
실측 확인 LCP, CLS, 전송 KB가 실제로 줄었는지 본다.
| 단계 | 해야 할 일 | 결정 값 | 통과 기준 |
|---|---|---|---|
| 원본 | 최대 표시 크기보다 과한 이미지를 줄인다. | 사진은 WebP/AVIF, 로고는 SVG/PNG | 불필요한 원본 KB 감소 |
| 레이아웃 | 이미지 자리를 먼저 예약한다. | `width/height` 또는 `fill` | CLS가 안정적 |
| 전송 | 실제 표시 폭에 맞는 후보 이미지를 고른다. | `sizes`, `quality` | 모바일에서 큰 후보 미전송 |
| 검증 | 첫 화면과 스크롤 후 이미지를 나눠 측정한다. | Lighthouse, Network, Core Web Vitals | LCP·CLS·KB가 함께 개선 |
아래 다이어그램은 next/image를 사용할 때 이미지 출처, 크기, 우선순위, 접근성 속성을 함께 점검하는 기준입니다.
이미지 하나를 넣을 때마다 아래 다섯 질문을 통과해야 한다. 어느 하나가 빠지면 로딩은 되더라도 성능이나 접근성 문제가 남는다.
- 출처로컬인가, 외부인가?
외부 URL이면 `remotePatterns`로 허용 범위를 좁힌다. 임의 도메인을 열어 두지 않는다.
- 공간이미지 자리가 먼저 잡히는가?
`width/height` 또는 `fill`로 레이아웃 공간을 확정해 CLS를 줄인다.
- 후보 폭브라우저가 필요한 크기를 고를 수 있는가?
`sizes`가 없으면 모바일에서도 큰 후보 이미지를 받을 수 있다.
- 우선순위정말 첫 화면 대표 이미지인가?
LCP 후보가 아니면 `preload`를 붙이지 않는다. 여러 이미지를 동시에 당기지 않는다.
- 대체 텍스트이미지가 의미를 전달하는가?
정보 이미지에는 구체적인 `alt`, 장식 이미지는 빈 `alt`로 스크린리더 부담을 줄인다.
아래 다이어그램은 next/image의 크기 속성, preload, 외부 경로 설정이 이미지 최적화에 연결되는 지점을 보여줍니다.
`next/image` 속성은 하나의 묶음처럼 보이지만 레이아웃, 전송량, 보안 경계를 각각 다룬다.
- 1핵심 1
크기 속성, preload, 원격 도메인 설정은 서로 다른 비용을 줄인다
- 2핵심 2
크기 속성, preload, 원격 도메인 설정은 서로 다른 비용을 줄인다
- 3핵심 3
`next/image` 속성은 하나의 묶음처럼 보이지만 레이아웃, 전송량, 보안 경계를 각각 다룬다.
- 4핵심 4
width/height 자리 예약 CLS 방지 sizes 필요한 후보 선택 과한 다운로드 방지 preload 첫 화면 이미지 LCP 개선 remotePatterns…
- 적절한 이미지 크기: 이미지를 업로드하기 전에 사용될 최대 크기에 맞춰 적절히 리사이즈하는 것이 좋습니다.
next/image가 반응형 이미지를 처리하지만, 너무 큰 원본 이미지를 업로드하는 것은 여전히 비효율적일 수 있습니다. -
파일 형식 선택
- JPEG: 사진과 같이 색상이 풍부한 이미지에 적합하며, 손실 압축을 통해 파일 크기를 줄일 수 있습니다.
- PNG: 투명도가 필요한 이미지나 로고, 아이콘 등 색상 수가 적고 선명도가 중요한 이미지에 적합합니다. (비손실 압축)
- WebP / AVIF: 최신 이미지 형식으로 JPEG나 PNG보다 효율적인 압축을 제공할 수 있습니다.
next/image의 기본 출력 형식은 WebP이며, AVIF를 함께 사용하려면next.config의images.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 컴포넌트를 올바르게 활용하면 개발자가 복잡한 최적화 기법을 직접 구현할 필요 없이 뛰어난 이미지 성능을 얻을 수 있습니다.
아래 다이어그램은 이미지 최적화를 렌더링 비용, 전송 비용, 측정 지표 순서로 좁혀 봅니다.
최종 점검은 “속성을 넣었는가”가 아니라 실제 비용이 줄었는가입니다. 첫 화면 시간, 레이아웃 안정성, 외부 이미지 설정을 분리해서 본다.
- LCP
첫 화면 대표 이미지만 빠르게 요청되어야 한다. preload sizes
- CLS
미지가 늦게 와도 문서 흐름이 밀리지 않아야 한다. width/height fill
- Remote
외부 이미지는 허용된 출처와 경로만 최적화한다. remotePatterns
| 검증 축 | 좋은 상태 | 나쁜 신호 | 다음 조치 |
|---|---|---|---|
| LCP | hero 이미지가 첫 요청 묶음에 포함된다. | 스크롤 아래 이미지도 함께 preload 처리됨 | 첫 화면 대표 이미지 하나만 남긴다. |
| CLS | 이미지 공간이 로딩 전에 예약된다. | 이미지가 뜨며 본문이 아래로 밀림 | 크기나 부모 높이, aspect ratio를 고정한다. |
| Remote | 허용된 host와 path만 통과한다. | 외부 이미지가 400/403으로 실패 | `remotePatterns`를 좁고 명확하게 둔다. |
| KB | 모바일에서 작은 후보 이미지를 받는다. | 모바일도 데스크톱급 원본 다운로드 | `sizes`와 원본 크기를 다시 맞춘다. |