[Web] avif와 webp 포맷을 사용하여 이미지 최적화하기

[Web] avif와 webp 포맷을 사용하여 이미지 최적화하기

2023-03-24 hit count image

Python을 사용하여 avif와 webp 포맷의 이미지를 생성하여 Lighthosue의 Serve images in next-gen formats 문제를 해결하는 방법에 대해서 알아보겠습니다. 또한, lazy loading을 사용하여 이미지를 불러와 Defer offscreen images 문제를 해결하는 방법에 대해서도 알아봅니다.

개요

GoogleChrome 브라우저에서는 웹 페이지의 성능을 측정할 수 있는 Lighthouse라는 기능을 제공하고 있습니다. 이 기능을 사용하면 웹 페이지의 전반적인 성능을 확인할 수 있고, 다음과 같이 문제점도 확인할 수 있습니다.

Optimization images - Lighthouse serve images in next-gen formats issue of images

이번 블로그 포스트에서는 웹 페이지에서 이미지 포맷을 최적화하여 Serve images in next-gen formats 문제를 해결하고 이미지의 lazy loading으로 Defer offscreen images 문제를 해결하는 방법에 대해서 알아보겠습니다.

avif와 webp 포맷

Serve images in next-gen formats 문제를 해결하기 위해서는 avif 포맷 또는 webp 포맷의 이미지를 제공할 필요가 있습니다. avifwebp 포맷의 이미지는 IE를 제외한 대부분의 모던 브라우저에서 지원하는 이미지 포맷입니다.

Python으로 avif와 webp 포맷 이미지 생성

이번 블로그 포스트에서는 jpg 또는 png 포맷의 이미지 파일을 Python을 사용하여 avifwebp 포맷의 이미지를 생성하는 방법에 대해서 알아보도록 하겠습니다.

Python을 사용하여 avif 또는 webp 포맷의 이미지를 생성하기 위해서는 Pillowpillow-avif-plugin 라이브러리를 설치할 필요가 있습니다. 다음 명령어를 사용하여 Pillowpillow-avif-plugin 라이브러리를 설치합니다.

pip install Pillow pillow-avif-plugin

PillowPython에서 이미지를 다루기 위한 라이브러리이며, pillow-avif-pluginPillow 라이브러리를 사용하여 이미지의 포맷을 변경할 때, avif 포맷으로 저장할 수 있게 도와주는 라이브러리입니다.

라이브러리를 설치했다면, optimization_images.py 파일을 생성하고 다음과 같이 수정합니다.

import os
import glob
from PIL import Image
import pillow_avif

target_folder = './assets'
files = glob.glob(f'{target_folder}/**/*.jpg', recursive=True) + glob.glob(f'{target_folder}/**/*.png', recursive=True)

for f in files:
    print(f)
    title, ext = os.path.splitext(f)
    webp_file = title + '.webp'
    avif_file = title + '.avif'

    if os.path.isfile(webp_file) == False:
        img = Image.open(f)
        img.save(webp_file, format='webp')
        img.close()

    if os.path.isfile(avif_file) == False:
        img = Image.open(f)
        img.save(avif_file, format='AVIF' )
        img.close()

그럼 소스 코드를 조금 더 자세히 살펴보도록 하겠습니다.

저는 이미지 파일들을 assets 폴더에 저장하고 있습니다. 만약 이미지 파일을 다른 폴더에 저장하고 있다면 다음 부분을 수정하시기 바랍니다.

...
target_folder = './assets'
...

이미지를 저장한 폴더안에서 모든 jpgpng 포맷의 파일을 찾습니다.

...
files = glob.glob(f'{target_folder}/**/*.jpg', recursive=True) + glob.glob(f'{target_folder}/**/*.png', recursive=True)
...

그런 다음 모든 파일을 루프를 돌며 파일 포맷을 변경합니다. 파일 포맷을 변경하고 포맷에 맞게 파일명도 변경하기 위해 os.path를 사용하여 파일명을 준비하였습니다.

...
for f in files:
  title, ext = os.path.splitext(f)
  webp_file = title + '.webp'
  avif_file = title + '.avif'
  ...

그런 다음, webp 포맷의 파일이 존재하는지 확인하고, 존재하지 않는 경우, 기존 파일을 webp 포맷으로 변경하여 저장합니다.

...
for f in files:
    ...
    if os.path.isfile(webp_file) == False:
        img = Image.open(f)
        img.save(webp_file, format='webp')
        img.close()
    ...

마찬가지로, avif 포맷의 파일이 존재하는지 확인하고, 존재하지 않는 경우, 기존 파일을 avif 포맷으로 변경하여 저장합니다.

...
for f in files:
    ...
    if os.path.isfile(avif_file) == False:
        img = Image.open(f)
        img.save(avif_file, format='AVIF' )
        img.close()

이것으로 jpg 또는 png 포맷의 이미지 파일을 avifwebp 포맷으로 변경하는 Python 스크립트를 제작했습니다.

avif와 webp 파일 생성

그럼 이렇게 만든 optimization_images.py 파일을 실행하여 avifwebp 포맷 파일을 생성해 봅시다. avifwebp 포맷 파일을 생성하기 위해 다음 명령어를 실행하여 Python 스크립트를 실행합니다.

python optimization_images.py

그럼 다음과 같이 이미지 파일 리스트를 확인할 수 있으며, avifwebp 포맷 파일이 생성되는 것을 확인할 수 있습니다.

...
./assets/images/category/flutter/2023/RefreshIndicator/refreshindicator_new_data_with_no_data.png
./assets/images/category/flutter/2023/RefreshIndicator/refreshindicator_pull_to_refresh.png
./assets/images/category/flutter/2023/RefreshIndicator/refreshindicator_no_data.png
./assets/images/category/flutter/2023/RefreshIndicator/listview.png
./assets/images/category/flutter/2023/RefreshIndicator/refreshindicator_refresh_no_data.png
./assets/images/category/flutter/2023/find_child_and_parent_widget/basic_app.png

picture와 source 태그

그럼 이렇게 생성한 avifwebp 포맷 파일을 웹 페이지에서 사용하는 방법에 대해서 알아보도록 하겠습니다. 웹 페이지에서 여러 포맷의 이미지를 사용하기 위해서는 <picture /> 태그와 <source /> 태그를 사용할 필요가 있습니다. 이미지를 불러오는 부분을 다음과 같이 <picture /> 태그와 <source /> 태그를 사용하여 avifwebp 포맷을 불러오도록 수정합니다.

<picture>
  <source srcset="example.avif" type="image/avif" />
  <source srcset="example.webp" type="image/webp" />
  <img src="example.png" alt="example file" />
</picture>

이렇게 <picture /><source /> 태그를 사용하면, 브라우저가 지원하는 포맷의 이미지를 다운로드하여 표시하게 됩니다.

loading lazy

Lighthouse를 사용하여 이미지가 많은 웹 페이지를 검사하면, Defer offscreen images 문제를 발견할 수 있습니다. 이 문제를 해결하기 위해서는 <img /> 태그에 다음과 같이 loading="lazy"를 추가합니다.

<picture>
  <source srcset="example.avif" type="image/avif" />
  <source srcset="example.webp" type="image/webp" />
  <img src="example.png" alt="example file" loading="lazy" />
</picture>

loading lazy에 관한 자세한 정보는 다음 링크에서 확인하실 수 있습니다.

완료

이것으로 웹 페이지 성능 향상을 위해 새로운 이미지 포맷인 avifwebp 파일을 생성하고, <picture /><srouce /> 태그를 사용하여 이미지 로딩을 최적화하는 방법에 대해서 알아보았습니다. 또한 loading="lazy"을 사용하여 이미지 로딩을 최적화 하는 방법에 대해서도 알아보았습니다.

Lighthouse에서 자주 발생하는 지적 사항이므로 이번 기회에 잘 기억해 두면 좋을 거 같습니다. 혹시 브라우저가 아닌 로컬이나 CI 환경에서 Lighthouse를 실행시키는 방법에 대해 궁금하신 분들은 아래 링크를 참고하시기 바랍니다.

제 블로그가 도움이 되셨나요? 하단의 댓글을 달아주시면 저에게 큰 힘이 됩니다!

앱 홍보

책 홍보

스무디 한 잔 마시며 끝내는 React Native 책을 출판한지 벌써 2년이 다되었네요.
이번에도 좋은 기회가 있어서 스무디 한 잔 마시며 끝내는 리액트 + TDD 책을 출판하게 되었습니다.

아래 링크를 통해 제가 쓴 책을 구매하실 수 있습니다.
많은 분들에게 도움이 되면 좋겠네요.

스무디 한 잔 마시며 끝내는 React Native, 비제이퍼블릭
스무디 한 잔 마시며 끝내는 리액트 + TDD, 비제이퍼블릭
Posts