이미지 최적화
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는 컨테이너의 최대 폭 900px을 기준으로 둔 보수적인 상한이며, 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의 원본을 제공합니다. 레이아웃이나 다른 이미지 속성까지 없애는 옵션은 아닙니다.
외부 이미지 도메인 설정
외부 이미지 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를 수정한 후에는 개발 서버를 다시 시작합니다.
이미지 최적화를 위한 추가 팁
- 적절한 이미지 크기: 이미지를 업로드하기 전에 사용될 최대 크기에 맞춰 적절히 리사이즈하는 것이 좋습니다.
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 컴포넌트를 올바르게 활용하면 개발자가 복잡한 최적화 기법을 직접 구현할 필요 없이 뛰어난 이미지 성능을 얻을 수 있습니다.