Next.js를 통해 이미지 최적화 알아보기

dahyeon·2023년 1월 7일
2

이미지는 용량이 크기 때문에 이러한 이미지들을 여러 개 불러와야 하는 상황에서 최적화를 거치지 않고 사용하게 되면 로딩시간이 너무 오래 걸려 사용자 경험을 해칠 수 있다. 본 글에서는 Next.js가 사용하고 있는 방법을 자세히 살펴봄으로써 이미지 최적화 방법에 대해 알아볼 것이다.

Next.js의 이미지 최적화 방법

Next.js의 공식문서(링크)를 보면, Next.js에서는 아래와 같은 이미지 최적화 기능을 제공한다고 나와있다.

  • Improved Performance: 적절한 이미지 포맷을 사용하고, 각 디바이스에 맞는 사이즈의 이미지를 제공
  • Visual Stability: Cumulative Layout Shift를 방지
  • Faster Page Loads: 이미지가 뷰포트 내에 들어올 때에만 로딩
  • Asset Flexibility: 원격 서버에 저장된 이미지라도 리사이징해서 제공

어떻게 각 기능을 제공하는지 하나씩 살펴보자.


Visual Stability: Cumulative Layout Shift

Cumulative Layout Shift(CLS)란?
사이트의 레이아웃 안정성(layout stability)을 측정하기 위한 지표이다.

  • 사용자가 서비스를 이용하던 도중 갑자기 레이아웃이 바뀌면 사용자 경험을 해칠 뿐만 아니라 실제 피해를 줄 수도 있다.
  • 이러한 layout shift는 일반적으로 DOM 요소가 초기 렌더링된 이후 동적으로 추가되거나, 리소스가 비동기식으로 로드되기 때문에 발생한다.
  • CLS는 core web vitals를 구성하는 3가지 요소 중 하나로, SEO에도 중요한 의미를 갖는다.

이미지의 크기를 지정해주지 않으면 이미지가 로딩된 후에 그 크기만큼 layout shift가 발생하게 된다.

이미지의 너비와 높이를 지정해준다면 그 크기만큼 공간을 확보하여, 이미지가 로딩된 후에도 layout shift가 발생하지 않는다.

Next.js의 Image 태그를 사용하면

  • 로컬 이미지의 경우, 이미지의 너비와 높이를 자동으로 파악해서 지정해준다.
  • 원격 이미지의 경우 너비와 높이를 무조건 지정해줘야 한다. (지정해주지 않으면 에러가 발생한다.)

Faster Pageloads: Lazy Loading

Lazy Loading이란?
현재 뷰포트에 표시되고 있지 않은 이미지의 경우 로딩을 미루다가 이미지가 필요한 시점(=이미지가 뷰포트 내에 들어오는 시점)에 로딩하도록 하는 기법을 image lazy loading 기법이라고 한다.

Lazy loading을 적용하려면 img 태그에 다음과 같이 loading="lazy"를 작성해주면 된다.

<img src="image.jpg" alt="..." loading="lazy" />

실제 next/image가 적용된 img 태그에도 위 속성이 포함되어 있다.

<img alt="thumbnail" srcset="...(중략)" 
  width="280" height="200" decoding="async" 
  data-nimg="1" loading="lazy" style="color: transparent;">

Improved Performance & Asset Flexibility: WebP 포맷으로 이미지 변환

이미지의 크기가 클수록 로딩하는 데 시간이 오래걸린다. WebP 파일 포맷을 사용하면 이미지의 품질은 유지하되 파일 크기를 줄일 수 있다.

WebP란?
WebP은 인터넷에서 이미지가 로딩되는 시간을 단축하기 위해 Google이 출시한 파일 포맷이다. WebP를 사용하면 웹 사이트에서 고품질 이미지를 표현할 수 있지만 png, jpeg 등 기존 포맷보다 파일 크기가 작아진다.
무손실 WebP 이미지가 PNG보다 최대 26%까지 줄어들 수 있다고 한다.

Next.js에서는

최초 이미지 요청 시 사용자의 디바이스에 맞는 이미지 사이즈를 만들고, webp 포맷으로 변환하여 이를 캐시(/cache/images 디렉토리)에 저장한다. 이후 요청이 들어온 이미지가 캐시에 있으면 캐시 된 이미지를 제공한다. 만약 캐시가 만료된 후 요청이 들어오면 오래된 이미지를 우선 제공하고 백그라운드에서 이미지 최적화를 다시 진행한다.

next/image가 적용된 img 태그를 살펴보자.

<img alt="thumbnail" srcset="/_next/image?url=https(...).png&amp;w=384&amp;q=75 1x, 
/_next/image?url=https(...).png&amp;w=640&amp;q=75 2x" 
src="/_next/image?url=https(...).png&amp;w=640&amp;q=75" 
width="280" height="200" decoding="async" 
data-nimg="1" loading="lazy" style="color: transparent;">

여기서 srcSetsrc에 주목하면,

  • src값은 원본 이미지 url과 차이가 있는데, 원본 이미지를 Next에서 변환하여 저장한 이미지의 주소가 들어간 것을 알 수 있다.

  • srcSet

    srcSet을 자세히 보면 "url1 1x, url2 2x"와 같은 형태로 구성되어 있는 것을 알 수 있는데, 이는 화면의 해상도에 따라 다른 이미지를 서빙한다는 뜻이다.

    즉, 낮은 해상도(1x)에서는 높은 해상도(2x)보다 사이즈가 더 작은 이미지를 사용하도록 하였다.

    ✔️ srcSet 속성을 sizes 속성과 함께 사용하면 반응형으로 디바이스 크기에 맞는 이미지를 서빙하게끔 설정할 수도 있다.


🤔 생각해볼만한 점

이 포스팅을 작성하게 된 이유는 Next.js를 사용하고 있는 프론트엔드 서버에서 메모리 사용량이 1GB가 넘게 치솟는 현상이 발생했기 때문이다. 이는 이미지 변환 관련 라이브러리인 sharp를 설치한 이후 어느 정도 해결이 되었다(1GB → 100MB대로 감소). 아마 next.js가 이미지 최적화 작업을 하면서 메모리 누수가 발생했기 때문으로 추정되는데(관련 링크), 해결은 되었지만 내부 동작을 이해해야 될 필요성을 느끼게 해주었다.

해당 프로젝트에서는 크기와 너비가 고정된 썸네일 이미지가 존재하는데, 이러한 이미지의 경우 서빙할 때마다 Next에서 변환해줄 것이 아니라, 저장할 때 서버에서 이미지를 변환하여 저장하는 것이 훨씬 효율적일 것으로 보인다. 이를 적용한 개선 작업은 추후 포스팅을 할 예정이다.


참고자료
Cumulative Layout Shift (CLS)
Optimize Cumulative Layout Shift
next/image | Next.js
이미지 리사이징을 통해 웹 성능 개선하기
Next/Image를 활용한 이미지 최적화

profile
https://github.com/dahyeon405

0개의 댓글

관련 채용 정보