Next.js Image hostname is not configured 에러 원인과 해결법
- 원인:
next/image로 외부 호스트의 이미지를 불러올 때 그 호스트를next.config.js에 등록해두지 않아서 발생 - 해결:
next.config.js의images.remotePatterns에 해당 호스트를 추가 - 주의: 이미지 호스팅 호스트가 새로 생길 때마다 설정 파일을 고치고 재배포해야 함 — 사용자 업로드처럼 호스트가 자주 바뀌면 와일드카드 패턴을 고려할 것
images: {
remotePatterns: [
{ protocol: 'https', hostname: 'cdn.example-cms.com' },
],
}
↓ 왜 이런 에러가 나는지 원리 보기
외부 CMS나 스토리지에서 받아온 이미지 URL을 next/image에 그대로 넣었더니 화면 대신 빨간 에러 화면이 뜬 적 있으실 겁니다. "Invalid src prop ... hostname ... is not configured under images in your next.config.js"라는 메시지인데, 이미지 URL 자체는 멀쩡한데도 나는 에러라 처음 보면 당황스럽습니다.
next/image가 외부 호스트를 화이트리스트로 관리하는 이유
일반 <img> 태그는 브라우저가 어떤 URL이든 그냥 요청해서 그리면 끝입니다. 반면 next/image는 요청이 들어올 때마다 Next.js 서버(또는 배포 플랫폼의 이미지 최적화 API)가 원본 이미지를 가져와 크기를 줄이고 포맷을 변환한 뒤 캐싱해서 내려주는 구조입니다. 이때 서버가 아무 URL이나 제한 없이 대신 가져오게 허용하면, 그 최적화 엔드포인트가 임의의 외부 서버에 요청을 대신 보내주는 통로로 악용될 수 있습니다. 그래서 Next.js는 next.config.js에 명시적으로 등록한 호스트의 이미지만 최적화 대상으로 받아들이고, 등록되지 않은 호스트는 아예 렌더링을 막아버립니다.
최근 외주로 참여한 프로젝트에서 헤드리스 CMS 이미지를 붙이는 작업을 했는데, 로컬에서 테스트할 때 쓰던 샘플 이미지는 문제없이 잘 나오길래 그대로 넘어갔습니다. 그런데 콘텐츠 담당자가 실제 운영 CMS에 이미지를 올리기 시작하자 그 페이지에서만 에러 화면이 떴습니다. 처음엔 이미지 URL 자체가 깨졌나 싶어서 그 주소를 브라우저 주소창에 직접 쳐서 열어봤는데 이미지는 멀쩡히 열렸습니다. 그제서야 콘솔에 찍힌 에러 메시지를 다시 천천히 읽어보고, 문제는 이미지가 아니라 next.config.js에 그 CMS 호스트가 등록돼 있지 않아서라는 걸 알았습니다.
헷갈리기 쉬운 지점: 일반 img 태그는 왜 이 제약이 없을까
이 제약은 next/image가 제공하는 자동 최적화 기능에만 걸리는 제약입니다. 최적화 과정 자체를 포기하고 브라우저가 원본 이미지를 그대로 가져오게 하고 싶다면 unoptimized prop을 쓰면 화이트리스트 없이도 동작합니다. 다만 이 경우 리사이징·포맷 변환 같은 next/image의 핵심 장점을 포기하는 셈입니다. (지연 로딩 자체는 unoptimized 여부와 무관하게 기본으로 유지됩니다.) 신뢰할 수 없는 출처의 이미지를 어쩔 수 없이 다뤄야 하는 특수한 상황이 아니라면 권장되지 않습니다.
실전 해결 코드: remotePatterns 등록하기
문제가 되는 코드
// next.config.js — images 설정이 비어있는 상태
/** @type {import('next').NextConfig} */
const nextConfig = {};
module.exports = nextConfig;
import Image from 'next/image';
export default function Avatar() {
return (
<Image
src="https://cdn.example-cms.com/uploads/avatar.jpg"
alt="프로필 이미지"
width={80}
height={80}
/>
);
}
// 콘솔: Invalid src prop (https://cdn.example-cms.com/uploads/avatar.jpg) on
// `next/image`, hostname "cdn.example-cms.com" is not configured under
// images in your `next.config.js`
해결: remotePatterns에 호스트 등록
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'cdn.example-cms.com',
pathname: '/uploads/**',
},
],
},
};
module.exports = nextConfig;
자주 놓치는 함정: 설정을 고쳤는데도 에러가 그대로일 때
next.config.js는 빌드·개발 서버가 시작될 때 한 번 읽히는 설정 파일이라, 파일을 고친 뒤 개발 서버를 재시작하지 않으면 브라우저를 아무리 새로고침해도 여전히 같은 에러가 뜹니다. 분명히 remotePatterns를 제대로 추가했는데도 에러가 안 없어진다면, 오타보다 먼저 개발 서버를 껐다 켜봤는지부터 확인하는 게 순서입니다.
참고: remotePatterns 필드 구성과 구버전 domains 차이
protocol·hostname·pathname을 조합해서 허용 범위를 필요한 만큼만 좁힐 수 있습니다. 이전 버전에서 쓰던 domains: ['cdn.example-cms.com'] 배열 방식도 여전히 동작은 하지만, 호스트 전체를 통째로 허용할 뿐 경로나 프로토콜을 제한할 수 없어 공식적으로 remotePatterns로 대체된 방식입니다. 새로 설정한다면 remotePatterns를 쓰는 게 맞습니다.
서브도메인이 계속 늘어나는 구조라면 hostname에 와일드카드를 써서 한 번에 처리할 수 있습니다.
{
protocol: 'https',
hostname: '**.example-cms.com', // 모든 서브도메인 허용
}
세 가지 대응 방식 비교
이미지 출처와 프로젝트 상황에 따라 골라 쓸 수 있습니다.
| 방법 | 동작 방식 | 적합한 상황 |
|---|---|---|
| remotePatterns | 프로토콜·호스트·경로 단위로 허용 범위를 좁혀서 등록 | 대부분의 경우에 권장되는 기본 방식 |
| domains (구버전) | 호스트 이름만으로 전체 경로·프로토콜을 통째로 허용 | 기존 프로젝트에 이미 설정돼 있어 당장 바꿀 이유가 없는 경우 |
| unoptimized | 최적화 자체를 건너뛰고 원본 URL을 그대로 사용 | 출처가 계속 바뀌어 화이트리스트 관리가 현실적으로 어려운 경우 |
images 설정 전체 옵션과 보안상의 이유는 공식 문서에서 확인할 수 있습니다.
Next.js 이미지 최적화와 설정 관련 내용을 좀 더 자세히 다룬 글은 [호마다의 IT 개발 입문 블로그] 에서 볼 수 있습니다.
댓글
댓글 쓰기