concept/React, Redux, RN

이미지 최적화를 위한 방법들과 Next/Image

오연 : Oana 2023. 11. 5. 00:25

배경

Web Vital 이란 웹에서 우수한 사용자 경험을 제공하는데 필수적인 품질 신호에 대한 통합 지침을 제공하기 위한 Google의 이니셔티브이다.

Web Vital 의 하위 집합으로 Core Web Vitals가 있는데 20년 기준으로는 UX 기준의 세 가지 측면인 1. Loading (로딩) 2. Interactivity (상호 작용) 3. Visual Stability (시각적 안정성) 에 중점을 두고 있다.

 

  • LCP (최대 콘텐츠풀 페인트) - 로딩 성능을 측정. 2.5초 이내에 LCP가 발생해야 우수하다고 평가한다.
  • FID (최초 입력 지연) - 상호 작용 측정. 100 ms 이내에 상호 작용이 가능해야 우수하다고 평가한다.
  • CLS (누적 레이아웃 시프트) - 시각적 안정성 측정. 레이아웃이 이동하는 데 소요되는 시간을 측정해서 점수로 표현. 0.1 이하의 CLS 를 유지해야 우수하다고 평가한다.

이 중 LCP가 페이지를 처음 로드한 시점을 기준으로 화면에 있는 가장 큰 이미지, 텍스트 블록의 렌더링 시간을 알려주는 지표이다. 특히 텍스트보다는 이미지 렌더링에 따라 이 시간이 좌우되기 때문에 이미지를 최적화 하는 것은 성능 최적화의 여러가지 요소 중 가장 먼저 고려되는 요소이다.

 

 💡 이미지를 최적화 하는 것은 성능 최적화의 여러가지 요소 중 가장 먼저 고려되는 요소

 

기본 HTML 에서의 이미지 최적화 전략

1. 이미지 형식 변경 (jpg → png → webp → avif)

  • webp 는 png보다 파일 크기를 26% 까지 줄일 수 있음.
  • avif 는 webp 대비 20% 더 높은 압축률을 보여줌.

2. lazy loading

  • 특정 리소스를 네트워크에 요청하지 않고 있다가, 필요한 순간에만 리소스를 요청하는 최적화 전략
  • <img> 태그에서 loading='auto | lazy | eager' 속성을 활용해 레이지 로딩을 적용할 수 있음.

3. 이미지 Resizing

  • 모바일, 태블릿, 노트북, 데스크탑 등 브라우저 화면에 알맞는 이미지를 가져와서 보여주는 방법
<img srcset="pikachu-320w.jpg 320w,
             pikachu-480w.jpg 480w,
             pikachu-800w.jpg 800w"
     sizes="(max-width: 320px) 280px,
            (max-width: 480px) 440px,
            800px"
     src="pikachu-800w.jpg" alt="잠자는 피카츄">
  • srcset - 브라우저에게 어떤 크기의 이미지를 보여주면 되는지 알려주는 역할
  • sizes - 미디어 조건문 설정
    • 미디어 조건문: (max-width: 320px)
    • 조건이 참일 때 이미지의 너비: 280px
  • 위와 같이 코드를 작성했을 때 브라우저에서 일어나는 순서
    1. 기기의 너비를 확인
    2. sizes 에서 참이 되는 조건문과 이미지 너비 확인
    3. srcset 에서 해당 너비와 가장 근접한 이미지를 찾기

4. 이미지 용량 압축

  • 라이브러리를 활용할 수 있음.

npm: browser-image-compression

5. 이미지 캐싱

Next/Image 에서의 이미지 최적화 전략

1. lazy loading

  • 레이지로드를 기본적으로 사용하고 있기 때문에 뷰포트에 노출되었을 때 이미지 로드. 선택적으로 블러링 처리한 이미지를 먼저 노출하는 기능
  • 중요한 이미지일 경우 레이지로딩을 사용하고 싶지 않다면 priority 속성을 주거나, loading prop 에 eager 값을 설정할 수 있음.

2. 이미지 사이즈 최적화

  • 장치의 크기에 맞춘 적절한 이미지 사이즈와 최신 이미지 포맷 지원
  • 이미지 최초 요청 시 cache/images 폴더에 최적화한 이미지를 저장. 이 후 동일한 요청에 대해서는 이미 만들어진 이미지를 캐시로서 재사용.
  • 모든 이미지를 최적화하지는 않는다. (ex. svg, gif)
  • next.config.js의 deviceSizes와 imageSizes를 통해 자동적으로 srcset이 설정되어 뷰포트 너비에 따라 최적화된 이미지가 로드된다.
    • deviceSizes
      • 웹 사이트 사용자로부터 예상되는 장치의 width를 알고 있는 경우에 deviceSizes 프로퍼티를 사용하여 특정 장치 사이즈의 breackpoint를 지정할 수 있다. 이 width는 next/image 컴포넌트가 layout="responsive" 또는 layout="fill"을 사용할 때나 장치에 따른 올바른 이미지가 제공된다.
    • imageSizes
      • imageSizes 프로퍼티를 이용하여 특정 이미지 width 목록을 지정할 수 있다. 이러한 width는 deviceSizes의 배열과 연결되어 있으므로 deviceSizes배열에 정의 된 width와 달라야하고, 일반적으로 그 배열의 width보다 작아야 한다. 이 width는 next/image 컴포넌트가 layout="fixed"또는 layout="intrinsic"를 사용해야한다. 만약 따로 imageSizes 프로퍼티를 지정하지 않으면 아래 값이 default로 사용된다.next.config에서 deviceSizes 와 imageSizes 설정을 통해 srcset을 설정할 수 있다. 만약 아무것도 설정하지 않으면 기본값은 아래 코드와 같다.

기본값으로 설정되어 있는 이미지 srcset

  • module.exports = { images: { deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840], imageSizes: [16, 32, 48, 64, 96, 128, 256, 384], }, }

3. placeholder 제공을 통한 Web Vitals의 CLS 발생을 방지

  • 이미지가 로드될 때 자동으로 레이아웃 이동을 방지

4. sharp vs squoosh

  • 둘 다 다양한 크기와 형식의 이미지들을 (jpeg, png, webp, gif, avif) 더 작은 크기로, 웹에 진화적으로 변환해 주는 매우 빠른 속도의 모듈
  • 기본 내장 라이브러리는 squoosh 인데 Next.js 는 공식문서에서 sharp 라이브러리 활용을 적극 권장.
  • Sharp Missing In Production
  • sharp를 설치하면 기존 squoosh를 활용한 이미지 최적화 결과보다 훨씬 빠른 응답을 받을 수 있음.

 

img 태그와 Next/Image 를 쓴 경우 비교
sharp와 squoosh 성능 비교
naoda-cobalt에 sharp 라이브러리 설치 전
naoda-cobalt에 sharp 라이브러리 설치 후

  • 큰 차이는 아니지만, 이미지 사이즈와 로딩 시간 측면에서 조금의 차이가 있는 것을 확인할 수 있음.

샘플 코드

https://github.com/KunHwanAhn/next-image-opt-example

참고 자료

Web Vitals

Optimizing: Images

NEXT.JS의 이미지 최적화는 어떻게 동작하는가? | 올리브영 테크블로그

Next/Image를 활용한 이미지 최적화 | 카카오엔터테인먼트 FE 기술블로그