Image Decoding

#image#ui optimzation#performance
Image Decoding

페이지에 고해상도 이미지를 출력할 때 img요소는 본문에 삽입되었지만, 이미지가 바로 보이지 않고 잠시 후에 출력되는 현상을 본 적 있을 것이다.

이는 이미지 다운로드가 완료되었지만, 아직 디코딩이 되기 전이라 페이지에 나타나지 않는 현상이다.

요소 추가와 동시에 이미지가 보여지길 원한다면 아래 코드를 사용해야 한다.

const img = new Image()

img.src = '...'

img.decode().then(() => {
  document.body.appendChild(img)
})

이미지 디코딩

jpg, png, webp 등 대부분의 이미지 파일들은 기본적으로 인코딩이 되어 있다. 쉽게 말하자면 압축이 되어 있는 것이다. 브라우저는 서버로부터 응답받은 이미지들을 화면에 그리기 위해 비트맵으로 디코딩. 즉 압축을 해제한다.

디코딩 결과는 브라우저가 알아서 캐시 했다가 화면에 그려야 할 때 알아서 메모리에 올리고. 화면에서 안 보이면 다시 해제하는 식으로 동작한다.

디코딩 시간에 영향을 주는 요소들

먼저 CPU와 RAM에 영향을 많이 받으므로. 모바일에서 성능 감소가 두드러질 수 있다.

제일 큰 영향을 주는 요소는 이미지의 크기이다. 크기가 큰 이미지일수록 디코딩 시간이 많이 소요되므로. 서버에서 적절한 크기의 이미지를 서빙하는 것이 매우 중요하다.

이미지 포멧 역시 중요하다. jpg나 png들은 오랫동안 사용되었기 때문이 이미 다양한 플랫폼에서 사용 가능한 효율적인 디코더(압축 해제 프로그램으로 이해하면 쉽다)들이 존재하지만, 비교적 최신 포멧인 jpeg-xr이나 webp의 경우 아직 그렇지 않으므로 디코딩 시간이 증가할 수 있다.

호텔, 호스텔 등 다양한 숙박시설의 요금을 비교하는 서비스인 trivago 에서는 고객으로부터 jpeg-xr 포멧에 관련된 성능 이슈를 접수했었다고 한다.

브라우저의 휴리스틱 알고리즘을 통해 일부는 GPU를 사용하여 디코딩되기도 하고. 이미지를 실제로 그려내기 위해서도 GPU를 사용하고 있기도 하다.

장축 3000정도 되는 크기의 이미지를 png, webp포멧으로 디코딩 시간을 측정해보았더니. png가 8ms, webp가 12ms 정도로 증가하였다 (M2 macbook pro 에서 측정)

각 포멧별로 하드웨어 자체에서 디코더를 제공하거나 그렇지 않아 소프트웨어 디코더를 적용해야 하는 등. 브라우저마다 차이가 있으므로 결정 전에 한번 찾아 보길 바란다.

이미지 디코딩 시간 측정하기

  1. 크롬 개발자 도구의 Performance탭에서 새로고침 측정으로 확인 가능.
  2. webpagetest의 측정 결과를 chrome://tracing페이지에서 로드하여 확인 가능.

decoding 속성을 활용한 비동기 디코딩

이미지 디코딩은 메인 쓰레드 혹은 래스터라이징 쓰레드를 사용한다. 해당 쓰레드에 여러 작업들이 할당될 것인데. 당연하게도 이미지 디코딩이 오래 걸리면 다른 작업들은 지연된다.

다른 이미지의 디코딩 뿐만 아니라, 화면에 텍스트를 출력하는 작업들도 지연될 수 있고. 60fps로 밑돌게 되면 사용자는 끊기는 느낌을 받게 된다. 이 때 이미지 디코딩을 비동기 처리한다면 도움이 될 수 있다.

decoding 지정 전
decoding 지정 전

위 이미지는 고해상도 이미지의 decoding을 적용하기 전의 측정 결과인데. 이미지 디코딩 과정으로 래스터라이저 쓰레드 1번의 다른 작업들이 밀린것을 확인할 수 있다.

여기서 img태그에 decoding=async를 할당할 경우 아래와 같은 측정 결과를 확인할 수 있다

decoding=async 지정 후
decoding=async 지정 후

개선 후의 측정 결과에서 디코딩 과정이 별도의 프로세스로 분리되어 다른 태스크들이 방해되지 않는 것을 볼 수 있다.

.decode() 메서드를 활용한 predecoding

서론에서 언급한 문제는 주로 이미지 갤러리등 이미지가 주요 컨텐츠이거나, 스크롤 등에 따른 점진적인 이미지 렌더링 과정에서 두드러지며 UX에 악영향을 끼칠 수 있다.

이 때 앞서 언급한 코드와 같이 .decode()를 호출해 디코딩이 완료된 후 본문에 삽입할 경우 이런 문제들을 해결할 수 있다.

next.js의 이미지 컴포넌트의 decode적용를 참고해서 필요에 따라 서비스에 구현하도록 하자.


참고

  1. Don’t use JPEG-XR on the Web jpeg-xr 은 소프트웨어 디코더를 사용하다 보니 렌더링 성능에 악영향이 있다고 함.
  2. Best practices from open source: Use img.decode() in image-heavy applications