
이 글은 개념적 내용(이미지 최적화 in Next.js)과
테스트해본 내용(실전 테스트 in Next.js)으로 구분된다.
웹 개발에서 이미지 최적화는 사용자 경험을 좌우하는 핵심 요소다.
Next.js에서 제공하는 Image 컴포넌트를 이용하여 최적화해보려 하는데, 단순히 <img> 태그를 <Image> 컴포넌트로 바꾸는 것만으로 끝내서는 안 될 것 같아서 깊게 고민해보고 정리한 내용이다.
Image 컴포넌트
Image컴포넌트를 왜 사용하는거지?
lazy loading 기본제공을 통해 보이는 이미지만 load한다.CLS(Cumulative Layout Shift) 방지를 위한 placeholder 제공을 통해, 이미지 로드 전에도 레이아웃이 흔들리지 않도록 해준다.Image 컴포넌트 필수 props예시를 통해 사용법을 확인해보자
import Image from 'next/image';
export default function ImageTest() {
return (
<>
<Image
src="/images/test/Monet_WomanWithAParasol.jpg"
alt="테스트01"
width={600}
height={200}
/>
</>
);
}
src 이미지 주소 필수alt 이미지 대체 텍스트 필수width height : 픽셀단위 이미지 크기설정 필수Image 컴포넌트 추가 propsimport 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"
fill
alt="Picture of the author"
width={500}
height={500}
quality={80}
loading="lazy"
/>
)
}
fill 부모 요소 크기에 맞춰 이미지를 확장하며, 반응형 디자인을 위해 사용한다. ⭐⭐⭐loader 이미지 url을 생성하는 함수로, src, width, quality 매개변수를 받아서 최적화된 url을 생성한다. 외부이미지를 사용할 때 적절해보인다.quality 1과100사이의 값을 통해 랜더링될 때의 이미지 화질을 조절할 수 있다.loading 이미지의 로딩방식을 결정할 수 있다.placeholder 이미지 로드되는동안 보여질 임시 이미지정적 이미지가 아니라면, blurDataURL props 필수 ⭐⭐⭐⭐⭐onLoad onError 등 추가적인 props에 대한 정보는 공식문서를 참고해보자.Next.js에서 이미지를 저장할 위치는 public 혹은 src 중에서 고민해 볼 수 있다.
물론, local image가 아닌 remote image를 활용할 수 도 있다.
src
import testImage from '../assets/test.jpg';
<Image src={testImage} alt="Hero" placeholder="blur" />
public
<Image src="/images/hero.jpg" alt="Hero" width={1920} height={1080} />
차이가 뭔데!
sizes and srcset propssizes와 srcset은 화면에 보여질 이미지 크기를 결정하는게 아니다. 이미지파일의 사이즈(용량/해상도)선택을 위한거다. 화면에 보여질 크기는 width와 heigth가 결정한다.
모바일 사용자와 PC사용자에게 동일한 크기(용량)의 이미지를 랜더링 시킨다면, 데이터 낭비가 발생하겠지? 사용자의 브라우저 크기에 따라 다른 버전의 이미지를 보여주도록 해주는게
srcset이다.
예를들어 모바일크기의 화면에서는 2MB 이미지 대신 500KB 이미지를 다운로드 시키도록 한다.
예시 코드 : srcset 를 명시한 코드
<img
src="image-400.jpg"
srcset="
image-400.jpg 400w,
image-800.jpg 800w,
image-1200.jpg 1200w
"
alt="이미지"
>
예시코드 2 : Image 컴포넌트는 내부적으로 srcset를 자동 생성해준다.
<Image
src="/hero.jpg"
width={800}
height={400}
sizes="(max-width: 768px) 100vw, 50vw"
alt="Hero Image"
/>
sizes는 브라우저에게 이미지가 실제로 표시될 크기를 알려주는 힌트의 역할이다. 즉, 브라우저가 srcset에서 적절한 이미지를 선택할 수 있도록 한다.sizes 문법
**sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"**
sizes="(max-width: 768px) 100vw, 800px"
loaderFile과 remotePatterns 설정dangerouslyAllowSVG와 contentSecurityPolicy 설정imageSizes 설정npm run build npm run start16mb 3.58mb 700kb 3가지 이미지로 테스트 진행disable cache slow 4gimport Image from 'next/image';
export default function ImageTest() {
return (
<>
<div className="text-4xl p-8 text-center font-paperlogy">Test 01 : img태그와 Image컴포넌트 비교</div>
<hr />
<div className="w-full justify-center flex gap-8 ">
<div>
<LabelTag text="Image컴포넌트 (700kb)" />
<Image src="/images/test/Monet_LuncheonOnTheGrass.jpg" alt="t-동적이미지-700kb" width={300} height={200} />
<LabelTag text="Image컴포넌트 (3mb)" />
<Image src="/images/test/Monet_ImpressionSunrise.jpg" alt="t-동적이미지-3mb" width={300} height={200} />
</div>
<div>
<LabelTag text="img태그 (700kb)" />
<img src="/images/test/Monet_LuncheonOnTheGrass.jpg" alt="t-동적이미지-700kb" width={300} height={200} />
<LabelTag text="img태그 (3mb)" />
<img src="/images/test/Monet_ImpressionSunrise.jpg" alt="t-동적이미지-3mb" width={300} height={200} />
</div>
</div>
</>
);
}
const LabelTag = ({ text }: { text: string }) => {
return <div className="mt-4 mb-2 bg-no2 rounded-lg p-0.5 text-xl font-paperlogy text-center">{text}</div>;
};


예상했던 결과
- Time(다운로드시간) : Image컴포넌트이 빠름
- Image 컴포넌트는 웹에 최적화된 형식인
jpg ⇒ webp로 변환시킨다.- Image 컴포넌트는 용량 최적화 시킨다.
3mb ⇒ 8kb700kb => 36kb- img태그의 Path는 public 폴더의 정적파일에 직접접근한다.
/images/test/Monet_LuncheonOn...- Image 컴포넌트의 Path는
/_next/image: Next.js의 내장 이미지 최적화 API 엔드포인트
- Next.js의 최적화 서버를 거친 뒤에 접근하며 Path가 부여된다.
결론 : 여러가지 요인들을 예상 해볼 수 있지만 명확한 원인을 찾지는 못했다.
jpg => jpeg 로 변환되었다.jpeg 파일은 로컬환경에서 .jpg 로 보여진다.jpeg 파일은 인코딩방식에 따라 두가지로 구분된다.결론 : 즉, 700kb jpg 이미지는 Progressive jpeg 이미지 파일이어서 화질낮은 상태로 먼저 렌더링 된 뒤 화질이 높아지는 방식이다. 3mb 이미지는 Baseline jpeg 이미지 파일이라서 쪼금씩 상단부터 렌더링되는 것이다.
Nextjs 환경에서 이미지를 사용한다면, 꼭 최적화에 대해 공부해보길 바란다. 이번 공부를 통해 정적이미지와 동적이미지의 차이에 대해 이해했으며, Image 컴포넌트의 사용법에 대해서도 익힐 수 있었다. 특히 다양한 device에서 접근 가능한 웹페이지의 특성상 device별로 최적화된 이미지를 제공할 수 있는 방법에 대한 고민이 필요했었다. 그 방법에 대한 힌트를 sizes, srcset으로부터 어느정도 얻을 수 있었던것 같다. 추후에 진행할 최적화작업에서 한번더 깊게 고민해 볼 예정이며, 오늘은 여기까지 할란다.