웹페이지를 이미지로 저장하는 기능을 구현하게 되었다. 하지만 언제나 복병인 ios/safari에서 웹페이지 내부에 자리하고 있는 이미지 요소가 누락되어 저장되는 이미지가 발생했고, 이를 해결한 과정을 기록해보려고 한다.
우선 나는 이미지화를 위해 html-to-image
라이브러리를 활용했다.
html-to-image의 동작 방식부터 알아보자.
<img>
로 만든 후 <canvas>
에 그림toBlob()
으로 추출내가 파악한 이슈의 원인은 다음과 같다.
Safari는 html-to-image로 DOM을 캡쳐할 때, 외부 이미지나 blob URL을 렌더링 직전에 비동기로 불러온다.
렌더 타이밍에 따라 이미지가 GPU 레이어에 올라가기 전에 toBlob이 실행될 수 있다.
👉 이로 인해 간헐적으로 canvas에 그림을 못 그리고, 빈 blob이 생성된 채로 캡쳐 이미지가 생성된 것이었다.
💡 Safari는 DOM에 그려진 것처럼 보여도, 내부적으로 GPU 레이어에 실제 이미지나 폰트가 올라오지 않은 상태일 수 있다.
💡 Safari는 리소스 최적화를 위해 백그라운드 탭에서의 이미지 로딩을 지연시키거나, 크로스-오리진 이미지에서 CORS 정책을 더 엄격하게 적용하기도 한다.
구글링해 본 결과 html-to-image 라이브러리에도 이미 해당 이슈가 등록 되어 있었고, 여러가지 방법을 시도해보았지만 이슈를 해결할 수 있었던 건 Blob 사이즈를 검사하는 방식이었다.
✨ Blob의 사이즈를 확인하여 이미지 요소가 정상적으로 렌더되었는지 확인 후 이미지 저장을 시도하도록 시점을 변경하였다.
⇒ Safari가 GPU 렌더링까지 끝낼 수 있도록 시간을 벌면서 반복 재시도
⇒ 이미지가 완전히 그려진 후 캡쳐됨으로써 이미지 누락 문제 해결
방법 | 설명 |
---|---|
await document.fonts.ready | 폰트 렌더 대기 후 완료 확인 |
await requestAnimationFrame() | DOM이 GPU에 올라갈 시간 확보 |
프로젝트 내부 public 이미지 사용 | CORS 대응을 위해 외부 이미지 대신 로컬 이미지 사용 |
pixelRatio는 최대 2로 설정 | GPU 병목 방지 |
Blob size 체크 & 반복 재시도 | 이미지 요소 정상 렌더 완료 확인 |
Summary
Safari가 이미지/폰트 렌더링을 완료하기 전에 캡쳐가 시도되어 이미지가 누락 되는 이슈 발생. 정상적으로 렌더링된 Blob의 사이즈인지 검사 후 캡쳐 시도하도록 개선.