Image 컴포넌트는 내부적으로 어떻게 최적화를 수행하는가

보통 프론트엔드에서 이미지는 로컬에 저장해서 사용하거나 S3에 올린 후 <img /> 를 이용해 보여주는 것이 대부분이다. 그러나 Nextjs에서는 내부적으로 Image 컴포넌트를 제공하는데 내부적으로 어떻게 최적화를 수행하고 있는지 알아보자.

공식문서: https://nextjs.org/docs/app/api-reference/components/image

Next/Image에서 제공하는 기능

Nextjs에서 기본적으로 제공하는 기능은 아래와 같습니다.

Nextjs에서는 내부적으로 어떻게 최적화를 수행하는가

<Image />를 이용하여 브라우저에 렌더링했을 때 개발자도구로 확인해보면 아래와 같이 렌더링 되는 것을 확인할 수 있다.

next-image-1.png

_next/image path 뒤에 쿼리스트링으로 width와 quality 값을 전달하는 것을 확인할 수 있다. 이 src가 변하는 이유는 Next/image 패키지 내부에서 loader 속성에 의해서 이미지 최적화를 위한 라우트를 만들고, 내부에서 이미지 최적화 로직을 수행한다.

function defaultLoader({
	// ... 생략
	if (src.endsWith(".svg") && !config.dangerouslyAllowSVG) {
		return src
	}

	return `${normalizePathTrailingSlash(config.path)}?url...생략`
})

Nextjs 서버는 동작 시, /_next/image 라는 이미지 최적화를 위한 라우트를 만들고, 내부에서 이미지 최적화 모듈을 사용하여 이미지를 최적화한다. w는 우리가 지정한 이미지 너비에 따라 next.config.ts 파일 내 지정한 브레이크 포인트에 맞춘 값을 사용하고 qnext/image 컴포넌트의 quality props에 기인하는데, 현재 값을 주지 않아서 기본값인 75가 사용된 모습이다.

srcset이란, 이미지 태그에서 사용하는 속성으로, 브라우저가 다양한 화면 크기와 해상도에 맞는 최적화된 이미지를 자동으로 선택하도록 도와주는 속성이다. 원래라면 개발자가 직접 이미지를 다양한 화면 크기에 대응하여 추가해야하지만 Nextjs에서 자동으로 생성하여 브라우저가 다양한 해상도와 화면 크기에 맞는 이미지를 선택할 수 있도록 한다.

이미지 캐싱

Nextjs는 요청이 들어왔을 때, .next/cache/image 하위에 최적화한 이미지를 동적으로 만들고, 이후에 동일한 요청에 대해서는 이미 만들어놓은 최적화한 이미지를 캐시로서 재사용한다.

다만 Nextjs가 모든 이미지를 최적화하는 것은 아니다. 동적으로 최적화를 해야하므로 이미지 최적화가 필요없는 SVG와 같은 Vector 이미지, GIF와 같은 상대적으로 복잡하고 최적화에 오래걸리는 애니메이션 이미지의 경우에는 최적화를 수행하지 않고 그대로 응답한다.

참고
https://deun.dev/post/nextjs-image-optimization