이미지 최적화
웹 애플리케이션의 성능 최적화는 사용자 경험을 향상시키고 검색 엔진 최적화(SEO)에도 긍정적인 영향을 미치는 중요한 작업입니다. 그중에서도 이미지 최적화는 웹 페이지 로딩 속도를 저해하는 가장 흔한 원인 중 하나이므로, 반드시 신경 써야 할 부분입니다. Next.js는 이미지 최적화를 위한 강력한 내장 컴포넌트인 next/image
를 제공하여 개발자가 손쉽게 고성능 이미지를 구현할 수 있도록 돕습니다.
이 절에서는 이미지 최적화의 중요성부터 next/image
컴포넌트의 사용법, 그리고 이미지 최적화를 위한 다양한 기법들을 상세히 알아보겠습니다.
왜 이미지를 최적화해야 하나요?
이미지는 웹 페이지에서 시각적인 매력을 더하고 정보를 전달하는 데 필수적이지만, 동시에 페이지 로딩 속도를 늦추는 주범이 되기도 합니다.
- 느린 로딩 속도: 용량이 큰 이미지는 네트워크를 통해 전송되는 데 시간이 오래 걸려 페이지 로딩을 지연시킵니다. 이는 사용자 이탈률 증가로 이어질 수 있습니다.
- 데이터 사용량 증가: 모바일 환경에서는 사용자의 데이터 요금 부담을 가중시킬 수 있습니다.
- 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 등 최신 이미지 형식으로 자동 변환하여 파일 크기를 줄입니다 (브라우저 지원 여부 확인 후).
- 이미지 품질 최적화: 지정된 품질 설정에 따라 이미지 압축을 수행합니다.
- Cumulative Layout Shift (CLS) 방지: 이미지의
width
와height
를 미리 정의하여 이미지 로딩 시 레이아웃이 밀리는 현상을 방지합니다. - 이미지 호스팅 최적화: Next.js 서버 또는 CDN을 통해 이미지를 효율적으로 제공합니다.
next/image
설치
Next.js 13 이상을 사용하는 App Router 프로젝트라면 별도의 설치 없이 바로 사용할 수 있습니다.
기본 사용법
<Image>
컴포넌트는 src
, width
, height
, alt
속성이 필수적입니다.
// 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="/nextjs-logo.png" // public 디렉토리 내의 이미지 경로
alt="Next.js 로고"
width={500} // 이미지의 실제 너비 또는 원하는 표시 너비
height={300} // 이미지의 실제 높이 또는 원하는 표시 높이
placeholder="blur" // 로딩 중 blur 효과 (정적 이미지에만 적용 가능)
blurDataURL="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" // blurDataURL (작은 base64 이미지)
style={{ maxWidth: '100%', height: 'auto' }} // CSS 스타일 적용
/>
<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}
priority // 페이지 로딩 시 가장 먼저 로딩 (LCP 개선)
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' }} // CSS object-fit 속성
/>
</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
: 이미지의 고유한 너비와 높이(픽셀).layout="fill"
속성을 사용하지 않는 경우 필수적입니다. CLS를 방지하는 데 중요합니다.priority
: (불리언) 페이지 로딩 시 가장 먼저 로딩되어야 하는 이미지에 사용합니다 (예: Above the fold 콘텐츠).<Image priority />
로 사용하며, LCP(Largest Contentful Paint) 개선에 도움이 됩니다.fill
: (불리언) 부모 요소의 크기에 맞춰 이미지를 채웁니다. 이 경우width
와height
속성은 필요 없으며, 부모 요소에position: relative
등의 스타일이 적용되어야 합니다.sizes
:layout="fill"
또는layout="responsive"
(레거시)와 함께 사용하여 브라우저 너비에 따라 이미지 크기를 최적화합니다. (예:sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
)quality
: 이미지 압축 품질을 1에서 100 사이의 숫자로 지정합니다 (기본값: 75).placeholder
: 이미지가 로딩되는 동안 표시될 자리표시자.blur
: 작은blurDataURL
을 사용하여 흐릿한 이미지 효과를 보여줍니다. 로컬 정적 이미지에 자동으로 적용됩니다.empty
: 투명한 이미지 자리표시자를 보여줍니다.
blurDataURL
:placeholder="blur"
와 함께 사용할 작은 Base64 인코딩 이미지 데이터 URL. 로컬 정적 이미지의 경우 Next.js가 자동으로 생성하지만, 외부 이미지의 경우 직접 제공해야 합니다.loading
:lazy
(기본값, 지연 로딩) 또는eager
(즉시 로딩)를 지정합니다.priority
를 사용하면 자동으로eager
가 됩니다.unoptimized
: (불리언) Next.js의 이미지 최적화 기능을 사용하지 않고 순수<img>
태그처럼 동작하게 합니다. 특수한 경우에만 사용합니다.
외부 이미지 도메인 설정
외부 이미지 URL을 사용하는 경우, Next.js가 해당 이미지를 최적화할 수 있도록 next.config.js
파일에 이미지 도메인을 명시적으로 허용해야 합니다.
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
domains: [
'images.unsplash.com', // Unsplash 이미지 도메인
'www.example.com', // 허용할 다른 외부 이미지 도메인
'openweathermap.org', // 이전 절의 날씨 아이콘 도메인
],
},
};
module.exports = nextConfig;
next.config.js
파일을 수정한 후에는 개발 서버를 다시 시작해야 변경 사항이 적용됩니다.
이미지 최적화를 위한 추가 팁
- 적절한 이미지 크기: 이미지를 업로드하기 전에 사용될 최대 크기에 맞춰 적절히 리사이즈하는 것이 좋습니다.
next/image
가 반응형 이미지를 처리하지만, 너무 큰 원본 이미지를 업로드하는 것은 여전히 비효율적일 수 있습니다. - 파일 형식 선택
- JPEG: 사진과 같이 색상이 풍부한 이미지에 적합하며, 손실 압축을 통해 파일 크기를 줄일 수 있습니다.
- PNG: 투명도가 필요한 이미지나 로고, 아이콘 등 색상 수가 적고 선명도가 중요한 이미지에 적합합니다. (비손실 압축)
- WebP / AVIF: 최신 이미지 형식으로, JPEG나 PNG보다 훨씬 효율적인 압축률을 제공하면서도 높은 품질을 유지합니다.
next/image
는 브라우저가 지원하는 경우 자동으로 이 형식들로 변환하여 제공하려고 시도합니다. - SVG: 로고, 아이콘 등 벡터 기반의 그래픽에 적합합니다. 해상도에 구애받지 않고 확대/축소해도 깨지지 않으며, 파일 크기가 매우 작습니다.
<Image>
컴포넌트가 아닌 일반<img>
태그로 사용하거나 React 컴포넌트로 직접 임포트할 수 있습니다.
- Placeholder 사용:
placeholder="blur"
또는placeholder="empty"
를 사용하여 이미지가 로딩되는 동안 사용자 경험을 개선합니다. 특히 LCP 이미지의 경우priority
와 함께placeholder="blur"
를 사용하여 로딩 시간을 시각적으로 채우는 것이 좋습니다. - 캐싱 전략: CDN(Content Delivery Network)을 사용하여 사용자에게 물리적으로 가장 가까운 서버에서 이미지를 제공하여 로딩 속도를 단축합니다. Next.js는 기본적으로 Vercel에 배포할 때 내장된 이미지 최적화 서비스를 활용합니다.
- Lazy Loading 활용: 기본적으로
next/image
는 지연 로딩을 사용하므로, 뷰포트 바깥의 이미지는 스크롤될 때까지 로딩되지 않습니다. 이는 초기 페이지 로딩 속도를 크게 개선합니다.
이미지 최적화는 단순히 이미지를 웹 페이지에 넣는 것을 넘어, 사용자 경험과 웹 성능을 극대화하기 위한 필수적인 과정입니다. Next.js의 next/image
컴포넌트를 올바르게 활용하면 개발자가 복잡한 최적화 기법을 직접 구현할 필요 없이 뛰어난 이미지 성능을 얻을 수 있습니다.