웹 페이지에서 이미지는 전체 리소스 크기의 가장 큰 비중을 차지합니다. 이미지만 잘 최적화해도 웹사이트 속도가 획기적으로 빨라지고, UX와 SEO 점수가 크게 올라갑니다.
Next.js는 이를 자동으로 처리해 주는 강력한 next/image 컴포넌트를 제공합니다.
일반 <img> 태그의 문제점과 최적화의 필요성
전통적인 <img> 태그를 그대로 사용하면 다음과 같은 성능 저하가 발생합니다.
-
포맷 미최적화: PNG나 JPEG 같은 무거운 포맷을 그대로 서빙하여 대역폭을 낭비합니다. (WebP나 AVIF 같은 최신 포맷이 훨씬 가볍습니다.)
-
크기 미최적화: 모바일 화면(모니터 크기 400px)에서도 4K 해상도(3840px)의 원본 이미지를 그대로 다운로드합니다.
-
Layout Shift: 이미지가 로드되면서 주변 텍스트나 레이아웃이 덜컥거리며 밀리는 현상이 발생해 사용자 경험을 해칩니다. (CLS 점수 저하)
-
Lazy Loading 부재: 당장 화면에 보이지 않는 페이지 맨 아래의 이미지까지 첫 페이지 로드 시 모두 다운로드합니다.
Next.js next/image가 자동으로 해결해 주는 것들
Next.js의 <Image /> 컴포넌트를 사용하면 코드 몇 줄로 아래 최적화가 기본 적용됩니다.
-
크기 최적화: 접속한 디바이스 크기에 맞는 적절한 사이즈의 이미지를 자동으로 생성하고 서빙합니다.
-
포맷 최적화: 브라우저가 지원하는 경우, 자동으로 더 가볍고 압축률이 높은 WebP 또는 AVIF 포맷으로 변환합니다.
-
레이아웃 유지: 이미지가 로드되기 전에 자리를 미리 확보하여 레이아웃이 깨지는 현상을 방지합니다.
-
지연 로딩: 뷰포트(화면)에 가까워진 이미지 만 선택적으로 로드하여 초기 페이지 로딩 속도를 올립니다.
로컬 이미지 vs 원격 이미지
next/image를 사용할 때는 이미지를 가져오는 출처에 따라 작성 방식이 약간 다릅니다.
① 로컬 이미지
public 폴더 내에 있거나 프로젝트 내부 파일을 직접 import하는 방식입니다. 이 경우 Next.js가 빌드 시점에 이미지의 width와 height를 자동으로 파악하므로 크기를 명시하지 않아도 됩니다.
import Image from 'next/image';
import profilePic from '../public/images/profile.png'; // 직접 import
export default function Page() {
return (
<div>
<h1>내 프로필</h1>
<Image
src={profilePic}
alt="Kukjin Lee 프로필 사진"
// width, height를 적지 않아도 자동으로 계산됨
placeholder="blur" // 로드 전 흐릿한 이미지 효과 (로컬 이미지 전용 기본 기능)
/>
</div>
);
}
② 원격 이미지
외부 CDN이나 API를 통해 가져오는 이미지 주소(https://...)를 사용할 때입니다. Next.js는 빌드 타임에 원격 이미지의 크기를 알 수 없기 때문에, 반드시 width와 height를 명시하거나 fill 속성을 사용해야 합니다.
import Image from 'next/image';
export default function Page() {
return (
<Image
src="https://example.com/products/shoes-01.jpg"
alt="러닝화 이미지"
width={500}
height={300}
/>
);
}
⚠️ 중요 보안 설정 (next.config.js):
원격 이미지를 사용하려면 악의적인 외부 자원 요청을 방지하기 위해 반드시 허용할 도메인을 설정해야 합니다.
// next.config.js
const nextConfig = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'example.com',
port: '',
pathname: '/products/**',
},
],
},
};
module.exports = nextConfig;
실무에서 자주 쓰는 핵심 속성 (Props) 정리
| 속성 (Prop) | 타입 | 설명 |
src |
String / Object | 이미지 경로 (필수) |
alt |
String | 이미지를 설명하는 텍스트 (SEO 및 웹 접근성 필수) |
width / height |
Number | 이미지의 화면 표시 크기 (픽셀 단위, fill 없을 때 필수) |
fill |
Boolean | 부모 요소의 크기에 맞게 이미지를 꽉 채울 때 사용 (width/height 생략 가능) |
sizes |
String | 미디어 쿼리에 따라 다운로드할 이미지 크기 힌트 제공 (성능 최적화 핵심) |
priority |
Boolean | true로 설정 시 지연 로딩을 끄고 가장 먼저 로드함 (LCP 이미지에 필수) |
실무 팁: fill과 object-fit 활용하기
반응형 웹을 만들다 보면 정해진 픽셀 크기가 아니라 부모 박스 크기에 맞춰 이미지가 늘어나거나 줄어들어야 할 때가 있습니다. 이때 fill 속성을 씁니다.
// 부모 박스에 반드시 position: relative 나 absolute 등이 있어야 합니다.
<div style={{ position: 'relative', width: '100%', height: '400px' }}>
<Image
src="/hero.jpg"
alt="메인 배너"
fill
style={{ objectFit: 'cover' }} // CSS object-fit 속성과 함께 사용
/>
</div>
초보 개발자가 자주 하는 실수 & 체크리스트
첫 화면 배너 이미지(LCP)에 priority를 빼먹는 경우
-
증상: 페이지를 열었을 때 메인 배너 이미지가 한참 뒤에 껌벅이며 나타남.
-
해결책: 서비스의 첫인상을 좌우하는 가장 큰 이미지에는
priority속성을 부여해 가장 먼저 다운로드하도록 만드세요.<Image src="/main-banner.jpg" alt="메인 배너" fill priority />
원격 이미지 크기 왜곡 현상
-
증상: 원본 가로세로 비율과
next/image에 적어준width,height비율이 맞지 않아 이미지가 찌그러짐. -
해결책: CSS의
object-fit: cover또는contain을 적절히 조합하여 비율을 유지하게 제어합니다.
무조건 큰 sizes 설정 또는 생략 (fill 사용 시)
-
증상:
fill을 썼을 때sizes속성을 생략하면, Next.js는 뷰포트 100vw 전체 크기에 맞춘 초고해상도 이미지를 서빙하여 대역폭 낭비가 발생할 수 있습니다. -
해결책: 반응형 구간에 맞춰 다운로드할 크기 힌트를 명시하세요.
// 모바일에선 100vw, 태블릿 이상에선 50vw 크기의 이미지만 다운로드해라! sizes="(max-width: 768px) 100vw, 50vw"