Next/Image로 이미지 최적화 적용하기
- Next 13 버전의 Image 컴포넌트에 대해 작성한 글입니다.
얼마전에 이미지 최적화를, 클라우드에서 필요한 크기만큼의 이미지를 불러오고 webp
포맷으로 변경하는 방식으로 진행했었습니다. 그런데, 사실 Next 환경에서는 Image
컴포넌트를 통해 이전 글의 과정 없이 쉬운 최적화가 가능합니다.
기본적인 최적화 파악하기
( Ref : Image Component and Image OptimizationExamples )
리모트 이미지를 다룰 때에 일반적으로 다음과 같이 사용합니다.
// next/Image <Image src={imgUrl} alt='Forest' width={300} height={300} />
아무런 속성 없이 Image
를 사용하면 width, height
를 필수로 입력해야 합니다. 그러면 Next
는 기본적인 최적화를 진행해 다음과 같은 이미지를 출력합니다.
그냥 img
태그를 이용해 렌더링 할 때에는, 위와 같이 5.4MB
의 6000x4000
이미지를 불러옵니다.
그렇지만 아무런 설정값이 없어도, Next이미지는 이미지의 포맷을 webp
로 불러오고 이미지의 크기를 축소시켜 렌더링해줍니다. 5.4MB -> 79.7kB
의 차이면 어마어마한 차이라고 생각합니다.
어떻게 최적화 하는지 파악하기
그런데 이미지의 최적화는 어떻게 진행된걸까요? Next
로 렌더링된 태그를 확인해보면 다음과 같습니다.
<img alt='Forest' srcset='/_next/image?url={이미지 url}&w=384&q=75 1x, /_next/image?url={이미지 url}&w=640&q=75 2x' src='/_next/image?url={이미지 url}&w=640&q=75' width='300' height='300' decoding='async' data-nimg='1' loading='lazy' style='color:transparent' ></img>;
img
의 프로퍼티에 여러가지 속성들이 추가되어 있습니다. 이 부분을 먼저 파악해 봅시다.
srcset과 sizes
<img srcset="elva-fairy-320w.jpg 320w, elva-fairy-480w.jpg 480w, elva-fairy-800w.jpg 800w" sizes="(max-width: 320px) 280px, (max-width: 480px) 440px, 800px" src="elva-fairy-800w.jpg" alt="요정 옷을 입은 엘바">
img
에는 srcset
과 sizes
라는 속성이 존재하고 이를 통해 다음과 같이 진행할 수 있습니다.
-
이 속성을 지원하는 뷰포트 너비가
480px
인 브라우저가 페이지를 불러온다면 -
max-width: 480px
의 미디어 조건문이 참이 되고, 440px이 선택됩니다. -
440px
이 선택되면srcset
에 가장 가까운 고유 너비인480w
가 선택되고두번째 이미지인
elva-fairy-480w.jpg 480w
이미지가 출력됩니다.
당연히, 작은 이미지의 경우 이미지의 사이즈가 작아지므로 이미지 최적화에 적절합니다. 게다가 반응형을 사용할 때에, 모바일 사용자에게 1080px
보다 큰 이미지를 제공할 필요는 없어 보입니다.
위를 간단히 정리하면, 아래와 같이 정리할 수 있습니다.
<img srcset="경로 원본크기, 경로 원본크기, 경로 원본크기" sizes="(미디어조건) 최적화출력크기, (미디어조건) 최적화출력크기, 기본출력크기" />
loading
브라우저가 이미지를 불러올 때 사용하는 방식을 지정합니다.
eager
이 기본값으로 뷰포트 안에 위치하는지와 관계없이 이미지를 즉시 불러옵니다.
lazy
를 설정하게 되면, 뷰포트의 일정 거리안으로 들어와야 불러옵니다.
즉 여기서 lazy
속성은 이미지의 lazy loading
을 가능하게 해줍니다. 최신 브라우저에서는 대부분 지원이 되고 있습니다.
이를 통해 보면 Next
의 이미지는 기본적으로 lazy
속성이 들어가 있음을 확인할 수 있습니다.
위 두가지 속성들은 Next/Image
에서 모두 사용할 수 있습니다. 이를 이용해 추가적인 기본적으로도 잘 되지만 추가적인 이미지 최적화를 적용할 수 있습니다.
추가적인 이미지 최적화 적용하기
반응형 이미지 최적화
오터로그의 첫 페이지는 카드 컴포넌트로 이루어집니다. 640px
을 기점으로 두개로, 1080px
을 기점으로 세개의 카드 컴포넌트가 수평으로 출력되는 형식입니다. 그리고 카드 컴포넌트는 320px
의 고정 크기를 가지고 있습니다.
그런데 한가지 아쉬운점이 있습니다.
3가지 컴포넌트가 모두 보이는 경우에 네트워크에 출력된 이미지의 크기는 다음과 같습니다.
이미지가 실제로 차지하는 부분은 320px
길이 뿐인데 가져오는 이미지는 2000px
입니다.
sizes
속성을 통해 이 부분을 수정해줄 수 있습니다.
<Image src={thumbnailImg} alt={title} // width={600} fill sizes='33vw' // 수정한 부분 className='rounded-t-lg object-cover' />
sizes
속성을 통해 뷰포트의 3분의 1 길이를 srcset
에서 찾아내 출력해 줄 겁니다. 이런 과정을 통해 받아오는 이미지를 다음과 같이 최적화 할 수 있습니다.
그렇지만, 이러한 설정은 모바일 뷰에서는 화질의 저하를 가지고 올 수 있습니다. sizes
를 미디어 쿼리를 사용해 다음과 같이 진행하면 모바일 뷰에서도 적절한 크기를 가지고 올 수 있도록 합시다.
위의 설정만 하면 위처럼 모바일 뷰에서는 256
너비의 이미지를 가지고 오게 됩니다. 이건 또 너무 작습니다.
<Image src={thumbnailImg} alt={title} fill **sizes='(max-width: 640px) 100vw, // 2장이 되는 브레이크 포인트 (max-width: 1080px) 50vw, // 카드가 3장이 되는 브레이크 포인트 33vw'** className='rounded-t-lg object-cover' priority={true} />
이제 반응형 카드 컴포넌트에서도, 렌더링 되는 시점의 디바이스 크기에 따라 적절한 크기의 이미지를 가지고 올 수 있습니다.
마지막으로 확인해봅시다. 아까 이미지 크기가 너무 컸던 이미지는 다음과 같이 상대적으로 적절한 크기의 이미지를 불러왔습니다. (sizes 설정을 조금 더 디테일하게 하면, 더 최적화가 가능할 것으로 생각됩니다.)
이미 최적화되어 24.2 kB
였던 이미지의 크기를 8.6 kB
까지 줄일 수 있었습니다.
필요없는 레이지 로딩 제거
Next의 Image
컴포넌트는 기본적으로 loading = lazy
가 적용되어 있습니다. 그런데 이 부분은 LCP
와 관련한 문제가 발생할 수 있습니다. LCP
가 측정될때 지연로드가 되면 지연로드가 된 시간이 측정 시간에 포함되기 때문입니다.
실제로, 페이지가 렌더링되자마자 보여지는 부분에 레이지로딩이 적용되어 있다면 라이트하우스도 위와 같은 진단을 내려줍니다.
이 글을 보고 계시다면, 이 글의 헤더 부분에 썸네일 이미지가 들어가는 것을 보셨을 겁니다. 그런데, 해당 부분은 페이지가 열리자마자 보여지게 되는 부분이므로, 레이지로딩이 적용되면 안됩니다. 오히려 이미지의 로딩 시간을 늘어나게 합니다.
<Image src={thumbnailImg} alt={title} fill priority={true} /> // priority 설정을 통해 레이지로딩을 취소하고, 우선적으로 받아오게끔 합니다.
정리하기
Next/Image
를 통해 이미지를 최적화하는 사용하는 과정에서 진행한 부분은 다음과 같습니다.
img
태그의srcset
,sizes
를 이용해 반응형에 따라 다른 크기의 이미지를 불러왔습니다.- 불필요한 레이지로딩을 제거해, LCP 속도를 줄일 수 있었습니다.
그런데 사용하고 나니, Next 환경이 아니더라도 이미지의 최적화를 위해 진행할 수 있는 부분이라는 생각이 들었습니다. 리액트 환경에서도, img
태그와 srcset, sizes
를 이용해 해당 부분을 충분히 진행할 수 있기 때문입니다. 이전 글에서 다루었던 것처럼, 다른 크기의 이미지를 클라우드에서 받아오는 방식과 결합하면 충분히 가능하다 생각됩니다.