사이트에 실제 렌더링 될 크기에 비해서 원본 이미지가 너무 크거나 혹은 이미지 용량이 너무 크다면 성능에 그리 좋지 않을 것이다. 특히 이미지가 최적화 되지 않은 사이트가 이커머스 사이트라면 웹 로딩 속도도 느려질 것이고 사용자 경험에도 악영향을 줄 수 있다고 생각되었다. 🤔
이번에는 이미지 최적화를 하기위한 작업들에는 어떤 것들이 있으며, 어떤 점들을 고려해야할 지 고민하고, 타 사이트의 사례를 참고해서 나는 우리사이트에 어떤 방법으로 접근할지 차근차근 정리해보자 😤
이미지의 내용과 용도에 따라 적절한 형식 (JPEG, PNG, GIF, WebP 등)을 선택한다. 예를 들어, 사진은 JPEG로, 투명 배경이 필요한 이미지는 PNG로 저장할 수 있다.
여러 이미지 압축 도구를 사용하여 이미지의 파일 크기를 줄일 수 있다. 예를 들면, TinyPNG, ImageOptim, Squoosh 등이 있다.
(1) TinyPNG
PNG 및 JPEG 이미지를 압축하는 웹 기반 서비스이다.
API를 통해 자동화된 작업에도 적용할 수 있으며, 데스크톱 버전도 제공한다.
(2)ImageOptim
Mac용 데스크톱 애플리케이션으로, PNG, JPEG, GIF 등의 이미지 형식을 지원하며, 다양한 압축 알고리즘을 통해 이미지를 최적화한다.
GUI를 통한 쉬운 사용법과, CLI를 통한 자동화가 가능하다.
(3)Squoosh
Google Chrome Labs에서 제공하는 웹 기반 이미지 압축 도구로, 다양한 형식의 이미지를 최적화할 수 있다.
WebP, AVIF와 같은 최신 이미지 형식의 변환도 지원하며, 세밀한 설정 변경이 가능하다.
웹사이트에 표시되는 이미지의 실제 크기에 맞게 이미지의 해상도를 조정한다.
큰 이미지를 작게 표시하는 것은 비효율적이다.
300*300 px
크기로 표시되는 이미지는 해당 크기로 리사이즈하는 것이 좋겠다.✍️ 권장 사항(사이트마다 다를 것이다)
250*250px
~ 400*400px
800*800px
~ 1200*1200px
2000*2000px
이상 (필요한 경우)이미지가 화면에 표시될 때만 로드되게 하는 기법이다. 이로써 초기 페이지 로딩 시간이 줄어들 수 있다.
콘텐츠 전송 네트워크(Content Delivery Network)를 사용하여 이미지를 빠르게 전송할 수 있다.
Retina와 같은 고해상도 디스플레이를 위한 이미지는 더 큰 해상도를 필요로 하지만, 일반 디스플레이에서는 그렇지 않으므로 적절한 해상도를 선택하는 것도 중요하다.
Next.js는 이미지 최적화를 쉽게 할 수 있도록 next/image 컴포넌트를 제공한다.
Next.js의 next/image 컴포넌트를 사용하려면, 이미지를 public 폴더에 저장하거나 외부 URL을 지정해야 한다. 또한, next.config.js에서 이미지 도메인을 허용해야 한다.
서버에서 자동으로 이미지 크기를 조절하여 필요한 크기만큼만 전송한다.
next/image는 사용자의 브라우저와 호환되는 최적의 이미지 형식을 자동으로 선택한다.
next/image는 기본적으로 이미지의 지연 로딩을 지원한다.
이미지가 로드되기 전에 표시할 플레이스홀더를 지정할 수 있다.
(1) Rendered size
이미지가 실제로 웹 페이지에서 어떻게 표시되는지에 대한 크기이다.
CSS나 자바스크립트에 의해 이미지 크기가 변경되었을 경우, 이 변경된 크기를 나타낸다.
(2) Rendered aspect ratio
'Rendered size'의 가로 세로 비율이다.
16:9, 4:3 등의 형태로 나타낼 수 있다.
(3) Intrinsic size
이미지 파일 자체의 원래 크기이다.
웹 페이지에 로드되기 전 이미지의 실제 크기를 나타낸다.
(4) Intrinsic aspect ratio
'Intrinsic size'의 가로 세로 비율이다.
(5) File size ⭐️
이미지 파일의 용량이다.
이는 웹 페이지의 로딩 속도에 영향을 줄 수 있으므로, 최적화가 필요한 경우 이 값을 참고하여 이미지의 용량을 줄일 수 있다.
(6)Current source
현재 렌더링된 이미지의 출처 URL이다.
❓ 개발자도구 Elements 탭에 img
태그를 찾을 수 없고 그저 ::after
처리가 되어있었다.
이때 직접 url 주소로 이미지를 확인하자.
다른 사이트는 어떤식으로 이미지 요소를 다루고있을까? 🧐
<picture> + webp + lazy
조합을 사용중인 것으로 보여졌다. 🧐
<picture data..."" data...="" class="picture product_img">
<source data...="" type="image/webp" srcset="https://site.png?type=m_webp">
<source data...="" srcset="https://site.png?type=m">
<img data...="" alt="" src="https://site.png?type=m" loading="lazy" class="image full_width"></picture>
✍️ 어떤 의도일까?
브라우저가 첫 번째 <source>
태그를 확인한다.
<source>
태그를 확인하지 않는다.<source>
태그로 넘어간다.브라우저가 두 번째 <source>
태그를 확인한다.
만약 브라우저가 <picture>
내의 모든 <source>
태그들을 지원하지 않는다면, <img>
태그의 src
속성에 지정된 이미지를 로드한다.
즉, 목록 사진과 디테일 사진을 아래와 같이 가져와서 사용, 이미지 최적화 중이다.
목록에 맞는 m사이즈 png 파일, m사이즈 webp 파일 → 2개 이미지
디테일에 맞는 l사이즈 png 파일, l 사이즈 webp 파일 → 2개 이미지
따라서 모든 이미지는 각각 여러 사이즈(m,l)의 이미지 파일이 필요하고, 각각 webp 전용 이미지가 필요할 것이다.
✍️ 미리 여러 사이즈의 이미지 파일을 준비하거나 백엔드 작업이 필요하다.🤔
https://data1.pokemonkorea.co.kr/newdata/pokedex/full/imageA.png
https://data1.pokemonkorea.co.kr/newdata/pokedex/mid/imageA.png
https://cdn2.a.com/images/uhren/imageA-ExtraLarge.jpg
https://cdn2.a.com/images/uhren/imageA-Square480.jpg
✍️ WebP 변환작업이 필요하다.
WebP
는 Google이 개발한 이미지 포맷이다.WebP 포맷의 주요 특징 및 장점
WebP의 단점 중 하나는 아직 모든 웹 브라우저에서 지원되지 않는다는 것! 그러나 주요 브라우저 대부분 (Chrome, Firefox, Edge, Opera 등)에서는 WebP 포맷을 지원하고 있다고한다.😀
(1) Native Lazy Loading
✍️ 우선 가장 기본적인 방법으로 lazy loading 을 적용해보자. (현재 단계에서 intersection Observer까지 필요할진 모르겠다.)
<img src={data.img_url} alt={data.name} loading="lazy" />
Native Lazy와 Intersection Observer 같이 사용하기
무한 스크롤 로직을 위해 Intersection Observer
를 사용하면서, 각각의 이미지 로딩을 지연시키기 위해 loading="lazy"
속성을 사용하는 것이 좋다고 한다. 이렇게 두 기능을 함께 사용하면 웹 페이지의 성능과 사용자 경험을 동시에 향상시킬 수 있기 때문일까 🤔
(2) Intersection Observer API
✍️ Native Lazy 를 벗어나 비로소 Intersection Observer를 써야할때??
"단순하지 않은 페이지"란 여러 가지 요소와 인터랙션들이 결합된 페이지를 의미한다.
이러한 상황에서는 native lazy loading만으로는 원하는 방식으로 이미지 로딩을 제어하기 어려울 수 있다. 따라서, IntersectionObserver
를 사용하면 더 세밀하게 이미지 로딩을 제어하고, 복잡한 페이지 구조와 상호작용에 더 효과적으로 대응할 수 있다.
예를 들어, 사용자가 특정 탭을 클릭하여 내용을 보여주는 경우, 해당 탭의 내용 안에 있는 이미지를 로드하려면 IntersectionObserver
를 사용하여 해당 영역이 활성화될 때 이미지를 로드하는 것이 더 적절할 수 있다.
(3) JavaScript 라이브러리
✍️ 여러 라이브러리와 플러그인이 Lazy Loading 구현을 도와준다.
data-src
와 data-srcset
속성을 사용하여 이미지의 지연 로딩을 관리한다.(4) Scroll Event Listerner
✍️ 사용자의 스크롤 이벤트를 감지하고, 특정 요소가 뷰포트에 들어오면 해당 요소를 로드하는 방식이다.
✍️ 미리 여러 사이즈의 이미지 파일을 준비하거나 백엔드 작업이 필요하다.🤔
❓ 레퍼런스 처럼 loading="lazy"
와 <picture>
를 같이 사용할 수 있는건가?
<picture>
<source media="(min-width: 650px)" srcset="large-image.jpg">
<source media="(min-width: 465px)" srcset="medium-image.jpg">
<img src="default-image.jpg" alt="Description" loading="lazy">
</picture>
<picture>
요소 내에서 <img>
태그에 loading="lazy"
속성이 설정되면, 해당 <img>
태그가 참조하는 이미지 소스(즉, 조건에 따라 선택되는 <source>
의 srcset
또는 <img>
의 src
)에 Lazy Loading이 적용된다.
따라서, 위의 예제에서 뷰포트의 너비가 650px 이상일 경우 large-image.jpg
이미지가 선택되며, 해당 이미지는 loading="lazy"
속성 덕분에 Lazy Loading이 적용된다.
요약하면, <source>
요소에 직접적으로 Lazy Loading을 적용할 수는 없지만, <picture>
요소 내의 <img>
태그에 loading="lazy"
속성을 설정함으로써 간접적으로 <source>
의 이미지에도 Lazy Loading을 적용할 수 있다고 😊
✍️ lazysizes는 더 많은 기능을 제공하는 Lazy Loading 라이브러리로, 이미지와 비디오뿐만 아니라 다양한 리소스와 웹 요소에 대한 Lazy Loading을 지원한다.
lazysizes와 lazyload 라이브러리 중 어떤것을 선호하는지 npm trends 검색을 통해 알아보았다.
data-src
, data-srcset
및 data-sizes
속성을 사용하여 이미지의 실제 경로와 크기를 지정한다.<picture>
태그 및 srcset
와 같은 반응형 이미지에 대한 지원이 내장되어 있다.✍️ 대량의 데이터나 컨텐츠를 여러 페이지로 분할하여 제공한다. 따라서 특히 이미지가 많이 포함된 웹 페이지에서는 페이지네이션의 중요성이 더욱 강조된다.
(1) 초기 로딩 속도 향상
페이지네이션을 사용하면 한 페이지에 표시되는 이미지 수가 제한되므로, 초기 로딩 시 다운로드해야 할 이미지의 양이 줄어든다. 이로 인해 초기 로딩 시간이 단축될 수 있다.
(2) 서버 부하 감소
사용자가 한 번에 모든 이미지를 요청하는 대신, 페이지네이션을 통해 일부만 요청하게 되므로 서버의 부하가 감소한다.
(3) 사용자 경험 향상
사용자는 필요한 부분만 로드하여 볼 수 있기 때문에 웹사이트를 더 효과적으로 탐색할 수 있다. 또한, 너무 많은 이미지가 한 페이지에 로드되는 것을 방지하여 스크롤의 양을 줄일 수 있다.
(4) 네트워크 트래픽 절약
한 번에 모든 이미지를 로드하지 않기 때문에, 불필요한 네트워크 트래픽을 줄일 수 있다.
(5) 지연 로딩(Lazy Loading)과의 조합
페이지네이션과 지연 로딩을 함께 사용하면, 현재 화면에 표시되는 이미지만 로드하고, 나머지는 스크롤 시 로드하는 방식으로 최적화할 수 있다.🤔
이미 개발이 끝난 페이지의 경우 원본 이미지를 화면에 맞게 다시 재조정 하기 어려운 상황에서는...
✍️ 이러한 작업을 너무 많이 프론트에서 처리하면 성능적으로 문제가 있을 수 있다고 한다.
서버 측 이미지 처리는 웹 서버나 백엔드 시스템에서 이미지를 처리하고 수정하는 작업을 의미! 이 작업은 클라이언트(브라우저)가 요청하기 전에 이미지를 처리하거나 클라이언트의 특정 요청에 따라 동적으로 이미지를 처리하는 방식으로 수행될 수 있다.
서버 측에서 이미지 처리를 하는 주요 이유
이렇게 서버 측에서 이미지 처리를 수행하면, 모든 클라이언트에게 최적화된 이미지를 제공할 수 있으며, 클라이언트 측에서 추가적인 이미지 처리 작업 없이 필요한 이미지만을 로드할 수 있다고한다! 🧐
이미지 최적화에 대해 노션에 고민해 뒀던 흔적중 일부이다.
일단 LazyLoading 기법을 사용한다음, 백엔드가 가능하다면 WebP와 picture을 도입하고, react-query로 페이지네이션을 구현하게 되면 Intersection-Observer로 같이 사용하면 좋을 것 같다고 생각했다. 그리고 next에서 제공하는 image 컴포넌트가 자동으로 LazyLoading과 WebP를 도와주니 이부분도 수정하면 좋겠다고 생각했다. 🤔
React Query - useInfiniteQuery & 이미지 최적화 적용하기 🎉
WebP와 next/Image, Lighthouse 지표와 관련하여
reference
next/image
안녕하세요
next Image를 이용하면 webp같은 이미지 형식으로 압축된 이미지를 불러올 수 있는건가요?
기존 배경이미지가 10mb가 넘어가서 화질을 낮추기엔 클라이언트가 화질이 높았으면 좋겠다고 요청하는 상황에서
그냥 두면 될지 따로 처리를 해야할지 고민중입니다!