이미지 최적화
next/image의 크기·우선순위·반응형 속성과 외부 호스트 설정으로 레이아웃 이동과 이미지 전송량을 줄입니다.
웹 애플리케이션의 성능 최적화는 사용자 경험과 SEO에 모두 영향을 주는 중요한 작업입니다.
특히 최적화하지 않은 큰 이미지는 로딩 속도를 늦추므로, 표시 크기와 전송량을 함께 살펴야 합니다.
Next.js는 내장 컴포넌트 next/image를 통해
고성능 이미지 처리를 쉽게 구현할 수 있게 해 줍니다.
이 절에서는 이미지 최적화의 중요성부터 next/image 컴포넌트의 사용법, 그리고 이미지 최적화를 위한 다양한 기법들을 상세히 알아보겠습니다.
왜 이미지를 최적화해야 하나요?
이미지는 웹 페이지에서 시각적인 매력을 더하고 정보를 전달하는 데 필수적이지만, 동시에 페이지 로딩 속도를 늦추는 주범이 되기도 합니다.
- 느린 로딩 속도: 용량이 큰 이미지는 네트워크를 통해 전송되는 데 시간이 오래 걸려 페이지 로딩을 지연시킵니다. 이는 사용자 이탈률 증가로 이어질 수 있습니다.
- 데이터 사용량 증가: 모바일 환경에서는 사용자의 데이터 요금 부담을 가중시킬 수 있습니다.
- Core Web Vitals 저하: 이미지 지연과 공간 이동은 LCP·CLS 같은 Core Web Vitals 및 페이지 경험에 영향을 줄 수 있습니다. 이 지표만으로 검색 순위가 정해지는 것은 아닙니다.
- 불필요한 리소스 낭비: 사용자 기기의 성능에 맞지 않는 고해상도 이미지를 전송하는 것은 불필요한 리소스 낭비입니다.
이미지 최적화는 이러한 문제들을 해결하여 웹 성능을 크게 개선하는 데 기여합니다.
next/image 컴포넌트 사용하기
Next.js는 이미지 최적화를 위해 특별히 설계된 <Image> 컴포넌트를 next/image에서 제공합니다.
기본 이미지 로더는 요청에 맞춰 크기·품질·형식을 변환합니다. 정적 import의 크기 정보와 blur 데이터 준비는 이 요청 시점의 변환과 구분합니다.
주요 최적화 기능- 반응형 이미지: 기기 크기에 따라 최적화된 크기의 이미지를 자동으로 제공합니다.
- 지연 로딩(Lazy Loading): 브라우저가 정한 뷰포트 주변 거리까지 이미지 로딩을 미룹니다.
- 이미지 형식 최적화: 기본 설정은 브라우저가 지원하면 WebP로 변환합니다. AVIF까지 제공하려면
images.formats에image/avif를 추가합니다. - 이미지 품질 최적화: 지정된 품질 설정에 따라 이미지 압축을 수행합니다.
- 공간 예약: 고유 크기나 부모 영역을 지정해 이미지가 도착할 자리를 확보합니다. 주변 CSS에 의한 이동까지 모두 제거하는 것은 아닙니다.
- 이미지 호스팅 최적화: Next.js 서버 또는 CDN을 통해 이미지를 효율적으로 제공합니다.
next/image 설치
Next.js 13 이상을 사용하는 App Router 프로젝트라면 별도의 설치 없이 바로 사용할 수 있습니다.
기본 사용법
<Image> 컴포넌트에는 src와 의미에 맞는 alt가 필요합니다.
public 문자열 경로나 원격 URL을 사용하면 width와 height로 비율을 알려야 하지만, 정적 import가 크기를 제공하거나 fill로 부모 영역을 채우는 경우에는 두 속성을 생략합니다.
public/logo.png를 준비하고, 코드 끝 주석의 CSS는 별도 src/app/page.module.css 파일에 저장합니다. 원격 이미지의 크기 속성은 사용하는 실제 이미지 비율에 맞춥니다.
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: 900px) 100vw, 900px"
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;
}
*/실습의 fill 이미지는 한 열 전체를 채웁니다. sizes는 컨테이너의 최대 폭 900 px을 기준으로 둔 보수적인 상한이며, padding·테두리를 뺀 실제 표시 폭보다 클 수 있습니다. 배포 화면의 폭에 맞춰 더 정확히 좁힐 수 있습니다.
둘째 이미지의 preload는 사용법을 보여 주는 설정입니다. 이 이미지가 실제 LCP 요소인지는 화면 크기와 콘텐츠로 확인해야 합니다.
<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후보를 선택하게 합니다. 이 값은 CSS의 표시 폭 자체를 바꾸지 않습니다.quality: 이미지 압축 품질을 1에서 100 사이의 숫자로 지정합니다. Next.js 16은next.config의images.qualities허용 목록을 적용하며 기본 허용값은75입니다. 다른 값을 사용하려면 허용 목록에도 추가합니다.placeholder: 이미지가 로딩되는 동안 표시될 자리표시자.blur: 작은blurDataURL을 사용하여 흐릿한 이미지 효과를 보여 줍니다. 지원 형식의 정적 import는 blur 데이터를 자동으로 제공하지만, 이 효과는placeholder="blur"로 선택합니다.empty: 별도의 placeholder를 표시하지 않습니다.
blurDataURL:placeholder="blur"와 함께 사용할 작은 Base64 인코딩 이미지 데이터 URL.import hero from "./hero.jpg"처럼 지원 형식의 이미지를 정적으로 import하면 Next.js가 자동 생성합니다.src="/logo.png"같은public문자열 경로나 외부 이미지는 직접 제공해야 합니다.loading:lazy(기본값, 지연 로딩) 또는eager(즉시 로딩)를 지정합니다.preload와 동시에 지정하지 않습니다.unoptimized: (불리언) 크기·품질·형식 변환 없이src의 원본을 제공합니다. 레이아웃이나 다른 이미지 속성까지 없애는 옵션은 아닙니다.
NEXT · 12.1
이미지 공간·파일 후보·로딩 시점은 서로 다른 설정입니다
본문의 세 Image 예제를 같은 질문으로 비교합니다. 이 설정들은 독립된 역할을 하므로 순서대로 실행하는 세 단계로 읽지 않습니다.
본문의 세 예제
- 로컬 로고 · 500 × 300
- width·height로 비율을 알리고 CSS의 max-width와 height: auto로 반응형 표시합니다. blurDataURL은 직접 제공한 미리보기입니다.
- 외부 사진 · 800 × 600
- 같은 방식으로 비율과 CSS 표시 크기를 구분합니다. preload는 발견 시점을 앞당기는 예시이며 실제 LCP 요소인지는 별도로 확인합니다.
- fill 사진 · 부모 높이 300px
- relative인 부모를 채웁니다. sizes는 최대 900px을 기준으로 한 후보 선택 힌트이며 padding을 뺀 실제 폭보다 클 수 있습니다.
설정이 답하는 세 질문
- 어디에 얼마나 표시하나요?
- width·height의 비율 또는 fill의 부모 크기와 CSS가 레이아웃 공간을 정합니다.
- 어느 파일 후보를 고르나요?
- sizes가 예상 표시 폭을 알립니다. 브라우저가 srcset 중 후보를 선택하며 sizes 자체는 CSS 폭을 바꾸지 않습니다.
- 언제 발견하고 요청하나요?
- preload·loading은 발견과 요청 시점에 관여합니다. lazy의 요청 기준은 브라우저가 정하며 화면에 닿는 순간으로 고정되지 않습니다.
blur 미리보기는 원본 다운로드를 대체하지 않습니다. 포맷·quality·표시 폭을 조정해도 전송량 감소와 LCP 개선은 실제 이미지와 화면에서 측정해야 합니다. 외부 URL은 remotePatterns 허용 범위도 확인합니다.
외부 이미지 도메인 설정
외부 이미지 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;이 예제는 Unsplash의 모든 경로를 허용하고 search를 생략해 URL의 쿼리도 허용합니다. 실제 서비스에서는 필요한 출처 범위로 제한합니다. next.config.ts를 수정한 후에는 개발 서버를 다시 시작합니다.
예시
로고의 원본 비율이 500:300이고 CSS로 너비를 300 px, 높이를 auto로 정했다면 표시 높이는 300 × 300 ÷ 500 = 180px입니다. 반면 800×600 이미지를 300×300 부모에 fill과 objectFit: 'cover'로 넣으면 비율을 유지한 크기는 400×300이 되어 가로 100 px이 잘립니다. 가운데 정렬이라면 양쪽에서 50 px씩 잘립니다. 이는 표시 영역의 계산이며, sizes가 이 CSS 크기나 잘림을 바꾸지는 않습니다.
sizes는 브라우저가 다운로드 후보를 고르는 힌트입니다. 화면에서 필요한 폭을 과하게 적으면 더 큰 파일을 선택할 수 있지만, 힌트를 작게 쓴다고 부모의 레이아웃도 작아지지는 않습니다. 먼저 실제 표시 영역을 정하고 그 폭에 맞는 힌트를 제공합니다.
직접 확인하기
로고 너비만 250 px로 바꾸면 높이는 얼마인가요? 또 허용 품질을 [50, 75, 100]으로 설정한 상태에서 <Image quality={80}>를 쓰는 경우와 이미지 최적화 API에 허용되지 않은 q=80을 직접 요청하는 경우를 구별해 보세요.
계산과 요청 조건 확인
높이는 150 px입니다. 현재 next/image의 컴포넌트 품질은 허용 목록에서 가까운 75를 사용하지만, 최적화 API의 직접 요청은 허용되지 않은 값이면 400 응답 대상입니다. 이 설정 차이를 외부 원본 이미지 서버의 응답이나 CSS 크기와 섞지 않습니다. 공식 Image 문서의 품질 조건을 기준으로 확인할 수 있습니다.
이미지 최적화를 위한 추가 팁
- 적절한 이미지 크기: 이미지를 업로드하기 전에 사용될 최대 크기에 맞춰 적절히 리사이즈하는 것이 좋습니다.
next/image가 반응형 이미지를 처리하지만, 너무 큰 원본 이미지를 업로드하는 것은 여전히 비효율적일 수 있습니다. -
파일 형식 선택
- JPEG: 사진과 같이 색상이 풍부한 이미지에 적합하며, 손실 압축을 통해 파일 크기를 줄일 수 있습니다.
- PNG: 투명도가 필요한 이미지나 로고, 아이콘 등 색상 수가 적고 선명도가 중요한 이미지에 적합합니다. (비손실 압축)
- WebP / AVIF: 최신 이미지 형식으로 JPEG나 PNG보다 효율적인 압축을 제공할 수 있습니다. 기본 형식 목록은 WebP이며 브라우저의
Accept와 원본 조건에 따라 적용됩니다. AVIF를 함께 사용하려면next.config의images.formats를['image/avif', 'image/webp']처럼 설정합니다. - SVG: 로고, 아이콘 등 벡터 기반의 그래픽에 적합합니다. 벡터 도형은 확대해도 선명하지만 복잡도에 따라 파일 크기는 달라집니다. 일반
<img>나<Image unoptimized>로 표시할 수 있습니다. SVG를 React 컴포넌트로 import하려면 별도 로더 설정이 필요합니다.
- Placeholder 사용:
placeholder="blur"또는placeholder="empty"를 사용하여 이미지가 로딩되는 동안 사용자 경험을 개선합니다. LCP 이미지가 명확하다면preload와 함께 사용할 수 있지만, 작은 Base64 placeholder가 실제 전송 비용을 늘리지 않는지도 확인합니다. - 캐싱 전략: CDN(Content Delivery Network)을 사용하여 사용자에게 물리적으로 가장 가까운 서버에서 이미지를 제공하여 로딩 속도를 단축합니다. Next.js는 기본적으로 Vercel에 배포할 때 내장된 이미지 최적화 서비스를 활용합니다.
- Lazy Loading 활용: 기본값은
lazy입니다. 브라우저가 뷰포트 주변의 이미지를 미리 가져올 수 있으므로, 화면 밖이라는 이유만으로 아직 요청되지 않았다고 단정하지 않습니다.
이미지 최적화는 단순히 이미지를 웹 페이지에 넣는 것을 넘어, 사용자 경험과 웹 성능을 극대화하기 위한 필수적인 과정입니다.
Next.js의 next/image 컴포넌트를 올바르게 활용하면 개발자가 복잡한 최적화 기법을 직접 구현할 필요 없이 뛰어난 이미지 성능을 얻을 수 있습니다.