Image 컴포넌트 사용법
이번에는 Next.js에서 제공하는 Image
컴포넌트에 대해서 알아볼게요. 이미지 최적화에 신경 써야 할 때 직접 <img>
태그를 쓰기보다는 이 컴포넌트를 활용하면 자동으로 다양한 최적화가 진행되어 성능 향상에 도움이 된답니다.
아래는 Image
컴포넌트를 사용하는 기본적인 예시입니다.
import Image from 'next/image'
export default function Page() {
return (
<Image
src="/profile.png" // 이미지 경로
width={500} // 이미지 너비(px)
height={500} // 이미지 높이(px)
alt="Picture of the author" // 대체 텍스트 (접근성에 필수!)
/>
)
}
Image
컴포넌트의 기본적인 사용법은 이렇게 src
, width
, height
, alt
속성을 넘겨주는 거에요. 이 외에도 여러 유용한 props들이 있는데, 이어서 자세히 살펴볼게요.
Image 컴포넌트 주요 Props
Prop | 타입 | 설명 |
---|---|---|
src | string | 불러올 이미지 경로 또는 URL |
width | number/string | 이미지 너비 |
height | number/string | 이미지 높이 |
alt | string | 이미지 설명 텍스트 (웹 접근성 측면에서 매우 중요) |
layout | string | 이미지 레이아웃 (fixed , intrinsic , responsive , fill ) 설정 |
priority | boolean | true일 경우 우선 로딩, 주로 초기 페이지에 사용 |
placeholder | string | 이미지 로딩 전 보여줄 플레이스홀더 (blur 가능) |
quality | number | 이미지 품질 설정 (1~100) |
loader | function | 커스텀 이미지 로더 함수 설정 |
layout 옵션 간단 설명
fixed
: 지정한 크기에서 고정intrinsic
: 원본 비율 유지하며 최대 크기까지 늘어남responsive
: 부모 컨테이너의 크기에 맞게 반응형 조절fill
: 부모 컨테이너를 가득 채움 (position: absolute 필요)
Tip
priority
속성을 설정하면 페이지 로딩 시 해당 이미지가 우선적으로 로드되어 사용자 경험이 개선됩니다. 예를 들어, 프로필 사진이나 메인 배너 이미지에 사용하는 게 좋아요.
또한 placeholder="blur"
와 함께 blurDataURL
속성을 사용하면 이미지를 불러오는 동안 흐릿한 미리보기 이미지를 보여줄 수 있어 깔끔한 로딩 효과를 줄 수 있답니다.
추가로 궁금한 점이나 더 자세한 사용법이 필요하면 언제든지 알려주세요! Next.js는 이미지 핸들링을 꽤 잘 처리해주니까 꼭 활용해보시길 추천해요.
Image 컴포넌트에 사용할 수 있는 주요 props를 정리해봤어요. 이 내용을 참고하면 이미지 컴포넌트를 사용할 때 어떤 옵션들을 넣을 수 있는지 한눈에 확인할 수 있을 거예요.
Prop | Example | Type | Status |
---|---|---|---|
src | src="/profile.png" | String | Required |
width | width={500} | Integer (px) | Required |
height | height={500} | Integer (px) | Required |
alt | alt="Picture of the author" | String | Required |
loader | loader={imageLoader} | Function | - |
fill | fill={true} | Boolean | - |
sizes | sizes="(max-width: 768px) 100vw, 33vw" | String | - |
quality | quality={80} | Integer (1-100) | - |
priority | priority={true} | Boolean | - |
placeholder | placeholder="blur" | String | - |
style | style={{objectFit: "contain"}} | Object | - |
onLoadingComplete | onLoadingComplete={img => done()} | Function | Deprecated |
onLoad | onLoad={event => done()} | Function | - |
onError | onError={event => fail()} | Function | - |
loading | loading="lazy" | String | - |
blurDataURL | blurDataURL="data:image/jpeg..." | String | - |
overrideSrc | overrideSrc="/seo.png" | String | - |
필수로 꼭 들어가야 하는 props
Image 컴포넌트를 쓸 때는 최소한 다음 네 가지는 꼭 지정해줘야 해요.
src
: 이미지 경로 (예:/profile.png
)alt
: 이미지가 로드되지 않을 때 대체 텍스트 역할을 하는 설명width
: 이미지 너비 (픽셀 단위)height
: 이미지 높이 (픽셀 단위)
width
와 height
대신에 fill={true}
옵션을 사용하는 방법도 있는데, 이 경우 이미지가 부모 요소의 크기에 맞춰서 자동으로 채워지게 돼요.
그 외에 알아두면 좋은 점들
loader
: 커스텀 이미지 로더를 만들 때 유용해요. 기본 로더 대신 내가 원하는 로딩 방식을 정의할 수 있어요.priority
: 중요한 이미지는 이 옵션을 true로 설정해서 퍼포먼스를 개선할 수 있어요. React 내장 lazy loading과 다르게 우선 로드되게 할 수 있답니다.placeholder
와blurDataURL
: 이미지를 불러오기 전에 흐릿한 이미지(blur 효과)를 보여 주는 용도로 자주 쓰여요. 사용자 경험을 한층 부드럽게 만들어주죠.sizes
: 반응형 이미지에서 매우 유용해요. 화면 크기에 따라 적절한 이미지 크기를 제공할 수 있게 도와줍니다.onLoad
와onError
: 이미지가 로드되었을 때나 에러가 났을 때 적절한 콜백 처리를 하고 싶으면 이 props들을 활용하세요.
onLoadingComplete
는 요즘은 사용을 권장하지 않는 Deprecated 상태니까, 가능하면 onLoad
를 사용하시는 걸 추천해요.
실제 프로젝트에서 이미지를 최적화하고 싶을 때 이 컴포넌트의 다양한 옵션들을 잘 활용하면 훨씬 효율적이고 깔끔한 구현이 가능해집니다. 저는 개인적으로 priority
, placeholder
, 그리고 sizes
옵션을 자주 사용해서 사용자 경험이 좋아지는 걸 체감했어요.
필요한 부분만 잘 골라서 쓰면 되니 너무 걱정 말고 하나씩 시도해보면서 익혀보세요!
안녕하세요! 오늘은 Next.js에서 Image
컴포넌트를 사용할 때 src
속성에 대해 알아볼게요.
import Image from 'next/image'
export default function Page() {
return (
<div>
<Image
src="/profile.png"
width={500}
height={500}
alt="Picture of the author"
/>
</div>
)
}
위 예제에서 src
는 이미지 경로를 나타내는데요, Next.js에서는 src
에 사용할 수 있는 값이 제한되어 있습니다. 그 조건을 한번 살펴볼게요.
src 값 유형 | 설명 |
---|---|
정적으로 import된 이미지 파일 | 예를 들어, import profilePic from '../public/profile.png' 처럼 미리 불러온 이미지 파일을 사용할 수 있어요. |
경로 문자열 | URL 형태의 문자열로, 다음 두 경우로 나뉘어요: - 외부 URL: https://example.com/image.png - 내부 경로: /profile.png 等 |
여기서 중요한 점! 내부 경로를 쓸 때는 loader
속성에 따라서 다음과 같이 처리되는데요,
- 기본
loader
를 사용할 때는/public
폴더 내의 자원을 지정할 때는 경로가/
로 시작해야 해요.즉,/profile.png
는public/profile.png
에 위치한 파일을 가리킵니다. - 외부 이미지 URL을 쓸 땐 반드시 도메인을
next.config.js
의images.domains
배열에 추가해줘야 정상적으로 최적화가 이루어져요.
또한, Next.js의 Image
컴포넌트는 이미지 최적화나 lazy loading 등 다양한 이점을 주는데요, 그래서 width
와 height
를 꼭 명시해주는 게 좋아요. 그래야 이미지가 로드되기 전에도 레이아웃이 안정적으로 잡히고, 화면이 덜 흔들리게 되거든요.
추가 팁으로, 만약 이미지를 자바스크립트에서 직접 import 한다면 이렇게 쓸 수 있습니다:
import profilePic from '../public/profile.png'
<Image
src={profilePic}
alt="Profile"
width={500}
height={500}
/>
이렇게 하면 Next.js가 빌드 타임에 이미지 사이즈를 정확히 파악해서 최적화에 도움을 줄 거예요.
요약하면, src
에 들어가는 값은 크게 두 가지! 정적 import한 이미지 또는 경로 문자열이에요. 외부 이미지를 쓸 때는 꼭 next.config.js
에 도메인 등록 잊지 마시고, 내부 경로는 public
폴더를 기준으로 적절하게 작성하면 된답니다.
Next.js에서 이미지 다루기를 조금 더 편하고 효율적으로 하려면 이 부분을 잘 이해하시면 큰 도움이 될 거예요! 😊
기본 로더(default loader)를 사용할 때는 다음 사항도 꼭 참고하세요:
- src가 외부 URL일 경우에는
remotePatterns
설정도 함께 해줘야 합니다. - src가 애니메이션 이미지거나 JPEG, PNG, WebP, AVIF, GIF, TIFF 같은 알려진 포맷이 아닐 때는, 이미지는 있는 그대로(served as-is) 서빙됩니다.
- src가 SVG 형식이면 기본적으로 차단됩니다. SVG를 사용하려면
unoptimized
또는dangerouslyAllowSVG
옵션을 활성화해야 해요.
width 속성
width
속성은 이미지의 실제 픽셀 너비를 나타내는 값이에요. 이 값은 이미지가 로드될 때 레이아웃이 흔들리는 걸 막기 위해 적절한 종횡비(aspect ratio)를 계산하는 데 쓰입니다. 하지만 이게 곧 화면에 표시되는 크기를 결정하는 건 아니에요! 화면에 보이는 크기는 CSS로 조절하게 돼요. 쉽게 말해, HTML의 img
태그에서 width
속성과 비슷한 역할을 한다고 생각하시면 됩니다.
요즘 반응형 이미지 대응이나 레이아웃 안정성을 위해 width, height 같은 실제 이미지 크기 정보를 명확히 지정해주는 게 권장되고 있으니 꼭 기억해 주세요!
필수 항목입니다. 단, 정적으로 임포트된 이미지나 fill 속성이 적용된 이미지의 경우는 예외입니다.
height (높이)
height 속성은 이미지의 본래 높이를 픽셀 단위로 나타냅니다. 이 값은 이미지의 올바른 가로세로 비율을 추론하는 데 사용되며, 로딩 중에 레이아웃이 흔들리는 현상(layout shift)을 방지하는 효과가 있어요. 다만, 실제 화면에 렌더링되는 이미지 크기를 결정하는 속성은 아니에요. 렌더링 크기는 CSS에서 제어하며, HTML img
태그의 height 속성과 비슷한 역할을 한다고 생각하시면 됩니다.
이 부분에서 중요한 점은, height 속성을 명시해두면 브라우저가 이미지 로딩 시점에 공간을 미리 확보하기 때문에 “레이아웃 점프” 현상을 줄여 사용자 경험을 개선할 수 있다는 것입니다. 특히 페이지 내 이미지가 많거나 이미지가 느리게 로드되는 환경에서 이 점이 큰 도움이 됩니다.
추가로, 만약 정적으로 임포트된 이미지라면 빌드 시점에 이미지 크기 정보가 이미 포함되어 있어 직접 height를 명시하지 않아도 되고, fill 속성(부모 요소의 크기에 맞춰 이미지를 꽉 채우는 스타일)을 사용하는 경우에도 마찬가지로 height를 직접 지정할 필요가 없습니다.
알아두면 좋은 점: width와 height 속성은 이미지의 가로세로 비율(종횡비)을 결정하는 데 함께 사용돼요. 브라우저는 이 정보를 바탕으로 이미지가 로딩되기 전에 미리 공간을 확보하죠.
하지만 intrinsic size(내재적 크기)가 항상 브라우저에 렌더링되는 크기와 같지는 않아요. 부모 컨테이너 크기에 따라 이미지가 그 안에 맞게 조절되거든요. 예를 들어, 부모 컨테이너가 이미지 intrinsic size보다 작으면, 이미지는 자동으로 축소되어 컨테이너에 맞춰지는 거죠.
만약 width와 height가 애초에 알려지지 않았다면,fill
속성을 활용할 수도 있어요.
alt 속성
이미지의 alt 속성은 화면 읽기 기기(screen reader)와 검색 엔진을 위한 설명 역할을 해요. 이미지가 비활성화되어 있거나 로딩 오류가 발생했을 때는 대체 텍스트로 나타나죠.
alt 텍스트는 페이지의 의미를 바꾸지 않고 이미지를 대신할 수 있는 내용을 담아야 합니다.
즉, 이미지를 보완하는 용도가 아니라, 이미지 캡션이나 주변 텍스트에서 이미 설명한 내용을 반복하면 안 돼요.
요즘은 웹 접근성의 중요성이 부각되면서, 올바른 alt 속성 작성이 필수로 권장되고 있어요. 특히 시각장애인들이 사용하는 스크린 리더에서는 이미지가 제공하는 정보를 alt가 대신 전달하기 때문에, 간결하면서도 핵심을 담는 것이 좋답니다.
속성 | 설명 |
---|---|
width, height | 이미지의 가로세로 비율 결정, 브라우저가 공간 미리 확보 |
intrinsic size | 실제 이미지 파일의 원본 크기, 부모 컨테이너에 따라 조정됨 |
fill | width, height 미지정 시 공간에 꽉 채우기 위한 옵션 |
alt | 이미지 설명, 스크린 리더와 검색 엔진에 활용되며 대체 텍스트 역할 |
이미지 태그를 작성할 때는 위 내용을 참고해서, 디자인 뿐 아니라 접근성과 SEO까지 한 번에 챙겨보시면 좋아요!
이미지가 단순히 장식용이거나 사용자에게 중요한 정보 전달 목적이 아니라면, alt 속성은 빈 문자열로 설정하는 게 좋아요 (alt="").
더 알아보기
선택적 속성(Optional Props)
Image /
컴포넌트는 필수 속성 외에도 다양한 추가 속성을 지원해요.
이번 섹션에서는 가장 자주 쓰이는 속성들을 소개할게요.
좀 더 특별하거나 덜 자주 쓰이는 속성들은 '고급 속성(Advanced Props)' 섹션에서 확인할 수 있어요.
참고로, alt 속성은 이미지가 로드되지 않을 때 대체 텍스트로 보여주기도 하고, 시각장애인들이 스크린 리더를 사용할 때 이미지 내용을 설명해줘서 접근성 측면에서 매우 중요하답니다.
하지만 장식용인 경우에는 오히려 의미 없는 텍스트가 읽히면서 사용자에게 혼란을 줄 수 있으니, 빈 문자열로 두는 게 맞아요!
loader
이미지 URL을 처리할 때 사용하는 커스텀 함수예요.
loader 함수는 이미지에 필요한 URL을 직접 만들어내는데, 함수에 넘겨지는 주요 인자는 다음과 같아요:
src
: 이미지 경로 또는 파일명width
: 이미지 너비 (픽셀 단위)quality
: 이미지 품질 (보통 압축률이나 화질 조절에 사용)
이걸 기억하면, 원하는 대로 이미지 URL을 동적으로 생성할 수 있답니다! 예를 들어, CDN에서 이미지를 받아올 때 너비나 품질에 맞게 URL을 조합해서 요청할 수 있죠.
추가로, 로더를 직접 만들면 기본 이미지 최적화 방식을 바꾸거나, 외부 이미지 서비스(예: Cloudinary, Imgix 등)를 쓸 때 유용해요. 그런 경우 URL 생성 규칙이 다르니까 loader 함수에서 그에 맞게 처리하면 되죠.
만약 간단한 예시가 궁금하다면 이렇게 써볼 수 있어요:
function myLoader({ src, width, quality }) {
return `https://mycdn.com/${src}?w=${width}&q=${quality || 75}`;
}
위처럼 src
, width
, quality
를 받아서 URL을 조합하고, 이 URL을 이미지 컴포넌트에 넘겨주면 돼요.
그럼 필요한 크기와 품질에 맞춰서 이미지를 호출하니 네트워크 비용도 아끼고 페이지 속도도 좋아져요!
여기 커스텀 로더(custom loader)를 사용하는 예제가 있어요:
'use client'
import Image from 'next/image'
const imageLoader = ({ src, width, quality }) => {
return `https://example.com/${src}?w=${width}&q=${quality || 75}`
}
export default function Page() {
return (
<Image
loader={imageLoader}
src="me.png"
alt="Picture of the author"
width={500}
height={500}
/>
)
}
핵심 포인트
loader
라는 프로퍼티에 함수 형태로 로더를 넣을 때는 클라이언트 컴포넌트(Client Components) 안에서 사용해야 해요. 이유는 Next.js가 이 함수를 직렬화(serialization)해야 하기 때문입니다.- 즉,
'use client'
지시어를 꼭 컴포넌트 상단에 추가해줘야 오류가 안 납니다.
참고!
만약 앱 전역에 걸쳐서 모든 next/image
컴포넌트에 대해 같은 커스텀 로더를 쓰고 싶다면, next.config.js
의 loaderFile
옵션을 설정하는 방법도 있어요. 그러면 매번 개별 컴포넌트에서 loader
prop을 넘겨주지 않아도 되죠.
// next.config.js
module.exports = {
experimental: {
images: {
loaderFile: './my-custom-loader.js',
},
},
}
my-custom-loader.js
에 로더 함수를 작성해두면 Next.js가 자동으로 이걸 참조합니다.
커스텀 로더 쓸 때 알아두면 좋은 점
내용 | 설명 |
---|---|
로더에서 다루는 인자 | src , width , quality 등 이미지 최적화에 중요한 값들 |
기본 품질 값 | quality 가 없으면 75로 기본 세팅되는 경우가 많음 |
'use client' 필요 | 로더 함수를 컴포넌트 안에서 직접 넘길 땐 클라이언트 컴포넌트여야 함 |
전역 설정 가능 | loaderFile 옵션으로 앱 전체에 일괄 적용 가능 |
커스텀 로더는 외부 CDN이나 이미지 서버에서 동적으로 크기 조절, 품질 조절이 가능할 때 특히 유용해요. 이렇게 하면 필요한 사이즈로 딱 맞는 이미지를 받아와서 페이지 로딩 속도를 개선할 수 있답니다.
혹시 이미지 최적화 관련해서 더 궁금한 점 있으면 언제든 질문 주세요!
fill
fill={true} // {true} | {false}
fill
은 이미지가 부모 요소를 꽉 채우도록 하는 불리언 값이에요. 특히, 이미지의 너비와 높이를 미리 지정하기 어려울 때 유용하죠.
단, 이 기능을 제대로 사용하려면 부모 요소에 position: "relative"
, position: "fixed"
, 또는 position: "absolute"
스타일을 꼭 지정해줘야 해요. 그래야 이미지가 부모 요소 안에서 정확하게 위치하고 크기가 맞춰지니까요.
예를 들어 이렇게 쓸 수 있겠죠:
<div style={{ position: 'relative', width: '100%', height: '300px' }}>
<Image src="/example.jpg" alt="Example" fill={true} />
</div>
여기서 div
에 position: 'relative'
를 줌으로써 이미지가 그 영역을 꽉 채우게 만들 수 있어요.
이 점 꼭 기억하고 활용하면, 반응형 이미지 배치 때 꽤 편리하답니다!
기본적으로 img 태그에는 position: "absolute" 스타일이 자동으로 적용돼요.
만약 이미지에 별도의 스타일이 지정되지 않았다면, 이미지는 컨테이너 크기에 맞춰 늘어나게 돼요. 그런데 이럴 때 이미지가 찌그러지거나 비율이 이상해질 수 있죠. 그래서 보통은 object-fit: "contain" 스타일을 주는 게 좋아요. 이걸 주면 이미지가 컨테이너 안에 딱 맞게 맞춰지고, 빈 공간이 있을 때는 레터박스(검은 여백)가 생기면서 원본 비율을 유지해 줍니다.
반대로 object-fit: "cover"를 사용하면 이미지가 컨테이너를 꽉 채우도록 잘리면서 보여줘요. 원본 비율은 지키지만, 이미지 일부가 잘릴 수 있다는 점 명심하세요.
이렇게 object-fit을 적절히 활용하면 웹에서 이미지가 깨지거나 비율이 이상해지는 걸 방지할 수 있어요. 특히 반응형 웹이나 다양한 크기의 컨테이너에 이미지를 넣을 때 꼭 알아두면 좋은 CSS 속성이죠.
속성 | 설명 |
---|---|
position | 이미지 위치 지정, 기본값은 "absolute" |
object-fit | 이미지의 컨테이너 내 맞춤 방식 지정 |
contain | 이미지 비율 유지하며 컨테이너 안에 모두 보여줌 |
cover | 비율 유지하며 컨테이너를 완전히 덮되 일부 잘림 |
더 자세한 내용은 CSS의 object-fit 관련 문서를 참고하면 도움이 될 거예요.
오늘은 웹 개발할 때 이미지 다룰 때 꼭 알아두면 좋은 CSS 속성 몇 가지와 함께, 특히 sizes
속성에 대해 이야기해볼게요. 이미지 로딩 성능과 반응형 디자인에 큰 영향을 주기 때문에 이해하면 꽤 유용하답니다.
position, object-fit, object-position 간단 정리
-
position
이미지나 요소의 위치를 설정할 때 쓰이는 CSS 속성이죠. 예를 들어,relative
,absolute
같은 값을 줘요. 이미지가 컨테이너 내에서 어떻게 배치될지 결정할 때 많이 씁니다. -
object-fit
이미지를 컨테이너 크기에 맞게 어떻게 맞출지 정하는 속성이에요. 대표값으로는cover
(이미지 꽉 채우기, 잘릴 수 있음),contain
(이미지 전체 보이도록 축소/확대),fill
(비율 무시하고 꽉 채움),none
(원본 크기 유지) 등이 있어요. -
object-position
object-fit
이cover
나contain
일 때, 이미지가 컨테이너 안에서 어디에 위치할지 정하는 속성이에요. 기본값은center center
죠. 간단히top left
,bottom right
등으로 조절할 수 있어요.
이 세 가지 조합하면, 이미지가 반응형일 때 원하는 방식으로 나타나게 할 수 있어요!
sizes 속성 이해하기 (이미지 로딩 최적화에 핵심!)
<img>
태그에서 특히 <source>
와 함께 쓰면서 '지금 이 화면 사이즈에서 이미지가 실제로 얼마나 크게 보여질지' 브라우저에 알려주는 역할을 해요. 사실, 브라우저가 이미지 크기를 미리 알고 있으면, 가장 적합한 크기의 이미지를 요청할 수 있어 성능이 팍 좋아집니다.
sizes 정의 방식
- 문자열 형태로 쓰고, CSS 미디어 쿼리랑 비슷해요.
- 화면 너비 기준으로 '어떤 상황에 이미지가 몇 픽셀 너비만큼 보여질지'를 지정해줘요.
- 여러 조건을 콤마(,)로 구분해 순서대로 해석합니다.
예를 들어,
sizes="(max-width: 600px) 480px, 800px"
이렇게 하면, 화면 너비가 600px 이하일 땐 이미지가 480px 크기로 보여지고, 그 이상일 땐 800px 크기로 보여질 거란 걸 알려주는 거죠.
sizes가 중요한 이유
fill
또는 반응형 크기를 가진 이미지를 사용할 때 특히 효과적입니다.- 화면 크기에 맞춘 적절한 이미지 해상도를 요청해 불필요한 데이터 낭비를 줄여줍니다.
- 결국, 로딩 속도 개선과 데이터 절약으로 이어지니 모바일 환경에선 필수!
간단 요약 테이블
속성 | 설명 | 대표값 및 의미 |
---|---|---|
position | 요소의 위치 설정 | relative, absolute 등 |
object-fit | 이미지가 컨테이너 내에서 크기 조절 방법 | cover, contain, fill, none |
object-position | 이미지 내부에서 위치 지정 | center center (기본), top left 등 |
sizes | 여러 화면 크기에 맞춘 이미지 출력 크기 안내 | (media query) 크기, 최종 이미지 너비 |
마무리 팁
-
sizes
가 없으면?
브라우저는 디폴트로 전체 이미지 크기를 사용하거나 해상도에 맞춘 이미지를 선택 못해, 큰 이미지 불필요하게 다운받을 수 있어요. -
srcset
과 함께 쓰기!
sizes
는 주로srcset
과 같이 써서 여러 해상도 이미지를 상황에 맞게 불러오도록 해요. -
반응형 이미지 사용시 필수!
화면 크기별로 이미지를 다르게 하고 싶으면,sizes
에 익숙해지는 게 좋습니다.
이미지를 효율적으로 다루는 건 작은 것 같지만 웹 페이지 속도와 사용자 경험에 큰 차이를 만듭니다. position
, object-fit
, object-position
으로 디자인을 잡고, sizes
로 똑똑하게 이미지 로딩 관리해보세요! 앞으로도 계속 실용적인 웹 테크닉 알려드릴게요. 궁금한 점 있으면 댓글 남겨주세요~ 😊
next/image의 sizes 속성, 제대로 이해하고 있나요?
이미지를 좀 더 똑똑하게 불러오고 싶다면 sizes 속성만큼 중요한 것도 없어요. 이번 글에서는 sizes가 왜 필요한지, 그리고 어떻게 활용하면 좋은지 쉽게 풀어볼게요.
1. sizes는 "얼마나 크게 이미지를 보여줄 건지" 알려주는 역할이에요.
브라우저는 <Image>
컴포넌트에 자동으로 만들어지는 srcset(다양한 크기의 이미지 묶음)을 보고 어떤 이미지를 다운받을지 결정하는데요. 이때 sizes 속성 값이 중요한 역할을 해요.
-
sizes가 없다면?
기본값인100vw
(화면 가로 너비 100%)로 인식해서, 화면 크기만큼 큰 이미지를 다운받아요.
즉, 모바일 화면이라도 큰 데스크톱용 이미지를 받을 수도 있다는 뜻이죠. 낭비 그 자체...! -
sizes가 있으면?
화면 크기나 레이아웃에 맞는 이미지를 미리 알려서, 그에 적합한 크기의 이미지를 똑똑하게 골라 다운받을 수 있어요.
2. sizes는 srcset을 어떻게 만들어내느냐에도 영향을 줘요.
자동 생성되는 srcset이 크게 두 가지로 나뉘는데요.
- sizes가 없을 때는, 고정 크기 이미지용으로 작은 srcset이 만들어져요 (예: 1x, 2x...)
- sizes가 있으면, 반응형 이미지를 위한 큰 srcset이 생성돼요(예: 640w, 750w 같은 픽셀 너비 단위)
특히 sizes에 50vw
처럼 뷰포트의 % 단위가 들어가면, 불필요하게 작은 이미지 옵션들이 걸러져서 srcset이 간결해지고, 진짜 필요한 이미지들만 주로 다운받게 되죠.
실제 사례로 살펴볼게요!
import Image from 'next/image'
export default function Page() {
return (
<div className="grid-element">
<Image
fill
src="/example.png"
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
/>
</div>
)
}
- 화면이 최대 768px이면? 이미지가 화면 최대 너비(
100vw
)로 보여져요 - 주로 모바일용 - 768px 초과, 1200px 이하 화면이면? 이미지가 화면 너비의 절반(
50vw
) - 1200px 초과면? 이미지가 화면 너비의 33%(
33vw
)만 차지한다는 의미
sizes의 위력: 성능에 미치는 영향
만약 sizes
를 위 예시처럼 적절히 지정하지 않으면, 특히 데스크톱에서 실제로 1/3 크기만 필요한 이미지를 3배 크기(가로 기준) 이미지로 다운받게 되겠죠?
이미지 파일 크기는 가로 * 세로이기 때문에, 파일 크기는 대략 가로 크기의 제곱에 비례해요.
즉, 3배 큰 이미지를 받으면, 용량은 9배가 될 수 있다는 말입니다!
정리!
포인트 | 설명 |
---|---|
sizes의 역할 | 이미지가 실제로 렌더링될 크기를 브라우저에 알려서 적합한 이미지 선택 유도 |
sizes 없을 때 | 기본 100vw 로 간주, 무조건 화면 가로폭만큼 큰 이미지 다운 |
sizes가 있으면 | 반응형 srcset 선택 가능, 불필요한 큰 이미지 다운로드 억제 |
권장 활용 | 뷰포트 크기별 예상 이미지 크기를 sizes에 정의해두기 |
추가 팁!
-
fill
속성을 사용할 때는 특히 sizes 설정 꼭 해주세요.
그 이유는 fill일 때 이미지가 부모 요소에 맞춰 꽉 채워지는데, sizes가 없으면 무조건 화면 가로 크기로 판단하기 때문이에요. -
복잡한 레이아웃 환경이라면, media query 같은 복수 조건으로 sizes를 꼭 세분화해서 지정하는 습관을!
이처럼 sizes가 제한적으로 보이지만, 성능 최적화에 있어선 꽤나 강력한 역할을 해요.
앞으로 next/image 쓸 때는 sizes 설정 잊지 말고 성능도 챙기는 스마트한 개발자가 되어봅시다!
궁금한 점 있으면 댓글로 알려주세요~
srcset과 sizes 속성에 대해 조금 더 재미있게 파헤쳐보도록 할게요!
srcset과 sizes: 이미지 최적화를 위한 든든한 친구들
웹에서 이미지가 너무 크거나 작게 나오면 사용자 경험이 떨어지잖아요? 그래서 요즘은 반응형 이미지를 쉽게 구현할 수 있도록 srcset과 sizes라는 속성을 많이 사용해요.
srcset이란?
이미지를 다양한 크기로 준비해두고, 브라우저가 화면 크기나 해상도에 맞게 적절한 이미지를 골라서 보여주도록 해주는 역할이에요.
예를 들어:
<img
src="small.jpg"
srcset="small.jpg 500w, medium.jpg 1000w, large.jpg 1500w"
alt="예쁜 고양이"
/>
위에서 500w, 1000w, 1500w는 각각 이미지의 가로 너비(pixel) 크기를 의미해요. 브라우저가 화면에 맞게 적절한 이미지를 골라서 내려받겠죠.
sizes란?
sizes는 브라우저에게 "내 이미지는 이렇게 크기로 보여질 거야"라고 미리 알려주는 역할을 해요. 예를 들어, 내가 이미지가 보통 뷰포트의 50% 정도 차지한다면 그에 맞게 알려주면 크기를 더 정확하게 고를 수 있습니다.
<img
src="small.jpg"
srcset="small.jpg 500w, medium.jpg 1000w, large.jpg 1500w"
sizes="(max-width: 600px) 100vw, 50vw"
alt="예쁜 강아지"
/>
(max-width: 600px) 100vw
: 뷰포트가 600px 이하일 때는 이미지가 화면 너비 전체(100vw)를 차지한다는 뜻이에요.50vw
: 그 외에는 화면 너비의 50%만 쓰인다는 뜻이에요.
quality 속성도 알아두자!
위에서 언급된 quality는 이미지 처리 라이브러리(예: Next.js 이미지 컴포넌트)에서 이미지를 압축할 때 몇 퍼센트 품질로 줄일 건지를 나타내요. 1100까지 숫자를 받는데, 너무 낮으면 이미지가 뭉개지고, 너무 높으면 용량도 커져서 로딩이 느려져요. 보통 7080 수준에서 적절히 타협하는 편입니다.
quality={75} // 적당한 화질과 용량의 밸런스를 잡을 수 있어요
참고하면 좋은 사이트
이제부터 이미지 태그 예쁘게 짜서 웹사이트 속도도 챙기고 사용자 경험도 쏙쏙 높여보아요!
궁금한 점 있으면 언제든 댓글로 물어봐 주세요~
이미지 최적화에서 quality(품질) 값은 1부터 100 사이의 정수로 설정할 수 있는데요, 100이 가장 높은 품질이자 가장 큰 파일 크기를 의미합니다. 기본값은 75로 설정되어 있어요.
만약 next.config.js 파일에서 qualities 설정을 따로 정의해뒀다면, quality 속성 값은 그 중 하나와 꼭 일치해야 한다는 점 기억하세요.
참고할 내용: 원본 이미지가 이미 저품질이라면, quality 값을 너무 높게 설정할 경우 최적화된 이미지가 오히려 원본 파일보다 용량이 더 커질 수 있으니 주의가 필요합니다.
priority (우선순위)
여기서 priority는 Next.js에서 이미지 로딩 시, 어느 이미지를 더 빨리 불러올지 우선순위를 정하는 속성입니다. 예를 들어, 페이지 상단에 보이는 히어로 이미지 같은 경우 priority를 true로 설정하면, 브라우저가 페이지 렌더링 시 해당 이미지를 더 빨리 불러오게 됩니다.
속성을 간단히 정리하면:
속성명 | 설명 |
---|---|
priority | true로 설정하면 해당 이미지에 우선순위를 줘 빠르게 로드 |
적절하게 priority를 설정하면 사용자 경험이 한층 더 좋아질 수 있으니, 중요한 이미지는 꼭 체크해보세요!
priority={false} // {false} | {true}
이 옵션이 true
일 때, Next.js는 해당 이미지를 미리(preload) 불러옵니다. 즉, 이미지가 더 빨리 로드되도록 하는 기능이죠. 중요한 점은, priority가 true일 경우 lazy loading(지연 로딩)이 자동으로 비활성화된다는 것입니다. 만약 loading
속성을 사용하면서 lazy
로 설정하면 priority
와 함께 쓸 수 없어요. 그래서 priority가 필요한 이미지에서는 loading
속성을 제거하는 게 좋습니다.
이 속성은 Largest Contentful Paint(LCP) 요소로 감지되는 이미지에 주로 사용해야 합니다. LCP란 페이지 로드 시 사용자에게 가장 크게 보이는 콘텐츠를 의미하는데, 이미지가 이 역할을 한다면 빠르게 불러오는 것이 UX 개선에 도움이 되거든요.
뷰포트 크기에 따라 LCP 요소가 달라질 수 있어서, 여러 개의 이미지를 priority로 설정하는 것도 괜찮습니다. 다만, 이 속성은 화면 상단(above the fold)에 보이는 이미지에만 사용하는 게 효과적입니다. 기본값은 false
로 설정돼 있어요.
추가 팁!
-
priority 이미지가 너무 많으면 어떻게 될까요?
너무 많은 이미지를 미리 불러오면 네트워크에 부담이 갈 수 있으니, 정말 필요한 이미지만 우선순위를 주는 게 좋아요. -
Next.js의
Image
컴포넌트와 함께 사용
Next.js의<Image />
컴포넌트에서 이 옵션을 쓰면 자동으로 적절한 최적화도 같이 해줘서, 별도로 신경 쓰지 않아도 됩니다. -
LCP 개선은 SEO에도 좋아요!
페이지 로딩 속도와 사용자 경험 개선은 검색 엔진 최적화(SEO)에도 긍정적인 영향을 주니, 중요한 이미지에는 꼭 priority를 고려해보세요.
우리 웹 페이지가 빠르게 렌더링되고, 사용자들이 이미지 때문에 답답해하지 않게 하는 작은 팁 하나였어요! 다음 번에도 더 유용한 개발 꿀팁 가지고 올게요 :)
placeholder
placeholder = 'empty' // "empty" | "blur" | "data:image/..."
이미지가 로딩되는 동안 보여줄 자리 표시자(플레이스홀더)를 설정하는 속성입니다. 사용할 수 있는 값은 'empty'
, 'blur'
, 또는 'data:image/...'
형태입니다. 기본값은 'empty'
입니다.
'empty'
: 아무 것도 표시하지 않고 그냥 빈 공간으로 둡니다.'blur'
: 이미지가 로딩되는 동안 블러 처리된 저해상도 이미지를 보여줍니다. 이때blurDataURL
속성에 지정된 데이터 URL이 플레이스홀더로 사용돼요.'data:image/...'
: 직접 데이터 URL 형태의 이미지를 넣어서 커스텀 플레이스홀더를 설정할 수도 있습니다.
특히, blur
옵션은 정적 임포트(static import)로 불러온 .jpg
, .png
, .webp
, .avif
같은 이미지 포맷의 경우 자동으로 blurDataURL
이 채워지는데, 만약 애니메이션 이미지라면 이 기능이 적용되지 않습니다.
이 기능 덕분에 이미지가 화면에 깜빡이거나 완전히 로드될 때까지 기다리지 않고, 자연스러운 흐릿한 미리보기를 보여줘서 UX가 훨씬 부드러워진답니다. 요즘 웹사이트에서 흔히 볼 수 있는 '더 좋은 사용자 경험'을 위한 작은 팁이죠!
추가로, blurDataURL은 보통 아주 작은 크기의 저해상도 이미지 정보를 base64로 인코딩한 문자열을 의미해요. 직접 생성하기 번거롭다면 자동 생성 도구나 라이브러리를 활용하는 것도 방법입니다.
동적 이미지(dynamic images)를 사용할 때는 반드시 blurDataURL
속성을 제공해줘야 해요. 이 속성은 이미지가 로딩되는 동안 보여줄 흐릿한(blurred) 이미지 데이터를 뜻하는데요, 이걸 넣어주면 사용자가 이미지가 완전히 로딩될 때까지 깜빡임 없이 부드러운 경험을 할 수 있답니다.
그런데 이 blurDataURL
값을 직접 만들기 번거로울 수 있죠? 그래서 Plaiceholder 같은 솔루션이 인기를 끌고 있어요. Plaiceholder는 이미지를 자동으로 Base64 포맷으로 변환해서 흐릿한 이미지로 만들어주니까, 요긴하게 쓸 수 있어요.
그리고 만약 이미지 URL이 data:image/...
형식이라면, 이 데이터 URL이 그대로 로딩 중인 플레이스홀더(placeholder)로 사용돼요. 반면에 blurDataURL
을 비워두면 로딩 중에 아무 것도 보이지 않고 빈 공간만 남게 되니 주의해야 해요.
쉽게 말해 이런 느낌이에요:
blurDataURL 상태 | 결과 |
---|---|
값이 세팅된 경우 | 로딩 중 흐릿한 이미지 표시 |
data:image/... 포맷 | 해당 데이터가 로딩 플레이스홀더로 사용됨 |
비워둔 경우(빈값) | 로딩 중 빈 공간 |
직접 써보면서 확인해보세요. 이미지의 로딩 경험이 훨씬 부드러워질 거예요! 혹시 더 자세한 설정 방법이나 Plaiceholder 사용법이 궁금하면 알려주세요~
- 블러(blur) 플레이스홀더 데모
- 데이터 URL을 이용한 플레이스홀더 프로퍼티로 쉐이머(shimmer) 효과 데모
- blurDataURL 프로퍼티를 활용한 컬러 효과 데모
고급 속성 사용법
때로는 더 복잡한 사용이 필요할 때가 있죠. Image /
컴포넌트는 선택적으로 아래와 같은 고급 속성들을 받을 수 있습니다.
style (스타일)
여기서 스타일을 지정해서 이미지에 다양한 시각적 효과를 줄 수 있어요. 예를 들어, 이미지 모서리를 둥글게 하거나, 그림자를 넣는 등 CSS를 직접 적용할 수 있답니다.
이런 고급 props들을 잘 활용하면 더 세련되고 사용자 친화적인 이미지 로딩 경험을 만들 수 있어요!
필요하면 다음에 blurDataURL
이나 쉐이머 효과 만들 때 사용하는 데이터 URL 생성 방법도 같이 소개할게요.
편하게 알려드릴 테니 기대해 주세요!
이미지에 CSS 스타일을 직접 전달할 수 있어요.
const imageStyle = {
borderRadius: '50%',
border: '1px solid #fff',
}
export default function ProfileImage() {
return <Image src="..." style={imageStyle} />
}
여기서 중요한 점! 이미지의 width
와 height
속성은 스타일링에 영향을 줄 수 있어요. 만약 CSS 스타일로 이미지의 너비를 조절한다면, 높이는 auto
로 설정해줘야 원래 비율을 유지할 수 있습니다. 안 그러면 이미지가 찌그러져 보일 수 있거든요.
예를 들면 이렇게 하면 좋아요:
const imageStyle = {
width: '100px',
height: 'auto',
borderRadius: '50%',
border: '1px solid #fff',
}
이렇게 하면 너비는 100px로 고정하고, 높이는 자동으로 조절되어 이미지 비율이 깨지지 않죠.
onLoadingComplete
onLoadingComplete
는 이미지가 모두 로드된 후에 실행되는 콜백 함수입니다. 이미지 처리나 로딩 상태 관리를 할 때 유용해요.
예를 들어, 이미지가 로드 완료되면 어떤 작업을 하고 싶을 때 이렇게 쓸 수 있어요:
export default function ProfileImage() {
const handleLoadingComplete = () => {
console.log('이미지가 성공적으로 로드되었습니다!');
// 여기서 로딩 스피너 숨기기 등 다른 작업을 할 수 있겠죠
}
return <Image src="..." onLoadingComplete={handleLoadingComplete} />
}
이 콜백 함수를 활용해서 사용자 경험을 좀 더 부드럽게 만들 수 있어요. 예를 들어 이미지가 로드 중일 때 스피너를 표시하고, 완료되면 스피너를 숨기는 식으로요.
이 부분도 꼭 기억해두세요!
안녕하세요, 여러분! 오늘은 Next.js 14에서 Image 컴포넌트의 새로운 변화에 대해 짧게 이야기해보려고 해요.
기존에 아래처럼 onLoadingComplete
를 썼던 분들 많으시죠?
'use client'
<Image onLoadingComplete={(img) => console.log(img.naturalWidth)} />
그런데 Next.js 14부터는 이 onLoadingComplete
가 deprecated(더 이상 권장되지 않음) 되었어요. 대신 onLoad
라는 새로운 콜백 함수가 도입됐답니다.
그럼 뭐가 달라졌을까요?
onLoad
콜백은 이미지가 완전히 로드되고, 플레이스홀더(placeholder)가 제거된 후에 호출돼요.- 이 함수는 실제
<img>
요소를 인자로 받아요. 그래서 이미지의 크기 정보나 기타 속성을 쉽게 가져올 수 있죠.
바뀐 사용법은 이렇게!
'use client'
import Image from 'next/image';
<Image
src="/example.jpg"
width={500}
height={300}
onLoad={(img) => console.log('Image width:', img.naturalWidth)}
/>
참고!
onLoad
안에서 받는 인자는 실제 HTML의<img>
엘리먼트이므로,naturalWidth
,naturalHeight
,currentSrc
같은 DOM 속성에 접근 가능해요.- 이전과 달리
onLoadingComplete
는 더 이상 공식 문서에서 지원하지 않으니, 꼭onLoad
로 바꾸시는 걸 추천드려요.
사실 이런 변화는 Next.js가 내부에서 이미지 로딩 과정을 더 효율적이고 통제하기 쉽게 만들기 위한 거라 생각하면 좋아요. 새로운 버전 나오면 공식 문서 체크와 함께 이렇게 바뀐 부분도 챙겨주면 프로젝트 유지보수가 훨씬 편하답니다.
혹시 이미지 로딩과 관련해서 더 궁금한 점 있으면 댓글로 알려주세요! 그럼 즐거운 개발 되세요~ 😊
참고할 점: onLoadingComplete 같은 함수 형태의 props를 사용할 때는, 전달한 함수를 직렬화하기 위해 클라이언트 컴포넌트를 사용해야 한다는 점을 기억하세요.
onLoad
<Image onLoad={(e) => console.log(e.target.naturalWidth)} />
이미지가 완전히 로드되고 플레이스홀더가 사라진 후에 실행되는 콜백 함수입니다.
여기에 덧붙여서, onLoad 이벤트는 이미지가 화면에 렌더링 되는 순간을 알 수 있어서, 예를 들어 이미지 크기에 따라 레이아웃을 조절하거나 로딩 상태를 업데이트 하는 데 쓸 수 있어요. 또한, 네트워크 상황이 느릴 때 사용자에게 로딩 완료 시점을 알려주는 용도로도 활용할 수 있답니다.
하지만 Next.js의 Image 컴포넌트를 사용할 때는 내장된 최적화 특징 때문에, "onLoadingComplete"라는 비슷한 이벤트도 함께 제공되니 필요에 따라 적절히 선택해서 사용하세요.
콜백 함수는 이벤트 객체 하나를 인자로 받게 되는데, 여기서 이벤트의 target
은 실제로 렌더된 img
요소를 가리키게 돼요.
참고할 점: onLoad처럼 함수 형태의 prop을 사용할 때는, 이 함수를 직렬화하기 위해 클라이언트 컴포넌트(Client Components)를 사용해야 해요.
onError
<Image onError={(e) => console.error(e.target.id)} />
onError
는 이미지 로드에 문제가 생겼을 때 호출되는 콜백 함수예요. 예를 들면, 이미지 URL이 잘못됐거나 서버에서 이미지를 불러올 수 없을 때 이 함수가 실행되죠. 위 코드에서는 에러가 발생한 이미지 엘리먼트의 id 값을 콘솔에 출력하고 있네요.
참고로, 이런 이벤트 핸들러를 사용할 땐 해당 컴포넌트가 클라이언트에서 동작하도록 설정돼 있어야 한다는 점 기억하세요. 특히 Next.js 같은 프레임워크에서는 서버 컴포넌트와 클라이언트 컴포넌트가 구분되어 있어서, 함수가 전달되는 이벤트 핸들러는 클라이언트 컴포넌트에서만 제대로 동작합니다.
혹시 이미지 로드 실패 시 사용자에게 다른 이미지를 보여주거나, 오류 메시지를 띄우고 싶다면 onError
안에서 상태를 변경하거나 별도의 로직을 구현하면 돼요!
이미지 로딩에 실패했을 때 호출되는 콜백 함수에 대해 이야기해볼게요.
onError
이미지 로딩 중 문제가 발생하면 실행되는 함수예요. 예를 들어, 이미지 URL이 잘못되었거나, 서버에 이미지가 없을 때 호출할 수 있죠.
참고로 알아두면 좋은 점!
onError
같은 콜백 함수를 프로퍼티로 넘기려면, 이 함수를 직렬화(serialization)할 수 있는 클라이언트 컴포넌트(Client Components) 안에서 사용해야 해요. 즉, 서버 컴포넌트에서 바로 함수 형태로 넘기면 동작하지 않을 수 있다는 뜻입니다.
loading 속성
loading = 'lazy' // 'lazy' 또는 'eager' 중 선택 가능
lazy
(지연 로딩): 이미지를 사용자가 실제로 볼 때까지 미뤄서 로딩해요. 페이지 초기 로딩 속도 향상에 도움을 줍니다.eager
(즉시 로딩): 페이지가 로드될 때 이미지를 바로 불러와요. 빠른 렌더링이 필요할 때 쓰면 좋아요.
요즘 웹에서는 지연 로딩이 기본 트렌드인데요, 특히 이미지를 많이 쓰는 페이지는 loading='lazy'
를 적용하는 게 퍼포먼스 향상에 크게 이바지한답니다.
간단한 예제도 같이 볼까요?
import { useState } from 'react';
function ImageWithFallback({ src, alt }) {
const [error, setError] = useState(false);
return (
<>
{!error ? (
<img
src={src}
alt={alt}
loading="lazy"
onError={() => setError(true)}
/>
) : (
<div>이미지를 불러올 수 없습니다.</div>
)}
</>
);
}
이렇게 하면 이미지가 실패할 때 텍스트로 대체 표시도 할 수 있죠.
필요에 따라서 onError
를 활용해 이미지 교체, 오류 기록, 사용자 알림 등 다양한 작업을 할 수 있으니 기억해두세요!
이미지 로딩 동작에 대해 이야기해볼게요. 기본값은 lazy
인데요, 쉽게 말해 이미지는 화면에 보이기 직전에야 로드되도록 지연시켜서 페이지 로딩 속도를 높여주는 거예요.
lazy
: 이미지가 화면에 가까워질 때까지 로딩을 미뤄요. 예를 들어, 사용자가 스크롤을 내려서 이미지가 보일 타이밍에 이미지를 불러오는 방식이죠. 데이터 소모도 줄이고 페이지가 훨씬 빨리 뜹니다.eager
: 이미지가 페이지가 로드될 때 바로 불러와요. 중요한 이미지나 빠르게 보여줘야 할 때 유용하답니다.
이 loading
속성 덕분에 개발자들이 웹사이트 성능을 최적화하기가 한결 쉬워졌어요. 자세한 내용을 더 보고 싶다면 loading attribute에 대해 알아보기에서 확인할 수 있어요.
참고로, lazy
로딩은 모든 브라우저가 지원하는 건 아니라서 필요에 따라 자바스크립트로 폴리필을 적용하는 경우도 있어요. 또한, 이미지가 중요한 정보라면 eager
를 선택하는 게 사용자 경험에 더 좋을 수 있으니 상황에 맞게 잘 활용해보세요!
blurDataURL
blurDataURL은 이미지가 로드되기 전에 임시로 보여줄 작은 이미지를 Base64 형태로 지정할 때 사용해요. 쉽게 말해서, 실제 이미지가 로딩되기 전 잠깐 나타나는 ‘흐릿한 미리보기’ 같은 역할이죠.
주의할 점은 이 값이 반영되려면 꼭 placeholder="blur"와 함께 써야 한다는 거예요!
또, blurDataURL에 넣는 이미지는 아주 작고 간단한 게 좋아요. 보통 10px 이하 크기의 이미지가 적당한데요, 그 이유는 이 이미지가 화면에 확대되고 흐려지면서 보여지기 때문이에요. 너무 큰 이미지를 넣으면 앱 성능에 안 좋을 수 있거든요.
예를 들어 마크다운으로 표현하면 이렇게 써볼 수 있어요:
속성명 | 설명 |
---|---|
blurDataURL | 로드 전에 쓰이는 미리보기 이미지의 Base64 인코딩 데이터 |
placeholder | 'blur' 으로 설정해서 blurDataURL과 함께 사용해야 효과를 볼 수 있음 |
이걸 활용하면 사용자 경험이 조금 더 매끄러워져서, 이미지가 늦게 뜰 때도 갑자기 빈 화면이 나오는 걸 방지할 수 있어요. 웹이나 앱에서 이미지가 무거워 딜레이가 있을 때 정말 유용하답니다.
직접 써보는 것도 추천해요! 예를 들어, Next.js의 Image 컴포넌트에서 자주 쓰이는 기능이라, 관심 있으면 찾아보시면 도움이 많이 될 겁니다.
- 기본 blurDataURL prop 사용법 데모
- blurDataURL prop를 이용한 색상 효과 데모
이미지와 어울리는 단색 색상 Data URL도 생성할 수 있어요.
unoptimized 옵션
unoptimized = {false} // false 또는 true 값 사용 가능
조금 더 설명을 덧붙이자면, blurDataURL
은 이미지가 로딩되기 전까지 잠깐 보여줄 작은 저해상도 이미지의 URL이에요. 흔히 ‘로딩 플레이스홀더’로 활용하죠. 특히 네트워크 속도가 느릴 때 사용자에게 부드러운 경험을 선사할 수 있어요.
unoptimized
옵션은 Next.js에서 이미지 최적화를 끌지 말지를 결정하는 건데요. 기본값은 false
로 최적화 기능을 켜둔 상태고, true
로 설정하면 최적화를 끄고 원본 이미지를 그대로 사용하게 돼요. 개발 과정에서 최적화 문제를 피하거나 특정 상황에서 이미지를 그대로 사용하고 싶을 때 유용합니다.
그리고 색상 효과를 내는 blurDataURL도 많이 활용되는데요, 실제 이미지와 비슷한 톤의 단색을 blurDataURL로 만들어 놓으면 로딩 시 배경색처럼 자연스럽게 보일 수 있어요. 이 방법은 이미지가 로딩되는 동안 사용자 시각 경험을 더 좋게 만들어주니까 개발할 때 한 번쯤 적용해 보는 걸 추천드려요!
원본 이미지를 src
에서 그대로 내보내고 싶을 때, 즉 퀄리티나 크기, 포맷을 변경하지 않을 때는 unoptimized
속성을 true
로 설정하면 됩니다. 기본값은 false
입니다.
이렇게 하는 게 특히 좋은 경우는 최적화를 해도 큰 이점이 없는 이미지들이에요. 예를 들어, 아주 작은 이미지(1KB 이하), 벡터 이미지(SVG), 또는 움직이는 이미지(GIF) 등이 그 예입니다.
아래는 간단한 사용 예시입니다:
import Image from 'next/image'
const UnoptimizedImage = (props) => {
return <Image {...props} unoptimized />
}
그리고 Next.js 12.3.0부터는 모든 이미지에 대해 unoptimized
옵션을 기본으로 적용하려면 next.config.js
파일에 다음과 같은 설정을 추가하면 됩니다:
// next.config.js
module.exports = {
images: {
unoptimized: true,
},
}
이렇게 설정하면 개별 이미지마다 unoptimized
속성을 일일이 지정하지 않아도 되니 관리가 훨씬 편해져요.
추가로, 최적화를 비활성화하면 이미지가 CDN이나 외부 최적화 서비스 없이 그대로 제공되므로, 속도나 용량 활용 측면에서 체크가 필요합니다. 그래서 보통은 작은 이미지나 이미 최적화된 SVG, GIF에만 적용하는 것을 추천드려요!
여러분, Next.js에서 이미지 최적화 관련 설정해보셨나요? 오늘은 간단하게 next.config.js
에서 이미지 최적화를 비활성화하는 법과, Image
컴포넌트에 src
를 넘겨줄 때 어떤 일이 일어나는지 살펴보겠습니다.
이미지 최적화 끄기
Next.js는 기본적으로 이미지 최적화를 자동으로 해주는데요, 가끔은 직접 최적화하지 않고 원본 이미지를 그대로 쓰고 싶을 때가 있죠. 그럴 때 next.config.js
파일에 이렇게 설정하면 됩니다:
module.exports = {
images: {
unoptimized: true,
},
}
이렇게 하면 Next.js가 이미지 최적화를 건너뛰고, 여러분이 지정한 이미지가 그대로 사용돼요.
Image
컴포넌트의 src
프로퍼티
그리고 Image
컴포넌트에 src
값을 넘기면, 내부적으로 srcset
과 src
속성이 자동으로 생성돼서 결국 HTML의 <img>
태그로 변환됩니다.
예를 들어:
<Image src="/me.jpg" />
이렇게 쓰면 Next.js가 자동으로 화면 크기에 맞는 여러 해상도의 이미지 URL을 만들어서 srcset
에 넣어주고, 동시에 기본 src
도 넣어줘서 브라우저가 가장 적합한 이미지를 골라서 보여주게 하는 거죠.
이 부분이 왜 좋냐면, 반응형 이미지 처리가 훨씬 편하고, 성능도 좋아진다는 점입니다!
팁! 이미지 최적화 관련 기억할 점
unoptimized: true
를 쓰면 캐싱이나 최적화가 없기 때문에, 프로덕션에서 할 때는 성능 저하가 있을 수 있으니 주의하세요.next/image
컴포넌트를 쓸 때는 반드시width
와height
를 지정하는 게 좋습니다. 그래야 레이아웃이 안정적으로 잡히고 CLS(Cumulative Layout Shift)를 줄일 수 있어요.- 외부 도메인의 이미지를 쓸 때는
next.config.js
에 도메인 허용 설정을 해줘야 합니다.
그리고 혹시 더 깊게 이미지 최적화를 알아보고 싶으면,
next/image
공식 문서도 한번 들여다보시면 정말 많은 팁들이 숨어있답니다!
오늘은 여기까지! 필요할 때마다 참고하시고, 이미지 최적화도 똑똑하게 활용해 보세요!
안녕하세요! 오늘은 Next.js 이미지 컴포넌트(Image) 사용 시, src
속성 자동 생성 문제와 이를 우회하는 방법에 대해 알아볼게요.
보통 <img>
태그를 사용하면 이렇게 srcset과 src를 직접 지정하잖아요?
<img
srcset="
/_next/image?url=%2Fme.jpg&w=640&q=75 1x,
/_next/image?url=%2Fme.jpg&w=828&q=75 2x
"
src="/_next/image?url=%2Fme.jpg&w=828&q=75"
/>
근데 Next.js의 <Image>
컴포넌트를 쓰면 내부적으로 src
와 srcset
을 자동으로 생성해줘요. 다 좋은데, 가끔은 이 자동 생성된 src
를 우리가 직접 지정하고 싶을 때가 있죠. 예를 들어, 기존 웹사이트를 img
에서 Image
컴포넌트로 바꿀 때, SEO 최적화를 위해 이미지 URL을 그대로 유지하고 싶을 수도 있어요. (검색엔진 최적화, 이미지 랭킹 유지 및 재크롤 방지 목적 등)
그럴 때 사용하는 게 바로 overrideSrc
prop 입니다.
<Image src="/me.jpg" overrideSrc="/override.jpg" />
이렇게 하면 내부적으로는 /me.jpg
로 이미지 최적화 및 로딩 처리를 하면서도, 실제 src
속성은 /override.jpg
로 지정할 수 있어요.
🎯 핵심 정리!
상황 | 해결법 |
---|---|
기본적으로 자동 생성된 src 쓰기 원함 | 그냥 <Image src="..." /> 사용 |
SEO 등 이미지 URL을 유지해야 할 때 | <Image src="..." overrideSrc="..." /> 사용 |
추가 팁!
- 이미지 최적화는 Next.js가 자동으로 해주지만, SEO를 위해 실제 보여지는 HTML 속성 값을 직접 관리해야 할 때가 있어서 이렇게
overrideSrc
가 유용해요. - 오버라이드 한 URL이 실제 이미지가 맞는지, 접근 가능 여부는 미리 확인하는 게 좋습니다.
overrideSrc
는 Next.js 공식 문서에 나오는 기능은 아닌 경우가 많아서, 직접 구현하거나 커스텀 이미지 컴포넌트를 만들어 사용하는 방법도 있어요!
Next.js 이미지 컴포넌트를 다룰 때 이런 작은 팁들이 개발 생산성도 올려주고, SEO 챙기는데도 크게 도움 된답니다. 다음에도 유용한 정보로 찾아올게요! 😄
아래 코드는 <img>
태그에서 srcset
과 src
속성을 활용하는 예시입니다.
<img
srcset="
/_next/image?url=%2Fme.jpg&w=640&q=75 1x,
/_next/image?url=%2Fme.jpg&w=828&q=75 2x
"
src="/override.jpg"
/>
간단 정리
srcset
은 브라우저가 디바이스 해상도에 맞는 이미지를 선택해서 로드할 수 있도록 도와줘요.- 위 예시에서는 1배(640px)와 2배(828px) 해상도의 이미지를 준비해뒀죠.
src
는 기본 이미지 URL로,srcset
을 지원하지 않는 구형 브라우저에서 사용돼요.
decoding 속성에 대해서
브라우저에 이미지의 디코딩(화면에 보여주기 위해 이미지 데이터를 해석하는 과정)을 어떻게 처리할지 힌트를 주는 속성이에요. 기본값은 async
로, 비동기적으로 디코딩해서 페이지 렌더링 속도에 도움을 줘요.
속성값 | 설명 |
---|---|
sync | 이미지 디코딩이 끝날 때까지 렌더링을 중단해요. 주요 이미지에 적합. |
async | 디코딩을 비동기 처리해요. 기본값이며, 렌더링에 지장 없어요. |
auto | 브라우저가 최적의 전략으로 디코딩 방식을 결정해요. |
왜 decoding
을 신경 써야 할까?
이미지가 많은 페이지에서 디코딩 방식을 잘 설정하면, 페이지 렌더링이 훨씬 부드러워지고 사용자 경험이 좋아집니다. 특히 중요한 이미지나 첫 화면에 노출되는 이미지가 있다면 sync
로 설정해서 바로 보이게 하는 것도 방법이에요.
한 가지 팁!
loading="lazy"
속성과 함께 쓰면, 이미지가 화면 근처에 올 때만 로드하고, 디코딩도 적절히 조절할 수 있어서 성능 최적화에 더 도움이 됩니다.
<img src="image.jpg" decoding="async" loading="lazy" alt="example" />
이렇게 이미지 관련 속성들을 적절히 이해하고 사용하면, 웹 페이지의 성능과 사용자 경험을 크게 개선할 수 있어요. 다음에도 좋은 팁으로 또 만나요! 😊
- async: 이미지를 비동기적으로 디코딩해서, 이미지 로딩이 완료되기 전에도 다른 콘텐츠가 먼저 렌더링될 수 있게 해줘요.
- sync: 이미지를 동기적으로 디코딩해서, 다른 콘텐츠와 함께 완전하게 한 번에 보여주고 싶을 때 사용해요.
- auto: 디코딩 모드에 대한 특별한 선호 없이 브라우저가 가장 적절하다고 판단하는 방식을 선택하게 합니다.
디코딩 속성(decoding attribute)에 대해 더 궁금하다면, 자세한 정보를 찾아보는 것도 좋아요. 이미지 로딩 방식에 따라 페이지 렌더링 속도나 사용자 경험이 달라질 수 있으니까요.
다른 속성들 (Other Props)
Image
컴포넌트에 넣는 다른 모든 속성들은, 특별히 제한된 몇 가지를 제외한 채로 실제 <img>
태그에 그대로 전달됩니다.
즉, 일반적인 alt
, width
, height
같은 속성들은 걱정 없이 사용할 수 있죠.
추가로 알려드리자면, 이미지 디코딩 방식은 특히 페이지가 많이 복잡하거나 이미지가 많은 경우 성능에 꽤 큰 영향을 줄 수 있어요. 예를 들어, async
로 둬서 텍스트 같은 중요한 콘텐츠가 먼저 보이게 하면 사용자 경험이 더 좋아질 수 있답니다. 반면, UI가 깔끔하게 한꺼번에 보여지는 게 중요한 경우엔 sync
를 선택하는 게 좋고요.
꼭 상황에 맞게 선택해 주세요!
- srcSet 대신 Device Sizes를 사용하세요.
설정 옵션
다음은 props 외에 next.config.js 파일에서 Image 컴포넌트를 설정할 수 있는 옵션들입니다. 이 옵션들을 통해 이미지 최적화 동작을 세밀하게 조절할 수 있어요.
localPatterns
(localPatterns 옵션에 대한 설명이 더 있으면 여기에 추가하면 좋겠지만, 지금은 제목만 주셨네요. 만약 이미지 경로나 패턴을 로컬 이미지 관리에 사용한다면, 이 옵션이 어떤 역할을 하는지 간단히 정리해볼게요.)
localPatterns는 로컬 이미지 파일 경로나 패턴들을 지정하는 옵션입니다. 예를 들어, 특정 폴더 내 이미지 파일만 이미지 최적화 대상에 포함하거나 특정 형식의 파일만 처리하도록 할 때 유용하답니다. 이렇게 하면 불필요한 파일까지 빌드 과정에 포함되는 걸 막아 최적화를 더욱 깔끔하게 할 수 있어요.
추가로 srcSet과 Device Sizes에 대해 조금 더 알려드릴게요.
srcSet 대신 Device Sizes 사용하기
예전에는 이미지 태그에 srcSet 속성을 직접 지정해서 여러 해상도 대응 이미지를 넘겨주곤 했는데요, Next.js Image 컴포넌트에서는 deviceSizes 배열을 통해 대응할 화면 너비별 이미지 사이즈를 미리 지정합니다. 그러면 Next.js가 자동으로 가장 적합한 이미지 크기를 골라 srcSet을 만들어서 넣어줘 개발자가 직접 일일이 srcSet을 관리할 필요가 사라지죠.
// next.config.js 예시
module.exports = {
images: {
deviceSizes: [320, 420, 768, 1024, 1200],
},
};
이런 식으로 설정하면 각 기기별 스크린 크기에 맞는 이미지가 제공되어 더욱 빠르고 효율적인 이미지 로딩이 가능해져요.
어려운 설정 같아 보여도, Next.js가 알아서 다 해주니까 걱정하지 마세요. 대신 나만의 이미지 정책에 맞게 deviceSizes 같은 옵션을 적절히 조정해주면 됩니다!
Next.js에서 이미지 최적화를 할 때, 특정 경로만 최적화 대상으로 지정하고 싶다면 next.config.js
파일에서 localPatterns
옵션을 활용할 수 있어요. 이 옵션을 사용하면 내가 원하는 경로만 이미지를 최적화하고, 그 외의 경로에 대해서는 요청을 차단할 수 있답니다.
예를 들어, 아래처럼 설정하면 /assets/images/
로 시작하는 로컬 이미지 경로만 최적화가 허용되고, 쿼리 스트링이 붙은 경로는 허용되지 않아요.
module.exports = {
images: {
localPatterns: [
{
pathname: '/assets/images/**',
search: '',
},
],
},
}
참고로, 위 설정을 적용하면
next/image
컴포넌트의src
속성이/assets/images/
로 시작하지 않거나 쿼리 스트링이 포함된 경우, 요청이 400 Bad Request로 처리됩니다. 따라서 불필요한 경로나 잘못된 요청을 막는 데 유용해요.
또한, localPatterns
외에도 remotePatterns
라는 옵션도 있는데, 이것은 외부 도메인의 이미지를 최적화 대상으로 설정할 때 사용됩니다. 다음에 이어서 remotePatterns
에 대해서도 알아볼게요.
이렇게 경로를 명확하게 지정해주면 보안 측면에서도 도움이 되고, 최적화 대상이 한정되니 관리도 편해지니 꼭 활용해보세요!
Next.js에서 외부 이미지를 사용할 때, 보안을 위해 외부 이미지에 대한 설정이 필요해요. 이렇게 해야 악의적인 사용자가 다른 도메인의 이미지를 마음대로 불러오는 걸 막을 수 있답니다.
Next.js는 외부 이미지를 Next.js Image Optimization API를 통해 제공하는데, 이때 remotePatterns
설정을 통해 내 계정에서 사용할 수 있는 외부 이미지 URL 패턴을 지정할 수 있어요. 이렇게 지정하면 지정된 도메인과 경로에 해당하는 이미지들만 안전하게 불러올 수 있습니다.
Next.js 15.3.0 이상 버전
아래처럼 next.config.js
파일에 remotePatterns
에 new URL
객체로 패턴을 지정해주면 돼요.
module.exports = {
images: {
remotePatterns: [new URL('https://example.com/account123/**')],
},
}
Next.js 15.3.0 미만 버전
이전 버전에서는 remotePatterns
를 객체 형태로 설정해줘야 하는데, 조금 더 자세하게 프로토콜, 호스트명, 포트, 경로 등을 나눠서 작성해줍니다.
module.exports = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'example.com',
port: '',
pathname: '/account123/**',
search: '',
},
],
},
}
추가 팁
pathname
에서/**
는 와일드카드로, 해당 경로 이하의 모든 이미지 경로를 허용한다는 뜻이에요. 필요에 따라 경로를 더 구체적으로 설정해주세요.- 만약 여러 도메인에서 이미지를 불러와야 한다면,
remotePatterns
안에 여러 패턴을 배열 형태로 추가하면 됩니다. - 보안을 위해 믿을 수 있는 도메인만 허용하는 게 가장 좋아요! 나중에 이미지 도메인이 바뀐다면 꼭 이 설정도 함께 업데이트해줘야 합니다.
앱에서 이미지를 최적화하면서도 보안도 챙기려면 이런 설정, 꼭 잊지 말고 해주세요!
next/image 컴포넌트를 사용할 때 외부 이미지 도메인을 어떻게 안전하게 설정할 수 있는지 아시나요? 이번에 알려드릴 내용은 Next.js의 next.config.js
파일에 remotePatterns
옵션을 활용하는 방법입니다.
먼저, 이렇게 설정하면 이미지 URL이 꼭 https://example.com/account123/
로 시작해야 하고, 쿼리 스트링은 없어야 해요. 다른 프로토콜(예: http), 호스트 이름, 포트 번호, 혹은 경로가 일치하지 않으면 400 Bad Request 에러가 발생합니다.
module.exports = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'example.com',
port: '',
pathname: '/account123/**',
search: '',
},
],
},
}
이렇게 해두면 고정된 경로 패턴을 안전하게 지정할 수 있어요. 근데 만약 여러 서브도메인이 있는 경우라면 어떻게 할까요?
그럴 때는 와일드카드 패턴을 활용할 수 있어요! 예를 들어, 아래 설정은 https 프로토콜에, img1.example.com
또는 me.avatar.example.com
같은 여러 서브도메인을 모두 허용합니다. 하지만 포트는 없고 쿼리 스트링도 없어야 하죠.
module.exports = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: '**.example.com',
port: '',
search: '',
},
],
},
}
속성 | 의미 | 예시 값 |
---|---|---|
protocol | 허용할 프로토콜 (http, https 등) | 'https' |
hostname | 호스트 이름, 와일드카드 가능(**) | '**.example.com' |
port | 포트 번호 (빈 문자열은 포트 없음) | '' |
pathname | 경로 (글로벌 와일드카드 /** 가능) | '/account123/**' |
search | 쿼리 스트링 (빈 문자열은 쿼리 없음) | '' |
참고로,
remotePatterns
안에 경로를 지정하려면pathname
속성을 꼭 써야 해요. 위 첫번째 예제에서/account123/**
처럼 사용하구요!
이 기능 덕분에 보안도 챙기면서 다양한 도메인에서 이미지를 마음껏 불러올 수 있답니다. 특히 외부 이미지가 많은 프로젝트에서는 꼭 살펴봐야 할 설정이에요.
요약하자면,
remotePatterns
를 쓰면 이미지 허용 도메인을 세밀하게 설정할 수 있어요.- 와일드카드를 활용해 여러 서브도메인도 한방에 통제 가능합니다.
- 프로토콜, 포트, 쿼리스트링까지 꼼꼼히 설정 가능해 오류를 줄이고 보안도 강화할 수 있죠.
이렇게 하시면 Next.js 프로젝트에서 안전하게 외부 이미지를 관리할 수 있어서, 성능과 안정성을 함께 챙길 수 있답니다! 혹시 더 궁금한 점 있으면 언제든지 댓글로 물어봐 주세요~
와일드카드 패턴은 경로 이름(pathname)과 호스트 이름(hostname) 모두에 사용할 수 있는데, 다음과 같은 문법을 가지고 있어요:
*
: 한 개의 경로 세그먼트(path segment) 또는 서브도메인(subdomain)에 대응해요.**
: 경로 세그먼트가 끝부분에 여러 개 있을 때, 또는 서브도메인이 시작 부분에 여러 개 있을 때 모두 매칭해요.
단, **
문법은 패턴 중간에는 사용할 수 없다는 점 참고하세요!
알아두면 좋은 점: 프로토콜, 포트, 경로(pathname), 검색(search) 정보를 생략하면 자동으로 와일드카드
**
가 포함된 것으로 간주돼요. 하지만 이 방법은 권장하지 않아요. 왜냐하면 의도치 않은 URL까지 매칭되어 악의적인 사용자의 공격에 노출될 수 있기 때문이에요.
조금 더 부연하자면, 와일드카드를 사용할 때는 어떤 부분에 얼마만큼 유연함을 줄지 꼭 신중히 결정해야 해요. 특히, **
는 매우 강력한 매칭이 되기 때문에 보안상 위험할 수 있으니, 가능한 최소한으로 사용하는 걸 추천합니다.
예를 들어, 호스트네임에 *.example.com
은 abc.example.com
에는 매칭되지만, xyz.abc.example.com
까지는 포함하지 않아요. 반면 **.example.com
이라면 여러 개의 서브도메인을 복수로 포함하는 경우에 사용하죠.
이런 와일드카드 패턴 활용법을 잘 이해하면, URL 필터링이나 라우팅 기능을 구현할 때 훨씬 편리하게 조건을 작성할 수 있어요!
next.config.js에서 remotePatterns 속성에 search 옵션을 사용하는 예시를 보여드릴게요:
module.exports = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'assets.example.com',
search: '?v=1727111025337',
},
],
},
}
참고할 점: 위 예시는 next/image 컴포넌트의 src 값이
https://assets.example.com
으로 시작해야 하고, 쿼리 스트링이 정확히?v=1727111025337
로 일치해야만 이미지를 불러올 수 있다는 뜻이에요. 만약 프로토콜이나 쿼리 스트링이 다르면 400 Bad Request 오류가 발생할 수 있습니다.
이렇게 remotePatterns에 search 값을 명시하면, URL 매칭 조건을 엄격하게 설정할 수 있어서 보안이나 캐시 관리를 조금 더 세밀하게 할 수 있어요.
domains와의 차이점
domains
속성은 단순히 hostname 기반으로 이미지를 허용하는 반면, remotePatterns
는 프로토콜, 호스트명, 경로, 포트, 검색(쿼리)까지 좀 더 자세히 지정할 수 있다는 점에서 훨씬 유연해요. 만약 특정 쿼리를 포함한 URL만 받고 싶다면 remotePatterns
를 사용하세요.
예를 들어, 다음은 domains와 remotePatterns의 비교입니다:
속성명 | 설명 | 유연성 |
---|---|---|
domains | 도메인 이름만 지정, 간단히 허용 목록 작성 가능 | 낮음 |
remotePatterns | 프로토콜, 호스트, 경로, 검색 등 세부적으로 지정 가능 | 매우 높음 |
개발할 때 이렇게 쿼리 스트링까지 조건에 넣으면, 이미지 요청에서 인증 토큰이나 버전 관리 같은 정보를 활용할 때도 편리해요.
필요에 따라 여러분 프로젝트에 맞는 방식을 선택해서 사용해 보면 좋겠네요!
Next.js 14부터는 remotePatterns라는 엄격한 설정 방식을 권장하면서, 기존의 domains 설정이 점점 Deprecated(사용 중단)되고 있다는 경고가 나오는 경우가 많아요.
왜 그러냐면, domains 설정은 외부 이미지 도메인을 허용하는 데는 편리하지만, 와일드카드 패턴 매칭이 안 되고, 프로토콜(https, http), 포트, 경로나 쿼리 스트링 같은 세밀한 조건을 제한할 수가 없거든요. 즉, 보안 측면에서 좀 허술할 수 있어요. 특히 도메인의 모든 콘텐츠를 소유하지 않은 경우에는 위험할 수도 있어요.
대신 remotePatterns는 이런 부분을 세밀하게 지정할 수 있어서, 악성 사용자가 이미지 요청을 조작해 공격하는 걸 막아줄 수 있답니다.
domains 설정 예시 (next.config.js)
module.exports = {
images: {
domains: ['example.com', 'cdn.example.com'],
},
};
위처럼 도메인을 배열로 넣어주면, 해당 도메인에 있는 외부 이미지들을 Next.js 이미지 최적화 기능과 함께 쓸 수 있어요. 하지만 프로토콜이나 경로는 제한하지 못하고, 와일드카드(*)도 안 돼서 살짝 아쉽죠.
추가로, remotePatterns 설정법은?
만약 remotePatterns
를 써보고 싶다면, 이렇게 쓸 수 있어요:
module.exports = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'example.com',
port: '',
pathname: '/images/**',
},
],
},
};
이렇게 하면 https://example.com/images/
하위 경로만 허용하는 식으로 훨씬 정밀한 제어가 가능해요. 물론, Next.js 14 이상이어야 지원됩니다.
정리하자면
- domains는 간단하지만 제한적이고 보안 취약점이 있을 수 있으니 조심!
- remotePatterns는 더 엄격하고 안전한 설정 방법
- 여러분이 도메인 소유주가 아니면 remotePatterns 추천!
개발하면서 이미지 도메인 설정할 때 참고하시고, 가능하면 최신 방식인 remotePatterns로 업데이트해보세요! 안전한 앱 만드는 데 큰 도움이 될 거예요.
여러분, Next.js에서 이미지 최적화를 할 때 기본 API 대신 클라우드 제공업체를 이용하고 싶을 때가 있죠? 그럴 땐 next.config.js 파일에서 loaderFile 옵션을 활용해서 커스텀 로더를 설정할 수 있어요.
먼저 기본적으로 도메인을 설정해주는 예제를 볼게요:
module.exports = {
images: {
domains: ['assets.acme.com'], // 여기서 외부 이미지 도메인을 지정해줘요.
},
}
이렇게 하면 Next.js가 assets.acme.com
에서 가져오는 이미지를 허용하고 최적화할 수 있게 됩니다.
그런데 만약 클라우드 제공업체(예: Cloudinary, Imgix 등)를 직접 써서 이미지 최적화를 맡기고 싶으면 어떻게 할까요? 이때는 loaderFile
옵션을 사용해서 커스텀 이미지 로더를 지정하면 돼요. 예시는 아래와 같습니다:
module.exports = {
images: {
loader: 'custom', // 커스텀 로더 사용 선언
loaderFile: './my/image/loader.js', // 직접 만든 로더 파일 경로
},
}
여기서 중요한 점
loader
는'default'
,'imgix'
,'cloudinary'
,'akamai'
등 미리 준비된 옵션도 있지만,custom
을 쓰면 직접 로더 코드를 만들어 제어할 수 있습니다.loaderFile
에 적힌 파일에서는 함수가 export되어 있어야 하며, 이미지 URL과 원하는 크기 등을 인자로 받아 최종 이미지 URL을 반환해야 해요.
예를 들어, ./my/image/loader.js
파일은 대략 이런 모양일 수 있어요:
module.exports = ({ src, width, quality }) => {
return `https://your-cloud-provider.com/${src}?w=${width}&q=${quality || 75}`
}
이렇게 하면 Next.js가 이미지 최적화를 요청할 때 이 커스텀 로더를 거쳐 이미지 URL을 생성하고, 클라우드에서 최적화된 이미지를 받아 올 수 있습니다.
요약하자면, Next.js의 이미지 컴포넌트는 기본 제공 API로도 훌륭하지만, 자신만의 클라우드 서비스나 특별한 최적화 방식을 쓰고 싶을 땐 loader
를 'custom'
으로 설정하고, loaderFile
에서 직접 URL 생성 방식을 관리하면 완전 내 맘대로 컨트롤할 수 있어 편리하답니다!
더 궁금하면 언제든 질문 주세요~
Next.js에서 이미지 로더를 설정할 때, loader
파일은 반드시 Next.js 애플리케이션 루트 기준으로 상대 경로를 가리켜야 합니다. 이 파일에서는 기본(default)로 문자열을 반환하는 함수 하나를 export해야 하는데요, 예를 들어 아래처럼 작성할 수 있습니다:
'use client'
export default function myImageLoader({ src, width, quality }) {
return `https://example.com/${src}?w=${width}&q=${quality || 75}`
}
여기서 src
는 이미지 경로, width
는 요청할 이미지의 너비, quality
는 이미지 품질(기본 75)인데요, 함수가 최종적으로 반환하는 문자열은 이미지 URL이 됩니다. 이 방식을 쓰면 이미지 요청을 커스터마이징할 수 있어서 CDN이나 외부 이미지 서버와 연동할 때 아주 유용하답니다.
그리고 꼭 파일 하나에 넣어야 한다고 생각하지 마세요! Next.js next/image
컴포넌트의 loader
프롭에도 직접 함수를 넘겨줄 수 있어요. 이렇게 하면 각 이미지마다 다른 로더를 쓸 수도 있어서 더욱 유연한 관리가 가능합니다.
아래는 두 가지 예시입니다:
구분 | 설명 | 코드 |
---|---|---|
1. loader 파일 사용 | 루트에 파일 만들어서 재사용 | js 'use client' export default function myImageLoader({ src, width, quality }) { return `https://example.com/${src}?w=${width}&q=${quality |
2. loader prop 사용 | 이미지 컴포넌트마다 다르게 설정 | jsx import Image from 'next/image'; const myImageLoader = ({ src, width, quality }) => { return `https://example.com/${src}?w=${width}&q=${quality |
Tip!
loader
함수가 반환하는 URL은 클라이언트가 실제로 접근 가능한 주소여야 해요. 내부 경로나 상대 경로를 그대로 쓰면 안 됩니다.- 품질(
quality
) 옵션을 넣는 이유는 이미지 크기와 품질간 밸런스를 맞추기 위해서인데, 기본값은 75이지만 필요에 따라 조절하세요. - 공식 Next.js 문서(https://nextjs.org/docs/api-reference/next/image#loader)에서도 다양한 예제를 참고할 수 있어요.
이미지 요청 경로 다루는 게 어렵게 느껴질 수 있는데, 이런 사용자 정의 로더를 잘 활용하면 서버 부담도 줄이고, 이미지 로딩 속도도 개선할 수 있답니다! 궁금하면 꼭 한 번 시도해 보세요.
- 커스텀 이미지 로더 설정
참고할 점: 이미지 로더 파일을 커스터마이징할 때 함수(function)를 넘겨줘야 하는데, 이 함수는 Client Components에서 직렬화(serialization) 과정을 거쳐야 제대로 작동해요.
고급 설정
아래 설정들은 주로 고급 사용자들을 위한 내용이에요. 보통은 건드릴 필요가 없고, 만약 이 설정들을 직접 바꾸면 향후 Next.js 업데이트 시 기본 설정이 변경돼도 자동 반영되지 않으니 주의가 필요해요.
사실 이미지 로더를 직접 커스터마이징하는 경우는 주로 이미지 처리 방식을 세밀하게 조정하고 싶을 때입니다. 예를 들면, 이미지 CDN을 직접 지정하거나, 특정 포맷 변환, 압축 옵션을 다루고 싶을 때 쓰이죠. 하지만 대부분 프로젝트에서는 Next.js가 제공하는 기본 이미지 로더로 충분한 성능과 편리함을 느낄 수 있으니, 특별한 이유가 없다면 기본 설정을 유지하는 게 안정적입니다.
필요하다면 Client Component에서 커스텀 함수를 어떻게 직렬화하는지 추가 예제도 나중에 소개할게요.
deviceSizes
만약 여러분이 타겟하려는 사용자들이 주로 사용하는 디바이스의 화면 너비를 알고 있다면, next.config.js
파일에서 deviceSizes
라는 속성에 해당 너비들을 지정할 수 있어요. 이렇게 지정해주면, next/image
컴포넌트가 sizes
속성을 사용할 때 사용자의 디바이스에 가장 적합한 이미지 크기를 골라서 제공할 수 있게 됩니다.
만약 별도로 설정하지 않으면, Next.js는 기본값으로 아래와 같은 디바이스 크기들을 사용해요.
module.exports = {
images: {
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
},
}
기본 deviceSizes 값 |
---|
640 |
750 |
828 |
1080 |
1200 |
1920 |
2048 |
3840 |
이걸 설정하는 가장 큰 장점은 이미지 최적화에서 효율성! 예를 들어, 모바일 사용자에게 굳이 큰 해상도의 이미지를 내려주면 데이터 낭비가 심하잖아요. 그런데 이걸 활용하면 크고 작은 여러 버전의 이미지가 준비되어서, 각각의 디바이스에 최적화된 이미지를 업로드 없이 자동으로 빠르게 제공해주니까요.
추가로, deviceSizes
와 비슷하게 imageSizes
라는 옵션도 있는데요, 이건 고정된 이미지 크기(아이콘 같은 작은 이미지)들을 지정할 때 유용해요. 보통 deviceSizes
는 반응형 이미지에, imageSizes
는 고정 사이즈 이미지에 활용한다고 이해하시면 좋아요.
실제로 프로젝트에 맞춰 이 값들을 잘 조절하면 성능이 확연히 개선되는 걸 체감할 수 있으니 한 번 프로젝트에 적용해보세요!
imageSizes란?
Next.js에서 next.config.js
파일 안에 images.imageSizes
라는 속성을 통해 이미지 너비 리스트를 직접 지정할 수 있어요. 이 리스트는 deviceSizes
라는 또 다른 배열과 합쳐져서, 이미지의 srcset
을 생성할 때 사용되는 전체 크기 배열을 만든답니다.
그런데 왜 두 개의 리스트가 따로 있냐고요? 그 이유는, imageSizes
는 이미지에 sizes
속성이 제공될 때만 사용돼요. 즉, sizes
prop이 있으면 이 이미지는 화면 전체 너비를 쓰는 게 아니라, 화면보다 작은 크기로 보여준다는 의미거든요. 그래서 imageSizes
에 들어가는 값들은 항상 deviceSizes
의 가장 작은 크기보다 작아야 해요. 이것은 이미지가 너무 커서 불필요한 리소스를 쓰는 걸 방지해줍니다.
만약 여러분이 따로 설정을 안 해주면 Next.js가 기본값을 사용하게 돼요.
추가로 알아두면 좋은 점!
deviceSizes
는 주로 일반적인 기기 화면 크기를 기준으로 하며, Next.js가 자동으로 기본값을 제공하지만, 상황에 맞게 조정할 수도 있어요.- 예를 들어, 모바일 최적화가 매우 중요하거나, 특정 컴포넌트가 작은 이미지 크기를 주로 쓴다면,
imageSizes
를 알맞게 지정해주면 네트워크 대역폭 절약과 빠른 로딩에 도움이 됩니다. - 또한, Image 컴포넌트에서
sizes
prop을 사용하면, 당신이 지정한imageSizes
배열에 맞게 다양한 이미지를 브라우저가 선택하게 됩니다.
아래는 각 속성별 기본값이 어떻게 설정되어 있는지 참고해보세요.
속성 이름 | 기본값 | 설명 |
---|---|---|
deviceSizes | [640, 750, 828, 1080, 1200, 1920, 2048, 3840] | 대표적인 기기 너비 배열 |
imageSizes | [16, 32, 48, 64, 96] | sizes prop이 있는 이미지에 사용되는 작고 세밀한 너비 배열 |
요렇게 이해하면 Next.js 이미지 최적화가 좀 더 명확해지실 거예요! 혹시 이미지 로딩 최적화 팁에 관심 있으면 또 알려드릴게요~
오늘은 Next.js에서 이미지 최적화 설정하는 방법에 대해 쉽고 간단하게 알아볼게요. 이미지 크기와 퀄리티(quality)를 직접 설정해서 웹사이트 로딩 속도와 사용자 경험을 개선할 수 있답니다.
이미지 크기 (imageSizes
) 설정하기
Next.js의 기본 이미지 최적화 API는 여러 크기의 이미지를 자동으로 만드는데요. 만약 이 크기를 내가 원하는 크기로 지정하고 싶다면 next.config.js
파일에 imageSizes
를 설정하면 돼요.
module.exports = {
images: {
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
},
}
위 코드에서 이미지 사이즈 배열에 들어간 숫자들은 픽셀 단위의 너비를 의미해요. 예를 들어 16은 16px 너비의 이미지를 생성한단 뜻이죠. 이 설정을 참고해서 사이트에 필요한 크기만 지정하면 불필요한 이미지 생성과 트래픽을 줄일 수 있어요.
이미지 퀄리티 (qualities
) 제한하기
기본적으로 Next.js는 이미지 퀄리티를 1부터 100까지 모두 허용해요. 근데 세부적으로 퀄리티를 제한하고 싶을 때는 이렇게 해볼 수 있습니다.
module.exports = {
images: {
qualities: [25, 50, 75],
},
}
이렇게 하면 오직 25%, 50%, 75% 퀄리티만 이미지 최적화 API에서 허용하게 돼요. 덕분에 최대한 이미지 품질과 용량을 조절할 수 있죠. 예를 들어, 퀄리티를 너무 낮게 하면 화질이 떨어져 사용자 경험이 나빠질 수 있으니, 적당한 균형을 찾는 게 중요해요!
참고로 더 알아두면 좋은 팁
imageSizes
는 기본적으로[16, 32, 48, 64, 96, 128, 256, 384]
크기들이 포함돼 있지만, 커스텀 사이즈 추가 가능해요.imageSizes
는 사이즈 지정에 사용하고, 실제 사용할 수 있는 이미지 최대 크기는deviceSizes
에서 설정할 수 있어요.- 퀄리티 제한을 할 때는 일반적으로 75~80 정도가 적당히 고화질과 적당한 용량의 밸런스로 추천됩니다.
- 더 나아가
formats
설정을 이용해서 WebP, AVIF 등 최신 이미지 포맷으로 변환하는 것도 가능하니까 같이 고려해보세요!
마무리
Next.js 이미지 최적화 세팅은 웹 속도를 개선하고 사용자 경험을 올리는 데 적절한 이미지 크기와 퀄리티를 직접 정할 수 있어 정말 편리해요. 사이트 규모와 용도에 맞춰서 적절하게 설정해 보시길 추천드려요!
앞으로도 이런 실용적인 팁들 많이 공유할게요. 언제든 질문 주세요~!
위 예시에서는 허용되는 품질 값이 25, 50, 75 세 가지로 정해져 있어요. quality prop에 이 배열에 없는 값이 들어가면, 이미지 요청이 실패하면서 400 Bad Request 에러가 발생합니다. 그러니까 품질 설정할 때는 꼭 허용된 값 중에서 선택해야 한다는 점 꼭 기억하세요!
formats
기본적으로 Image Optimization API는 요청에 포함된 브라우저의 Accept 헤더를 통해 어떤 이미지 포맷을 지원하는지 자동으로 감지합니다. 이 정보를 바탕으로 가장 최적화된 출력 포맷을 선택해주는 거죠.
여기서 중요한 점은 만약 Accept 헤더에 여러 개의 지원 포맷이 포함되어 있다면, 미리 설정된 formats 배열에서 가장 먼저 일치하는 포맷을 사용한다는 것입니다. 즉, 배열의 순서가 정말 중요해요! 만약 일치하는 포맷이 없거나, 원본 이미지가 애니메이션 GIF 같은 특수한 경우라면, API는 원본 이미지 포맷을 그대로 반환합니다.
이 부분을 이해하면, 이미지 최적화를 하면서도 호환성을 최대한 보장할 수 있고, 불필요한 에러도 막을 수 있으니 꼭 참고하세요!
아래는 요점 정리한 표예요:
키워드 | 설명 |
---|---|
quality | 허용값은 25, 50, 75. 배열 외 값 입력 시 400 Bad Request 발생 |
Accept 헤더 | 브라우저가 지원하는 이미지 포맷 정보를 포함하는 HTTP 헤더 |
formats 배열 | Accept 헤더와 비교해 출력 포맷을 결정. 배열 내 순서가 최종 포맷 선택에 중요 |
Fallback | 일치하는 포맷 없거나 애니메이션 이미지일 경우 원본 포맷 사용 |
이렇게 정리해두면 이미지 최적화 설정할 때 훨씬 이해가 쉬워질 거예요!
설정을 따로 해주지 않으면 기본값으로 아래와 같은 설정이 적용돼요.
module.exports = {
images: {
formats: ['image/webp'],
},
}
여기서 formats
는 이미지 최적화를 위해 어떤 포맷을 사용할지 지정하는 옵션인데요, 기본값은 webp
포맷이에요. webp
는 구글에서 만든 이미지 포맷으로, JPG나 PNG보다 용량을 줄이면서도 화질 손실이 적어 많이 쓰이죠.
그리고 만약 AVIF 포맷을 사용해보고 싶다면, 이렇게 바꿔주면 돼요.
module.exports = {
images: {
formats: ['image/avif'],
},
}
AVIF는 최근에 각광받는 이미지 포맷으로, webp
보다 압축률이 더 좋고 화질도 뛰어나지만 아직 모든 브라우저에서 완벽하게 지원하는 건 아니에요. 그래서 위 설정처럼 넣으면, 브라우저가 AVIF를 지원하지 않을 땐 원본 이미지 포맷을 대신 보여줘서 큰 문제가 없답니다.
참고로, formats
배열에 여러 포맷을 넣어서 동시에 지원하게 할 수도 있어요. 예를 들어 내비게이션이 상황에 맞게 최적화된 포맷을 골라주도록 하려면 이렇게 할 수 있죠.
module.exports = {
images: {
formats: ['image/avif', 'image/webp'],
},
}
이렇게 하면 AVIF를 우선 시도하고, 지원하지 않으면 webp, 또 그마저 지원하지 않으면 원본 이미지로 자동으로 fallback 해준답니다.
요즘은 이미지 최적화를 하는 게 페이지 로딩 속도 개선에 큰 도움이 되니까, 이런 포맷 지정은 꼭 활용해보길 추천해요!
참고할 점: 대부분의 경우에는 여전히 WebP 형식을 사용하는 것을 추천해요. AVIF는 인코딩하는 데 WebP보다 약 50% 더 시간이 걸리지만, 압축률은 약 20% 더 좋아서 파일 크기가 더 작아진답니다. 그래서 이미지를 처음 요청할 때는 속도가 조금 느릴 수 있지만, 캐시된 이후에는 훨씬 빠르게 로드돼요. 만약 Next.js 앞에 Proxy나 CDN을 직접 운영한다면, 반드시 Proxy가
Accept
헤더를 전달하도록 설정해줘야 해요. 이 부분을 놓치면 최적화된 이미지가 제대로 제공되지 않을 수 있거든요.
캐싱 동작 방식
기본 이미지 로더의 캐싱 알고리즘은 다음과 같아요. 다른 로더를 사용한다면 클라우드 제공업체의 문서를 참고하는 게 좋아요.
이미지는 요청 시 동적으로 최적화되고, 프로젝트의 distDir/cache/images
디렉터리에 저장돼요. 이렇게 최적화된 이미지는 만료 시간이 도달할 때까지 이후 요청에 동일하게 제공됩니다. 만약 요청한 이미지가 캐시돼있지만 만료되었다면, 서버는 만료된(오래된) 이미지를 바로 보여주고, 백그라운드에서는 이미지를 다시 최적화(재검증)해서 새 캐시와 만료 시간으로 업데이트합니다.
이렇게 하면 처음 사용자에게도 최소한의 지연만 주면서 최신 이미지를 유지하는 효율적인 캐싱 전략이 구현되는 거죠. 실제로 운영할 때는 압축률과 인코딩 속도, 그리고 캐시 만료 정책을 적절히 조절하는 게 중요해요. CDN이나 프록시를 쓴다면, Accept
헤더가 제대로 전달되는지 꼭 확인해서 원하는 포맷으로 이미지를 제공받도록 하는 것도 핵심입니다!
이미지 캐시 상태를 확인할 때는 x-nextjs-cache
라는 응답 헤더 값을 보면 됩니다. 이 헤더가 가리키는 값은 보통 다음 세 가지 중 하나예요:
캐시 상태 | 설명 |
---|---|
MISS | 경로가 캐시에 없다는 뜻이에요. 보통 이건 처음 한번, 첫 방문할 때 발생해요. |
STALE | 경로가 캐시에 있긴 한데, 재검증(revalidate) 시간이 초과돼서 백그라운드에서 업데이트가 진행 중인 경우입니다. |
HIT | 경로가 캐시에 있고, 재검증 시간이 지나지 않았을 때를 의미해요. |
캐시 만료 시간(정확히 말하면 Max Age)은 minimumCacheTTL
설정값과 업스트림 이미지의 Cache-Control
헤더 중 더 큰 값으로 정해집니다. 여기서 Cache-Control
헤더의 max-age
값을 기준으로 하는데, 만약 s-maxage
와 max-age
둘 다 있으면 s-maxage
가 우선입니다. 그리고 이 max-age
값은 CDN이나 브라우저 같은 하위 클라이언트에게도 그대로 전달돼요.
그리고 몇 가지 팁을 드리면:
minimumCacheTTL
을 설정하면, 업스트림 이미지에서 Cache-Control 헤더가 없거나 너무 낮을 때 캐시 지속 시간을 인위적으로 늘릴 수 있어요.deviceSizes
와imageSizes
를 잘 설정해서 생성되는 이미지 종류(사이즈)를 줄이면 서버의 부하도 줄이고 캐시 효율도 높일 수 있어요.formats
설정으로 여러 이미지 포맷 생성을 끄고, 한 가지 포맷만 사용하도록 할 수도 있어요. 이건 클라이언트의 호환성이나 트래픽 관리에 도움됩니다.
이렇게 캐시 설정을 적절히 조절하면, 이미지 로딩 속도 향상과 서버 비용 절감 두 마리 토끼를 잡을 수 있으니, 꼭 한 번 살펴보세요!
minimumCacheTTL
이미지 최적화 캐시의 TTL(Time to Live), 즉 이미지가 캐시에서 살아있는 시간을 초 단위로 설정할 수 있어요.
사실 대부분의 경우에는 Static Image Import 방식을 쓰는 게 더 좋아요. 이 방법은 파일 내용을 자동으로 해시(hash)해서 캐시를 사실상 영구적으로 유지하는 효과가 있고, Cache-Control
헤더에 immutable
이 설정돼서 브라우저가 굳이 자주 새로 다운로드하지 않게 해주거든요.
만약 따로 설정하지 않으면 기본값으로 아래 설정이 사용됩니다.
module.exports = {
images: {
minimumCacheTTL: 60, // 1분 간 캐시 유지
},
}
추가 팁!
- TTL을 너무 짧게 하면 캐시를 자주 새로 받아서 서버 부하가 늘어나고, 반대로 너무 길게 설정하면 오래된 이미지가 사용될 수도 있어요.
- 그래서 가능하면 정적 이미지(import 방식)를 활용하는 게 좋고, 동적으로 변경될 가능성이 있는 이미지만 TTL을 적절히 조절하는 방식을 추천드립니다.
- 만약 이미지가 자주 변경되는 편이라면 TTL을 짧게, 그렇지 않으면 길게 잡는 게 무난해요.
이렇게 적절한 캐시 설정으로 웹사이트 속도와 사용자 경험을 더 좋게 만들어 보세요!
이미지 최적화할 때 TTL(Time To Live)을 늘려서 재검증(revalidation) 횟수를 줄이고, 그 결과 비용 절감 효과를 기대할 수 있어요. 예를 들어, Next.js 내 설정 파일에서 이렇게 써주면 돼요:
module.exports = {
images: {
minimumCacheTTL: 2678400, // 31일 (초 단위)
},
};
여기서 minimumCacheTTL
은 이미지가 캐시에 머무르는 최소 시간을 의미해요. 최적화된 이미지의 만료 시간(또는 Max Age)은 이 값과 원본 이미지 서버가 보내는 Cache-Control
헤더 중 더 긴 쪽을 따르게 됩니다.
만약 이미지마다 캐싱 정책을 다르게 하고 싶다면, 최적화 요청이 가는 /_next/image
경로가 아니라 원본 이미지 경로(예: /some-asset.jpg
)에 직접 Cache-Control
헤더를 설정해야 합니다. 그렇지 않으면 모든 이미지가 같은 캐시 정책을 따르게 돼요.
참고로, TTL을 너무 길게 잡으면 업데이트된 이미지가 바로 반영되지 않을 수 있으니, 상황에 맞게 적절한 기간을 선택하는 게 좋아요. 개발 중에는 짧게, 배포 환경에선 길게 설정하는 방식도 괜찮겠죠? 그리고 이미지 캐싱 전략은 비용뿐만 아니라 사용자 경험에도 큰 영향을 주니 신중하게 고민해보세요!
지금은 캐시를 무효화할 수 있는 별도의 방법이 없기 때문에, minimumCacheTTL 값을 낮게 설정하는 게 좋아요. 그렇지 않으면 src 속성을 직접 변경하거나, distDir
/cache/images 폴더를 수동으로 삭제해야 할 수도 있어요.
disableStaticImages
기본 설정으로는 import icon from './icon.png'
처럼 정적 파일을 임포트해서 src 속성에 바로 전달할 수 있어요.
하지만 어떤 경우에는, 다른 플러그인과 충돌이 일어나서 임포트가 다르게 동작해야 할 때가 있어요. 그럴 때 이 기능을 끄는 옵션이 바로 disableStaticImages
입니다.
한 가지 팁을 더 드리자면, 이미지 캐시를 적절히 관리하지 않으면 개발 중에 변경 사항이 바로 반영되지 않아 꽤 답답할 수 있어요. 그래서 개발 모드에서는 캐시 TTL을 낮게 설정하거나, 필요할 땐 수동으로 캐시를 삭제하는 습관을 들이면 좋습니다. 또한, 플러그인 간 충돌을 미리 파악해서 disableStaticImages
옵션을 활용하면 예상치 못한 문제도 피할 수 있답니다!
Next.js에서 static 이미지 임포트를 비활성화하고 싶다면, next.config.js
파일에 아래와 같이 설정할 수 있어요:
module.exports = {
images: {
disableStaticImages: true,
},
}
이 설정을 하면 정적 이미지 임포트를 끄게 되어, 이미지 파일들을 직접 import 하는 방식 대신 다른 방법으로 이미지를 다루게 돼요. 예를 들어, public 폴더에 이미지를 두고 경로로만 접근하는 방식이죠.
dangerouslyAllowSVG 옵션에 대해
Next.js 기본 이미지 로더는 SVG 최적화를 지원하지 않는데, 그 이유는 두 가지가 있어요.
- SVG는 벡터 이미지라서 크기를 조절해도 화질 손실이 없어요. 그렇기 때문에 변형 과정이 딱히 필요 없죠.
- SVG 파일은 HTML/CSS와 비슷한 특성을 갖고 있어서, 제대로 된 Content Security Policy(CSP) 헤더 없이는 보안 취약점이 생길 수 있어요.
그래서 dangerouslyAllowSVG
옵션이 있긴 하지만, 이걸 사용할 땐 보안에 특히 신경 써야 해요. SVG를 사용하면서 보안 문제가 걱정된다면, SVG를 컴포넌트 형태로 다루거나 외부에서 안전하게 처리한 뒤 사용하는 걸 추천합니다.
추가 팁!
- 만약 SVG를 React 컴포넌트처럼 사용하고 싶다면,
@svgr/webpack
을 쓰는 방법도 있어요. 이걸 쓰면 SVG를 import 해서 SVG 컴포넌트로 만들 수 있죠. - 그리고 이미지 최적화는 Next.js 이미지 컴포넌트의 핵심 기능 중 하나니까, 가능하면 static 이미지 임포트 기능을 끄기보다는 제대로 활용하는 걸 추천해요.
이런 설정 하나하나 이해하고 적용하다 보면 Next.js에서 이미지 다루는 법에 자신감이 생길 거예요!
그래서 src 속성이 SVG임이 확실할 때는 unoptimized 속성을 사용하는 걸 추천해요. 실제로 src가 ".svg"로 끝날 때는 이 부분이 자동으로 적용됩니다.
하지만 만약 기본 이미지 최적화 API를 사용해서 SVG 이미지를 제공해야 한다면, next.config.js 파일 안에 dangerouslyAllowSVG 옵션을 설정할 수 있어요:
module.exports = {
images: {
dangerouslyAllowSVG: true,
contentDispositionType: 'attachment',
contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;",
},
}
여기서 주의할 점은, SVG 이미지가 종종 악성 스크립트를 포함할 수 있어서 보안 면에서 취약할 수 있다는 거예요. 그래서 contentDispositionType을 'attachment'로 설정해서 브라우저가 이미지를 그냥 보여주는 게 아니라 다운로드하도록 강제하는 걸 추천하고, contentSecurityPolicy를 지정해서 이미지 안에 숨어있는 스크립트가 실행되지 못하도록 막는 것이 매우 중요하답니다.
참고로, SVG는 벡터 이미지라서 해상도에 상관없이 선명한 장점이 있지만, 안전하지 않은 SVG를 그대로 사용하면 XSS 공격에 노출될 수 있으니, 가능하면 신뢰할 수 있는 SVG만 쓰거나 이런 보안 설정을 꼭 해주세요!
contentDispositionType
기본적으로 이미지 로더(loader)는 Content-Disposition
헤더를 attachment
로 설정합니다. 이 설정이 중요한 이유는 API가 원격 이미지를 임의로 불러올 수 있기 때문에 보안을 강화하기 위해서예요.
설정값 | 설명 |
---|---|
attachment | 브라우저가 이미지를 직접 방문하면 다운로드하도록 강제함 (기본값) |
inline | 브라우저가 이미지를 직접 방문했을 때 바로 렌더링해서 보여줌 |
특히, dangerouslyAllowSVG
옵션이 켜져 있을 경우 attachment
로 설정하는 것이 더 안전합니다. SVG 파일은 악성코드로 악용될 수 있기 때문에, 무조건 다운받게 만들어서 브라우저가 바로 실행하지 못하게 하는 거죠.
하지만 경우에 따라서는 직접 이미지를 방문했을 때 다운로드 받는 게 아니라 바로 화면에 표시되길 원할 수도 있어요. 이런 때는 contentDispositionType
을 inline
으로 설정하면 됩니다. 그러면 URL을 브라우저에 직접 입력했을 때도 이미지가 바로 보이게 되죠.
추가 팁!
- 만약 내 서비스에서 외부에서 임의로 이미지를 불러오는걸 허용한다면, 보안에 신경 써서 꼭
attachment
로 두세요. - SVG는 특히 조심해야 하니, 내장 SVG가 아닌 외부 SVG를 불러올 때는
inline
보다는attachment
를 추천해요. - 하지만 사용자 경험(UX)을 고려해 이미지 바로 보기가 자주 필요한 서비스라면
inline
설정을 고려해볼 수 있습니다. 이럴 땐 SVG 보안 이슈도 신경 써주세요!
자, 이번에는 Next.js에서 이미지 최적화와 애니메이션 이미지 다루는 방법에 대해 살펴볼게요.
module.exports = {
images: {
contentDispositionType: 'inline',
},
}
위 설정 코드는 이미지 응답 시 Content-Disposition
헤더를 'inline'으로 설정해주는 거예요. 이렇게 하면 브라우저가 이미지를 다운로드하지 않고, 바로 화면에 표시하도록 하는 역할을 해요. 이미지를 웹페이지 내에서 자연스럽게 보여주고 싶을 때 유용하죠.
애니메이션 이미지 처리
Next.js의 기본 이미지 로더는 애니메이션이 들어간 이미지(예: GIF, APNG, WebP)가 들어오면 자동으로 이미지 최적화를 건너뛰고 원본 그대로 이미지를 서빙해줍니다. 왜냐면 애니메이션 이미지 최적화는 조금 까다롭고, 최적화 과정에서 애니메이션이 깨질 위험이 있거든요.
자동으로 애니메이션인지 판단하는 기능은 완벽하진 않지만, 대부분의 경우 GIF, APNG, WebP 파일 포맷을 기준으로 잘 감지해 줍니다.
만약 특정 애니메이션 이미지에 대해서 확실하게 최적화를 건너뛰고 싶다면, Next.js의 Image
컴포넌트에 unoptimized
속성을 추가해주면 돼요.
import Image from 'next/image'
export default function MyComponent() {
return (
<Image
src="/animated-image.gif"
alt="애니메이션 이미지"
width={500}
height={300}
unoptimized // 이 속성 덕분에 해당 이미지는 최적화 없이 바로 서빙됩니다.
/>
)
}
추가 팁!
- GIF 대신 WebP 포맷으로 애니메이션 이미지를 사용하는 것도 좋은 방법이에요. WebP는 파일 크기는 훨씬 작으면서도 화질을 잘 유지하거든요.
- Next.js에서 자동 최적화를 사용하면 성능이 좋아지지만, 애니메이션 이미지 때문에 어쩔 수 없이 건너뛰는 경우가 있으니, 애니메이션 이미지가 많다면 CDN 또는 별도의 외부 이미지 서버를 사용하는 것도 고려해보세요.
요약하면, Next.js는 애니메이션 이미지에 대해 친절한 자동 최적화를 제공하지만, 100% 완벽하진 않아서 직접 unoptimized
플래그를 활용하는 게 좋고, contentDispositionType: 'inline'
설정으로 이미지를 웹 내에서 부드럽게 다룰 수 있다는 이야기였습니다!
필요하면 언제든지 더 구체적인 사용법도 알려드릴게요 :)
반응형 이미지 만들기
웹 개발하다 보면 다양한 기기별 해상도를 지원하기 위해 이미지도 그에 맞게 잘 보여줘야 할 때가 많죠. 기본적으로 srcset 속성에는 1x, 2x 해상도 이미지가 들어가서 화면의 픽셀 밀도에 맞춰 이미지를 바꿔주는데요. 하지만, 뷰포트 크기에 따라 이미지 크기도 유동적으로 바꾸고 싶다면, srcset 뿐만 아니라 sizes 속성도 함께 설정해줘야 합니다. 그리고 스타일이나 className으로 이미지 크기를 조정하는 것도 중요하죠.
아래는 반응형 이미지를 만드는 몇 가지 방법 중 하나인 '정적 import 사용하기' 예시입니다.
정적 임포트로 반응형 이미지 만들기
React나 Next.js 같은 환경에서 자주 쓰이는 방식인데요, 이미지를 프로젝트 안에서 import 해서 쓸 때 유용합니다.
import responsiveImg from '../public/images/responsive-image.jpg';
export default function MyComponent() {
return (
<img
src={responsiveImg.src}
srcSet={`${responsiveImg.src} 1x, ${responsiveImg.src.replace('.jpg', '@2x.jpg')} 2x`}
sizes="(max-width: 600px) 100vw, 50vw"
style={{ width: '100%', height: 'auto' }}
alt="Responsive example"
/>
);
}
여기서 sizes 속성은 뷰포트 너비가 600px 이하일 때 이미지가 화면 너비(100vw)의 100%를 차지하고, 그 이상일 땐 50%를 차지하도록 지정하는 거예요. 이렇게 하면 작은 화면에선 이미지가 꽉 차 보이고, 큰 화면에선 절반 크기로 나란히 배치하기 좋아요.
그리고 style이나 className으로 width: 100%
를 주면 이미지가 부모 컨테이너의 크기에 맞게 늘어나거나 줄어듭니다. height는 auto로 하면 비율이 유지돼서 이미지가 찌그러지지 않죠.
팁!
- 이미지를 더 다양하게 반응형으로 다루고 싶으면 picture 태그와 함께 여러 해상도, 형식(webp, jpeg 등)을 srcSet에 넣어주는 것도 좋은 방법이에요.
- Next.js를 쓰는 분들이라면 내장된 Image 컴포넌트를 활용하면 알아서 최적화 해주니 더 쉽게 반응형 이미지를 만들 수 있습니다.
이제 images 태그만 써도 다양한 기기에서 딱 예쁘게 보여줄 수 있으니 꼭 sizes값과 css 조합도 함께 고려해보세요!
만약 이미지가 정적(변하지 않는) 소스라면, 이렇게 statically import해서 반응형 이미지(Responsive Image)를 쉽게 만들 수 있어요:
import Image from 'next/image'
import me from '../photos/me.jpg'
export default function Author() {
return (
<Image
src={me}
alt="Picture of the author"
sizes="100vw"
style={{
width: '100%',
height: 'auto',
}}
/>
)
}
이 코드에서 중요한 점은 sizes="100vw"
예요. 이 설정 덕분에 이미지가 뷰포트(Viewport) 너비 100%에 맞춰서 크기가 조절돼요. 그리고 style
로 width: '100%'
그리고 height: 'auto'
를 줘서 이미지 비율을 유지하며 폭에 맞게 크기가 변하게 됩니다.
왜 이렇게 하는 걸까?
- 정적 import는 번들링할 때 이미지 정보를 미리 알아서, 최적화된 사이즈를 자동으로 만들어줘서 페이지 로딩 속도가 빨라져요.
next/image
컴포넌트 자체가 여러 크기의 이미지를 제공해주고, 뷰포트에 따라 적절한 사이즈의 이미지를 불러오기 때문에 데이터 사용량도 줄일 수 있답니다.sizes
속성은 반응형 이미지에서 어떤 폭에서 이미지를 얼마나 크게 보여줄 지 브라우저에 알려주는 역할을 해요.
한번 해보세요!
위 코드를 페이지에 넣고 브라우저 창 크기를 조절해서 이미지가 폭에 맞춰 잘 변하는지 확인해 보면 정말 반응형 이미지임을 체감할 수 있을 거예요.
만약 이미지를 동적으로 불러야 한다면, 다음 시간에 그 방법도 알려드릴게요!
반응형 이미지에 비율 맞추기
웹에서 이미지를 쓸 때 특히 원본 이미지가 동적이거나 외부 URL일 경우, 반응형으로 잘 보여주려면 이미지의 가로(width)와 세로(height)를 꼭 지정해줘야 해요. 그래야 이미지가 비율을 유지하면서 화면 크기에 맞게 조절되거든요.
Next.js의 next/image
컴포넌트를 예로 들어볼게요.
import Image from 'next/image'
export default function Page({ photoUrl }) {
return (
<Image
src={photoUrl} // 동적 혹은 외부 주소로 이미지 호출
alt="Picture of the author"
sizes="100vw" // 화면 전체 폭에 맞춰서 크기를 조절
style={{
width: '100%', // 가로는 부모 컨테이너에 딱 맞게
height: 'auto', // 세로는 비율에 맞게 자동 조절
}}
width={500} // 이미지의 원본 가로 크기
height={300} // 이미지의 원본 세로 크기
/>
)
}
여기서 width={500}
과 height={300}
은 원본 이미지의 가로, 세로 크기를 나타내고, style
에서 width: 100%
로 설정해주면 화면 크기에 딱 맞춰서 이미지 크기가 바뀌면서도 가로세로 비율이 깨지지 않아요.
실제로
next/image
는width
,height
정보가 있어야 이미지 사이즈 계산과 레이아웃 잡는 데 도움을 줘서 CLS(Cumulative Layout Shift) 같은 레이아웃 깨짐 문제도 예방할 수 있답니다.
추가로, 반응형 이미지를 만들 때 기억하면 좋은 점!
팁 | 설명 |
---|---|
sizes 속성 | 뷰포트(viewport) 크기에 따라 어떤 크기의 이미지를 로딩할지 브라우저에 알려줍니다. 100vw 는 화면 전체 너비를 뜻해요. |
style | CSS로 가로 세로 비율을 조절하지만 원본 비율과 다르면 이미지가 찌그러질 수 있어 비율 맞추는 게 중요해요. |
원본 크기 지정 | width 와 height 를 지정해야 브라우저가 최적의 이미지를 불러오고 레이아웃이 안정적이에요! |
필요하다면, CSS로 부모 요소에 max-width
를 정해서 너무 커지지 않도록 제한하는 것도 좋아요.
오늘은 이렇게 동적 이미지로 반응형 이미지를 깔끔하게 다루는 방법을 알아봤어요. 실무에서 꽤 자주 쓰이니 한번 써보시면서 감 잡아보세요!
뷰포트에 반응하는 이미지 데모
이미지의 가로세로 비율(aspect ratio)을 모를 때 - fill
속성 활용법
이미지를 반응형으로 만들고 싶은데, 이미지의 정확한 가로세로 비율을 모를 경우가 있죠? 그럴 땐 부모 요소에 position: relative
를 주고, Next.js의 Image
컴포넌트에 fill
속성을 사용하면 꽉 차게 이미지가 들어가요. 이때 object-fit
스타일로 이미지가 어떻게 맞춰질지 정할 수 있는데, 'contain'
으로 하면 이미지가 찌그러지거나 잘리지 않고 꽉 차도록 조절해 줍니다.
아래 예시 코드를 보시면 이해가 빠르실 거에요:
import Image from 'next/image'
export default function Page({ photoUrl }) {
return (
<div style={{ position: 'relative', width: '300px', height: '500px' }}>
<Image
src={photoUrl}
alt="Picture of the author"
sizes="300px"
fill
style={{
objectFit: 'contain',
}}
/>
</div>
)
}
div
에position: relative
를 줘야fill
로 설정된 이미지가 부모 영역을 기준으로 꽉 채울 수 있어요.width
와height
를 고정 또는 반응형 단위로 지정해주면 이미지 크기가 결정되고, 그 안에서fill
이 동작하는 거죠.objectFit
을'contain'
으로 하면 이미지가 영역 내에 꽉 차면서 비율이 유지되고,'cover'
로 설정하면 영역을 가득 채우되 일부 잘릴 수도 있습니다.
추가 팁!
만약 이미지가 부모 크기에 맞춰 유연하게 크기가 변하길 원한다면, 부모 요소를 미디어 쿼리나 CSS 그리드/플렉스를 활용해 동적으로 크기를 조절하는 방법도 있어요. 그러면fill
과 함께 더 자연스러운 반응형 이미지를 구현할 수 있습니다!
한번 해볼까요?
- fill prop을 활용하는 데모
테마 감지 CSS
웹사이트를 만들 때, 라이트 모드와 다크 모드에 따라 다른 이미지를 보여주고 싶을 때가 있죠? 이럴 때는 두 개의 Image
컴포넌트를 감싸는 새로운 컴포넌트를 만들어서 CSS 미디어 쿼리를 활용해 적절한 이미지만 보여주도록 할 수 있어요.
예를 들어, CSS의 prefers-color-scheme
미디어 쿼리를 사용하면 사용자가 설정한 테마(라이트 or 다크)에 따라 다른 스타일이나 이미지를 적용할 수 있답니다. 이를 이용하면 자바스크립트로 복잡한 조건문을 작성하지 않아도, 아주 깔끔하게 테마별 이미지를 처리할 수 있어요.
아래는 이 개념을 활용한 간단한 예시입니다.
import Image from 'next/image';
const ThemeImage = () => {
return (
<>
<Image
src="/images/light-mode.png"
alt="라이트 모드 이미지"
className="light-image"
width={200}
height={200}
/>
<Image
src="/images/dark-mode.png"
alt="다크 모드 이미지"
className="dark-image"
width={200}
height={200}
/>
<style jsx>{`
.light-image {
display: block;
}
.dark-image {
display: none;
}
@media (prefers-color-scheme: dark) {
.light-image {
display: none;
}
.dark-image {
display: block;
}
}
`}</style>
</>
);
};
export default ThemeImage;
위 코드에서는 기본적으로 라이트 모드 이미지를 보여주고, 다크 모드가 감지되면 다크 모드 이미지만 보이도록 CSS로 조절해줍니다.
추가로 팁 하나!
만약 이미지가 너무 많아서 이렇게 일일이 컴포넌트를 만들기 번거롭다면, picture 태그와 source
태그를 활용하는 방법도 있어요. source
의 media
속성에 prefers-color-scheme
조건을 넣으면 브라우저가 자동으로 맞는 이미지를 골라줍니다.
<picture>
<source srcSet="/images/dark-mode.png" media="(prefers-color-scheme: dark)" />
<img src="/images/light-mode.png" alt="테마별 이미지" width={200} height={200} />
</picture>
이렇게 하면 자바스크립트나 추가 CSS 없이도 손쉽게 테마별 이미지를 관리할 수 있어서 더 깔끔하니 참고해보세요!
이번 시간에는 다크모드 지원을 할 때 이미지도 알아서 라이트모드용, 다크모드용 이미지를 잘 보여주게 하는 간단한 방법을 소개해 드릴게요. CSS와 Next.js를 함께 활용한 스타일링과 컴포넌트 설계법을 보면서, 실제로 어떻게 이미지 로딩이 최적화되는지도 짚어보려 합니다.
1. CSS로 다크모드에 따라 보이기/숨기기 설정하기
.imgDark {
display: none;
}
@media (prefers-color-scheme: dark) {
.imgLight {
display: none;
}
.imgDark {
display: unset;
}
}
.imgDark
이미지는 기본적으로 숨겨져 있어요.- 사용자의 OS나 브라우저가 다크모드를 감지하면(
prefers-color-scheme: dark
),.imgLight
이미지는 숨기고.imgDark
이미지는 보여주게 됩니다.
이렇게만 해줘도 다크모드 감지 시 자동으로 이미지를 바꿔서 보여줄 수 있죠.
2. Next.js 컴포넌트로 깔끔하게 관리하기
import styles from './theme-image.module.css'
import Image, { ImageProps } from 'next/image'
type Props = Omit<ImageProps, 'src' | 'priority' | 'loading'> & {
srcLight: string
srcDark: string
}
const ThemeImage = (props: Props) => {
const { srcLight, srcDark, ...rest } = props
return (
<>
<Image {...rest} src={srcLight} className={styles.imgLight} />
<Image {...rest} src={srcDark} className={styles.imgDark} />
</>
)
}
- Next.js의
Image
컴포넌트를 한 번에 두 개 넣고, CSS로 어느 이미지를 보여줄지 제어하는 방식입니다. Priority
나loading="eager"
를 쓰면 이미지가 모두 로드되어 버려서 비효율적이므로 기본loading="lazy"
속성을 이용하거나 **fetchPriority="high"
**를 활용하세요.
3. 핵심 포인트: 이미지 로딩 최적화
고려사항 | 설명 |
---|---|
기본 로딩 방식 | loading="lazy" 는 스크롤 등 사용자 행동에 맞게 이미지를 불러옵니다. 이 덕분에 안 보이는 이미지는 바로 로드되지 않아요. |
priority 속성 사용 제한 | 라이트/다크용 이미지가 동시에 로드되기 때문에 priority 를 쓰면 번거로워질 수 있습니다. |
fetchPriority 활용 | Next.js 13부터 새로 도입된 fetchPriority="high" 를 이용하면 우선순위 지정이 가능하지만, 옮겨서 써야 하는 상황이라는 점 참고! |
간단 요약
- CSS 미디어 쿼리로 라이트/다크 이미지를 제어하자.
next/image
를 두 개 겹쳐서 쓰되, 실제 보여줄 이미지만 노출되도록 CSS로 관리하자.- 불필요한 이미지 로딩을 막기 위해 기본
loading="lazy"
를 사용하자. 필요할 땐fetchPriority="high"
도 써보자.
덧붙여서 알려드리는 팁!
- 만약 이미지가 너무 많거나 용량이 크다면, 라이트/다크 모드 전환 시 자바스크립트로 이미지를 동적으로 교체하는 방법도 고려할 수 있습니다.
- 또 CSS
image-set
같은 기능도 사용해볼 수 있지만, 브라우저 지원이 조금씩 다르니 꼭 테스트하세요!
다크모드 시대에 잘 맞춰 사용자 경험을 선물해주는 테마별 이미지 처리, 어렵지 않지요? 직접 코드를 돌려보면서 이미지 교체가 매끄럽게 되는 걸 느껴보시면 이해가 훨씬 쉬워집니다.
그럼 즐거운 개발 하세요! 🚀
- 데모: 라이트/다크 모드 테마 감지
getImageProps 함수
조금 더 고급 활용법을 원할 때는 getImageProps() 함수를 호출해서, 실제 img
태그에 들어갈 props들을 받아올 수 있어요. 그리고 이 props들을 다른 컴포넌트나 스타일, 혹은 캔버스 등에 넘겨서 자유롭게 사용할 수 있죠.
이 방법의 좋은 점은 React의 useState()를 사용하지 않기 때문에 퍼포먼스가 좀 더 좋아질 수 있다는 거예요. (useState를 쓰면 상태 변경으로 리렌더링이 발생할 수 있거든요.) 하지만 여기서 주의할 점은 placeholder prop과 함께 사용할 수 없다는 겁니다! 왜냐하면 placeholder가 한 번 설정되면 절대 사라지지 않거든요.
즉, 이미지 로딩 상태를 직접 관리하거나, 커스텀한 방식으로 이미지를 표시하고 싶을 때 이 함수를 쓰면 꽤 유용하겠죠? 다만, 나중에 placeholder 효과를 구현하고 싶다면 다른 방법을 고민해야 합니다.
테마 감지용 이미지 바꾸기
웹사이트를 만들 때, 라이트 모드와 다크 모드에 따라 다른 이미지를 보여주고 싶을 때가 있죠? 이럴 때 유용한 방법이 바로 picture
태그를 활용하는 거예요. 사용자 브라우저가 선호하는 색상 모드(라이트/다크)를 감지해서 그에 맞는 이미지를 자동으로 보여줄 수 있답니다.
아래는 Next.js에서 next/image
의 getImageProps
를 활용해, 라이트/다크 모드별로 서로 다른 이미지를 설정하는 예시 코드입니다.
import { getImageProps } from 'next/image'
export default function Page() {
const common = { alt: 'Theme Example', width: 800, height: 400 }
const {
props: { srcSet: dark },
} = getImageProps({ ...common, src: '/dark.png' })
const {
props: { srcSet: light, ...rest },
} = getImageProps({ ...common, src: '/light.png' })
return (
<picture>
<source media="(prefers-color-scheme: dark)" srcSet={dark} />
<source media="(prefers-color-scheme: light)" srcSet={light} />
<img {...rest} />
</picture>
)
}
여기서 알아두면 좋은 점!
picture
태그: 여러 이미지를 상황에 맞게 골라 보여주기 위해 쓰여요.<source>
에 조건(미디어쿼리)을 걸고, 마지막<img>
태그가 기본 이미지 역할을 해주죠.prefers-color-scheme
미디어 쿼리: 사용자 기기의 모드 설정을 감지해dark
나light
여부를 판단해요.- Next.js의
getImageProps
: 이미지를 최적화하고 메타 정보를 제공해서, 별도의 수동 작업 없이도 쉽게 이미지 관련 속성을 얻을 수 있어요.
추가 팁!
-
이미지 파일을 많이 만드는 게 번거롭다면, CSS 필터(예:
filter: invert(1)
)를 활용해 이미지 색상을 조절하는 방법도 있어요. 단, 복잡한 이미지엔 적용하기 어려울 수 있답니다. -
다크 모드 이미지를 SVG로 만들면 색상 변경도 더 자유롭고, 용량도 줄일 수 있으니 참고하세요.
-
picture
태그가 구형 브라우저에서는 지원되지 않을 수 있으니, 꼭 호환성 체크도 해보시면서 사용하시길 추천드립니다.
다음에는 ‘아트 디렉션’(Art Direction)을 위해서 어떻게 이미지 크기나 구성을 바꾸는지에 대해 다뤄볼게요! 디자인 변화에 따라 이미지를 컨트롤하는 재미난 방법들이 많이 있답니다. 기대해주세요!
데스크탑과 모바일에서 각각 다른 이미지를 보여주고 싶을 때, 흔히 'Art Direction(아트 디렉션)'이라고 불러요.
이럴 때는 getImageProps()
함수에 각 기기별로 다른 src
, width
, height
, quality
값을 넘겨줘서 손쉽게 처리할 수 있답니다.
아래 예제를 보면 조금 더 이해하기 쉬울 거예요:
import { getImageProps } from 'next/image'
export default function Home() {
const common = { alt: 'Art Direction Example', sizes: '100vw' }
// 데스크탑용 이미지 속성 가져오기
const {
props: { srcSet: desktop },
} = getImageProps({
...common,
width: 1440,
height: 875,
quality: 80,
src: '/desktop.jpg',
})
// 모바일용 이미지 속성 가져오기
const {
props: { srcSet: mobile, ...rest },
} = getImageProps({
...common,
width: 750,
height: 1334,
quality: 70,
src: '/mobile.jpg',
})
return (
<picture>
<source media="(min-width: 1000px)" srcSet={desktop} />
<source media="(min-width: 500px)" srcSet={mobile} />
<img {...rest} style={{ width: '100%', height: 'auto' }} />
</picture>
)
}
<picture>
태그와 <source>
를 사용해서 화면 크기에 맞는 이미지를 선택하도록 해주는데요, 브라우저가 조건에 맞는 첫 번째 <source>
를 찾아 이미지를 보여주고, 맞는 게 없으면 <img>
태그에 있는 이미지가 기본으로 나옵니다.
그리고 여기서 getImageProps()
로 반환받은 srcSet
은 여러 해상도 이미지를 브라우저가 자동으로 선택하게 도와줘서, 고해상도 기기에서도 선명한 이미지를 볼 수 있습니다.
CSS 배경 이미지에 활용하기
사실 이미지를 태그로만 쓰는 게 아니라, CSS background-image
로도 활용할 수 있어요.
이때 srcSet
문자열을 CSS의 image-set()
함수로 변환해서 반응형 배경 이미지를 만들 수 있답니다.
예를 들어, 이렇게 쓸 수 있어요:
background-image: image-set(
url('/image-1x.png') 1x,
url('/image-2x.png') 2x
);
image-set()
함수는 해상도에 따라 다른 이미지를 로드하도록 도와주는데, 그렇게 하면 모바일과 데스크탑의 배경 이미지를 각각 최적화할 수 있어요.
이렇게 활용해보세요!
- 이미지 용량과 해상도를 따로 조절해서 모바일에서는 가볍고, 데스크탑에서는 선명한 이미지를 제공할 수 있어요.
<picture>
태그를 활용하면 SEO와 접근성에도 좋아요.<img>
의alt
속성을 꼭 챙기는 것도 잊지 말고요!- CSS
image-set()
으로 배경 이미지에 반응형을 적용해보면 디자인 퀄리티가 더 좋아질 수 있답니다.
이미지 아트 디렉션, 처음에는 조금 복잡해 보여도 차근차근 손에 익으면서 효과를 톡톡히 볼 수 있을 거예요.
필요하면 제가 배경 이미지용 image-set()
변환법도 예제로 한번 더 알려드릴게요!
JS와 Next.js의 next/image 컴포넌트를 활용해서 배경 이미지를 다루는 예제와, 관련한 브라우저 호환 이슈를 알아볼게요.
배경 이미지로 next/image srcSet 활용하기
import { getImageProps } from 'next/image';
function getBackgroundImage(srcSet = '') {
const imageSet = srcSet
.split(', ')
.map((str) => {
const [url, dpi] = str.split(' ');
return `url("${url}") ${dpi}`;
})
.join(', ');
return `image-set(${imageSet})`;
}
export default function Home() {
const {
props: { srcSet },
} = getImageProps({ alt: '', width: 128, height: 128, src: '/img.png' });
const backgroundImage = getBackgroundImage(srcSet);
const style = { height: '100vh', width: '100vw', backgroundImage };
return (
<main style={style}>
<h1>Hello World</h1>
</main>
);
}
설명을 덧붙이자면
getImageProps
는 next/image가 이미지 로딩에 최적화된 속성들(srcSet
,src
,sizes
등)을 생성해주는 함수입니다.- 여기서는
srcSet
문자열을 받아서 CSSimage-set()
구문에 맞게 변환해줍니다. image-set
은 여러 해상도에 맞는 이미지들을 선언해서 브라우저가 상황에 맞게 선택적으로 로딩할 수 있게 합니다.- 이걸 스타일 객체에 바로
backgroundImage
로 넣어서 배경으로 활용하는 거죠.
작은 팁!
- CSS
image-set()
지원은 아직 일부 브라우저에서 제한적이라 실제로 쓸 땐 호환성 체크 꼭 하세요. - 여기선 inline style로 썼지만, css-in-js나 external css로 작성하면 유지보수가 더 좋을 수 있어요.
- next/image 기본 컴포넌트는
<img>
태그에 최적화되어 있기 때문에 배경 이미지로 쓰긴 다소 변칙적이라, 상황에 따라 직접 이미지 태그를 쓰는 것도 고민해보세요.
알려진 브라우저 버그 및 주의점
브라우저 | 이슈 내용 | 해결법 / 참고 |
---|---|---|
Safari 15 ~ 16.3 | 이미지 로딩 시 회색 테두리 표시 | - CSS @supports로 특정 스타일 적용 - priority 속성으로 위쪽 영역 이미지 우선 로딩 |
Safari < 15.4 | native lazy loading 미지원 (eager fallback) | 최신 버전으로 업데이트 권장 |
Safari < 12 | blur-up placeholder 빈 상태 fallback | blur-up 효과 대신 다른 placeholder 사용 고려 |
Safari < 15 | width/height auto 스타일 사용 시 레이아웃 시프트 발생 가능 | 고정 크기 사용 추천 |
Firefox 67+ | 로딩 중 흰색 배경 표시 | - AVIF 포맷 활성화 - placeholder 사용 |
알아두면 좋은 점
- Next.js의 next/image는 기본적으로 lazy loading을 지원하지만, 브라우저마다 native 지원 범위가 다릅니다.
- 특히 구형 브라우저에서는 eager로 강제 변경되거나 placeholder가 제대로 보이지 않는 등 UX가 달라질 수 있어요.
- blur-up placeholder 효과가 안되는 구형 사파리는 사용자 눈에 빈 화면이 되는 단점이 있습니다.
- Firefox에서 흰색 배경 문제는 AVIF 사용 및 placeholder 설정으로 완화할 수 있으니 가능하면 이미지를 AVIF 포맷으로 변환하는 게 좋아요.
- Layout Shift (레이아웃 변화)를 막으려면 기본적으로 이미지 영역의 크기를 미리 지정해주는 게 중요합니다.
마무리
next/image는 편리한 이미지 최적화 도구지만, 브라우저 별로 미묘한 차이가 있어서 꼼꼼한 테스트가 필요해요.
특히 배경 이미지로 변형하거나 레거시 브라우저를 지원한다면, 위 이슈들을 참고해서 적절히 대응하세요!
더 궁금한 점 있으면 언제든 질문해 주세요~
개발할 때 자주 쓰는 팁도 하나씩 알려 드릴게요 :)
버전 히스토리
버전 | 변경사항 |
---|---|
v15.3.0 | remotePatterns 에서 URL 객체 배열을 지원하도록 추가됨. |
v15.0.0 | contentDispositionType 설정 기본값이 attachment 으로 변경됨. |
v14.2.23 | qualities 설정이 추가됨. |
v14.2.15 | decoding prop과 localPatterns 설정이 추가됨. |
v14.2.14 | remotePatterns.search prop이 추가됨. |
v14.2.0 | overrideSrc prop이 추가됨. |
v14.1.0 | getImageProps() 가 안정화됨. |
v14.0.0 | onLoadingComplete prop과 domains 설정이 deprecated됨. |
v13.4.14 | placeholder prop이 data:/image... URI를 지원하도록 추가됨. |
v13.2.0 | contentDispositionType 설정이 추가됨. |
v13.0.6 | ref prop이 추가됨. |
v13.0.0 | - next/image 임포트가 next/legacy/image 로 이름 변경됨.- next/future/image 임포트가 next/image 로 이름 변경됨.- 안전하고 자동으로 임포트를 변경해주는 codemod 제공. - <span> 래퍼 제거.- layout , objectFit , objectPosition , lazyBoundary , lazyRoot prop 삭제.- alt prop 필수화.- onLoadingComplete 이 img 엘리먼트 레퍼런스를 받음.- 기본 내장 로더 설정 제거됨. |
v12.3.0 | remotePatterns 및 unoptimized 설정이 안정화됨. |
v12.2.0 | 실험적 remotePatterns 와 unoptimized 설정 추가.layout="raw" 삭제됨. |
v12.1.1 | style prop 추가.실험적 layout="raw" 지원 추가. |
v12.1.0 | dangerouslyAllowSVG 와 contentSecurityPolicy 설정 추가됨. |
v12.0.9 | lazyRoot prop 추가됨. |
v12.0.0 | - formats 설정 추가.- AVIF 이미지 포맷 지원 추가. - 래퍼 엘리먼트가 <div> 에서 <span> 으로 변경됨. |
v11.1.0 | onLoadingComplete 과 lazyBoundary prop 추가됨. |
v11.0.0 | - src prop이 정적 임포트를 지원.- placeholder prop 추가.- blurDataURL prop 추가. |
v10.0.5 | loader prop 추가됨. |
v10.0.1 | layout prop 추가됨. |
v10.0.0 | next/image 컴포넌트 도입됨. |
이렇게 버전별로 주요 업데이트가 정리되어 있으니, 특히 v13
에서 큰 변화가 있었던 점 잊지 마세요! alt
가 필수로 변경된 점이라든지 next/image
의 경로가 달라진 부분은 프로젝트 마이그레이션 시 꼭 체크하세요. 그리고 최신 버전에서는 AVIF 포맷 지원 등 이미지 최적화 기능도 점점 좋아지고 있어서, 가능하면 최신 버전을 쓰는 게 좋습니다.
필요하면 각 버전에 있는 새로운 prop이나 설정을 활용해 성능과 사용자 경험을 개선해보시길 추천합니다!