이미지 최적화에 대해

seul_velog·2023년 9월 18일
0
post-thumbnail

사이트에 실제 렌더링 될 크기에 비해서 원본 이미지가 너무 크거나 혹은 이미지 용량이 너무 크다면 성능에 그리 좋지 않을 것이다. 특히 이미지가 최적화 되지 않은 사이트가 이커머스 사이트라면 웹 로딩 속도도 느려질 것이고 사용자 경험에도 악영향을 줄 수 있다고 생각되었다. 🤔

이번에는 이미지 최적화를 하기위한 작업들에는 어떤 것들이 있으며, 어떤 점들을 고려해야할 지 고민하고, 타 사이트의 사례를 참고해서 나는 우리사이트에 어떤 방법으로 접근할지 차근차근 정리해보자 😤


이미지 포맷 선택

이미지의 내용과 용도에 따라 적절한 형식 (JPEG, PNG, GIF, WebP 등)을 선택한다. 예를 들어, 사진은 JPEG로, 투명 배경이 필요한 이미지는 PNG로 저장할 수 있다.

  • JPEG: 사진이나 복잡한 이미지에 적합하다. 압축률이 높아 품질 손실이 있을 수 있지만 웹용으로는 충분하다.
  • PNG: 배경이 투명한 이미지나 아이콘, 로고 등에 적합하다.
  • 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 이상 (필요한 경우)


Lazy Loading

이미지가 화면에 표시될 때만 로드되게 하는 기법이다. 이로써 초기 페이지 로딩 시간이 줄어들 수 있다.

  • 사용자가 스크롤을 내리면서 해당 이미지 부분에 도달할 때 이미지가 로드된다.



CDN 사용

콘텐츠 전송 네트워크(Content Delivery Network)를 사용하여 이미지를 빠르게 전송할 수 있다.

  • CDN (Content Delivery Network)은 전 세계 여러 위치에 분산된 서버 네트워크로, 사용자에게 웹 컨텐츠를 빠르게 제공하기 위해 설계되었다.
  • 이미지를 전세계 여러 서버에 배포하여 사용자에게 빠르게 제공한다.

적절한 해상도 선택

Retina와 같은 고해상도 디스플레이를 위한 이미지는 더 큰 해상도를 필요로 하지만, 일반 디스플레이에서는 그렇지 않으므로 적절한 해상도를 선택하는 것도 중요하다.





Next.js에서 next/image 사용하기⭐️

Next.js는 이미지 최적화를 쉽게 할 수 있도록 next/image 컴포넌트를 제공한다.

Next.js의 next/image 컴포넌트를 사용하려면, 이미지를 public 폴더에 저장하거나 외부 URL을 지정해야 한다. 또한, next.config.js에서 이미지 도메인을 허용해야 한다.

자동 리사이징 및 이미지 변환 제공

서버에서 자동으로 이미지 크기를 조절하여 필요한 크기만큼만 전송한다.
next/image는 사용자의 브라우저와 호환되는 최적의 이미지 형식을 자동으로 선택한다.

  • Next.js는 이미지를 webp와 같은 용량이 작은 포맷으로 이미지를 변환해서 제공한다.
    (항상 WebP를 지원하는 것은 아니다. 브라우저에 따라 지원되지 않을 수도 있다. 즉, next/image는 브라우저의 지원 여부에 따라 최적의 형식을 선택한다.)



Lazy Loading

next/image는 기본적으로 이미지의 지연 로딩을 지원한다.

  • Next/Image를 사용하게 되면 자동으로 lazy loading이 적용된다.
  • 중요한 이미지 일부에 lazy loading을 적용하고 싶지 않은 경우에는 해당 기능을 끌 수도 있다.
    Image 컴포넌트의 priority라는 prop을 true로 설정하거나, loading prop에 “eager” 값을 설정하면 된다. priorty 값을 설정하는 것이 더 권장되는 방식이다.😀



Placeholder

이미지가 로드되기 전에 표시할 플레이스홀더를 지정할 수 있다.

  • 어떤 웹 사이트에 방문했을 때 이미지가 로드되기 전까지 영역의 높이가 0이었다가 이미지가 로드된 후 이미지만큼 영역이 늘어서 레이아웃이 바뀌는 현상을 CLS(Cumulative Layout Shift)라고 부른다.
    Next/Image는 레이아웃이 흔들리는 현상을 방지하기 위해 placeholder를 제공한다.





이미지 파일 사이즈, 용량 확인하기

Elements 탭의 img 속성 알기

(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 탭에서 확인하기



직접 이미지 소스 확인하기

❓ 개발자도구 Elements 탭에 img 태그를 찾을 수 없고 그저 ::after 처리가 되어있었다.
이때 직접 url 주소로 이미지를 확인하자.



Network 탭에서 확인하기





레퍼런스

다른 사이트는 어떤식으로 이미지 요소를 다루고있을까? 🧐

A사이트

<picture> + webp + lazy 조합을 사용중인 것으로 보여졌다. 🧐

  • 목록 페이지 상품) 실제 사이즈 525*525 : 약 10kb
  • 디테일 페이지 상품) 실제 사이즈 1120*1120 : 약 36kb
<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>

✍️ 어떤 의도일까?

  1. 브라우저가 첫 번째 <source> 태그를 확인한다.

    • WebP를 지원한다면 해당 WebP 이미지를 로드하고, 더 이상 다른 <source> 태그를 확인하지 않는다.
    • WebP를 지원하지 않는다면 다음 <source> 태그로 넘어간다.
  2. 브라우저가 두 번째 <source> 태그를 확인한다.

    • 해당 이미지 (이 경우 PNG)를 로드한다.
  3. 만약 브라우저가 <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 변환작업이 필요하다.

  • WebP는 Google이 개발한 이미지 포맷이다.
  • WebP는 특히 웹에서 사용하기 위해 설계되었으며, 웹 페이지의 로딩 속도를 향상시키기 위한 목적으로 최적화되어 있다.

WebP 포맷의 주요 특징 및 장점

  1. 효율적인 압축: WebP는 기존의 JPEG 및 PNG에 비해 더 작은 파일 크기로 동일한 품질의 이미지를 제공할 수 있다. 이것으로 웹페이지의 로딩 시간이 단축될 수 있다.
  2. 손실 및 비손실 압축 지원: WebP는 손실 (lossy) 압축과 비손실 (lossless) 압축 모두를 지원한다. 이를 통해 웹 개발자는 상황에 따라 적절한 압축 방식을 선택할 수 있다.
  3. 알파 채널 지원: WebP는 투명성을 지원하는 알파 채널을 포함하여 비손실 압축 이미지를 제공할 수 있다. 이는 PNG와 유사한 기능이지만, 일반적으로 더 작은 파일 크기로 결과를 얻을 수 있다.
  4. 애니메이션 지원: WebP는 여러 프레임으로 구성된 애니메이션 이미지도 지원한다. 이는 기존의 GIF에 대한 더 효율적인 대안을 제공한다.
  5. 풍부한 색상 정보: WebP는 24-bit RGB 색상 정보와 함께 8-bit 알파 채널을 지원한다.
  6. 프로그래시브 디코딩: WebP 이미지는 점진적으로 로딩되어 사용자에게 빠르게 저해상도 버전의 이미지를 보여준 후, 높은 해상도로 전환될 수 있다.

WebP의 단점 중 하나는 아직 모든 웹 브라우저에서 지원되지 않는다는 것! 그러나 주요 브라우저 대부분 (Chrome, Firefox, Edge, Opera 등)에서는 WebP 포맷을 지원하고 있다고한다.😀


LazyLoding ✔️

(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

  • picture 태그와도 같이 쓸 수 있다고 한다!
  • 이 API는 타겟 요소가 뷰포트와 얼마나 교차하는지 관찰하고, 특정 조건에서 콜백 함수를 실행할 수 있게 한다.
  • 이를 활용하여 이미지가 뷰포트에 근접하게 되면 이미지를 로드하는 방식으로 Lazy Loading을 구현할 수 있다.

✍️ Native Lazy 를 벗어나 비로소 Intersection Observer를 써야할때??

"단순하지 않은 페이지"란 여러 가지 요소와 인터랙션들이 결합된 페이지를 의미한다.

  • 복잡한 애니메이션: 페이지 내에서 이미지나 다른 요소들이 다양한 애니메이션 효과 (페이드인, 슬라이드, 회전 등)를 통해 등장하는 경우.
  • 동적 컨텐츠 로딩: AJAX, WebSocket, 또는 다른 비동기 메커니즘을 사용하여 동적으로 컨텐츠가 추가되는 페이지. 예를 들어, 무한 스크롤 기능이 있는 경우.
  • 페이지 레이아웃의 변화: 사용자의 인터랙션에 따라 페이지 레이아웃이 크게 변화하는 경우. 예를 들어, 필터링 옵션을 선택하면 상품 목록의 레이아웃이나 순서가 바뀌는 경우.
  • 탭이나 아코디언 구조: 페이지의 일부 내용이 처음에는 숨겨져 있고, 사용자의 인터랙션에 따라 보여지는 탭, 아코디언, 모달 창 등.
  • 다양한 뷰포트와 반응성: 다양한 디바이스 크기와 해상도에 반응하는 반응형 디자인이 적용된 페이지.

이러한 상황에서는 native lazy loading만으로는 원하는 방식으로 이미지 로딩을 제어하기 어려울 수 있다. 따라서, IntersectionObserver를 사용하면 더 세밀하게 이미지 로딩을 제어하고, 복잡한 페이지 구조와 상호작용에 더 효과적으로 대응할 수 있다.

예를 들어, 사용자가 특정 탭을 클릭하여 내용을 보여주는 경우, 해당 탭의 내용 안에 있는 이미지를 로드하려면 IntersectionObserver를 사용하여 해당 영역이 활성화될 때 이미지를 로드하는 것이 더 적절할 수 있다.


(3) JavaScript 라이브러리
✍️ 여러 라이브러리와 플러그인이 Lazy Loading 구현을 도와준다.

  • Lozad.js: Intersection Observer를 사용하여 Lazy Loading을 구현한 라이브러리
  • LazyLoad: 가벼운 Vanilla JS 라이브러리로, data-srcdata-srcset 속성을 사용하여 이미지의 지연 로딩을 관리한다.

(4) Scroll Event Listerner
✍️ 사용자의 스크롤 이벤트를 감지하고, 특정 요소가 뷰포트에 들어오면 해당 요소를 로드하는 방식이다.

  • Intersection Observer와 비교하여 성능이 떨어질 수 있으며, 스크롤 이벤트가 너무 자주 발생하면 성능 문제가 발생할 수 있다. 패스!



picture ✔️

✍️ 미리 여러 사이즈의 이미지 파일을 준비하거나 백엔드 작업이 필요하다.🤔
❓ 레퍼런스 처럼 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-srcsetdata-sizes 속성을 사용하여 이미지의 실제 경로와 크기를 지정한다.
  • 자동으로 가장 가까운 뷰포트의 이미지를 미리 로드하는 기능을 제공한다.
  • <picture> 태그 및 srcset와 같은 반응형 이미지에 대한 지원이 내장되어 있다.
  • 플러그인 아키텍처를 제공하여 확장성이 뛰어나며 다양한 플러그인을 통해 추가 기능을 제공한다.
  • LazyLoad는 가볍고 간단한 기능에 중점을 둔 반면, lazysizes는 더 많은 기능과 확장성을 제공하기 때문에 상대적으로 더 크고 복잡할 수 있다.



페이지네이션 ✔️

✍️ 대량의 데이터나 컨텐츠를 여러 페이지로 분할하여 제공한다. 따라서 특히 이미지가 많이 포함된 웹 페이지에서는 페이지네이션의 중요성이 더욱 강조된다.

(1) 초기 로딩 속도 향상
페이지네이션을 사용하면 한 페이지에 표시되는 이미지 수가 제한되므로, 초기 로딩 시 다운로드해야 할 이미지의 양이 줄어든다. 이로 인해 초기 로딩 시간이 단축될 수 있다.

(2) 서버 부하 감소
사용자가 한 번에 모든 이미지를 요청하는 대신, 페이지네이션을 통해 일부만 요청하게 되므로 서버의 부하가 감소한다.

(3) 사용자 경험 향상
사용자는 필요한 부분만 로드하여 볼 수 있기 때문에 웹사이트를 더 효과적으로 탐색할 수 있다. 또한, 너무 많은 이미지가 한 페이지에 로드되는 것을 방지하여 스크롤의 양을 줄일 수 있다.

(4) 네트워크 트래픽 절약
한 번에 모든 이미지를 로드하지 않기 때문에, 불필요한 네트워크 트래픽을 줄일 수 있다.

(5) 지연 로딩(Lazy Loading)과의 조합
페이지네이션과 지연 로딩을 함께 사용하면, 현재 화면에 표시되는 이미지만 로드하고, 나머지는 스크롤 시 로드하는 방식으로 최적화할 수 있다.🤔



이미지 리사이징과 서버 측 이미지 처리

이미 개발이 끝난 페이지의 경우 원본 이미지를 화면에 맞게 다시 재조정 하기 어려운 상황에서는...

canvas 를 사용해서 이미지 리사이징하기

✍️ 이러한 작업을 너무 많이 프론트에서 처리하면 성능적으로 문제가 있을 수 있다고 한다.

서버 측 이미지 처리

서버 측 이미지 처리는 웹 서버나 백엔드 시스템에서 이미지를 처리하고 수정하는 작업을 의미! 이 작업은 클라이언트(브라우저)가 요청하기 전에 이미지를 처리하거나 클라이언트의 특정 요청에 따라 동적으로 이미지를 처리하는 방식으로 수행될 수 있다.

서버 측에서 이미지 처리를 하는 주요 이유

  1. 성능 최적화: 이미지의 크기와 해상도를 조절하여, 필요한 이미지만 제공함으로써 페이지의 로딩 속도를 향상
  2. 저장 공간 절약: 여러 버전의 이미지를 미리 생성하고 저장하는 대신, 요청 시점에 이미지를 동적으로 생성하므로 저장 공간을 절약
  3. 유연성: 다양한 디바이스와 화면 크기에 맞게 이미지를 동적으로 제공

이렇게 서버 측에서 이미지 처리를 수행하면, 모든 클라이언트에게 최적화된 이미지를 제공할 수 있으며, 클라이언트 측에서 추가적인 이미지 처리 작업 없이 필요한 이미지만을 로드할 수 있다고한다! 🧐


이미지 최적화에 대해 노션에 고민해 뒀던 흔적중 일부이다.
일단 LazyLoading 기법을 사용한다음, 백엔드가 가능하다면 WebP와 picture을 도입하고, react-query로 페이지네이션을 구현하게 되면 Intersection-Observer로 같이 사용하면 좋을 것 같다고 생각했다. 그리고 next에서 제공하는 image 컴포넌트가 자동으로 LazyLoading과 WebP를 도와주니 이부분도 수정하면 좋겠다고 생각했다. 🤔



📌 실제 개선 과정 & 결과에 대한 기록 👇👇👇

React Query - useInfiniteQuery & 이미지 최적화 적용하기 🎉
WebP와 next/Image, Lighthouse 지표와 관련하여





reference
next/image

profile
기억보단 기록을 ✨

3개의 댓글

comment-user-thumbnail
2024년 3월 28일

안녕하세요
next Image를 이용하면 webp같은 이미지 형식으로 압축된 이미지를 불러올 수 있는건가요?
기존 배경이미지가 10mb가 넘어가서 화질을 낮추기엔 클라이언트가 화질이 높았으면 좋겠다고 요청하는 상황에서
그냥 두면 될지 따로 처리를 해야할지 고민중입니다!

1개의 답글