HTML을 이미지 변환 및 다운로드 - 트러블 슈팅
HTML을 이미지로 변환하고 다운로드하는 기능을 구현하면서 예상하지 못한 여러 문제를 겪었다.
이 글은 그 과정에서 발생했던 이슈와, 문제를 해결하기 위해 시도했던 방법, 그리고 최종적으로 얻게 된 인사이트를 기록하기 위한 것이다.
간단한 작업인줄 알았지만 아니었다
처음에는
html2canvas
라이브러리를 통해 DOM을 canvas로 변환한 뒤 dataUrl
을 생성하는것으로 간단하게 처리 할 수 있다고 생각했다.
하지만 실제 적용해보니 다운로드를 클릭하면 canvas 변환을 하는데 최소 2s ~ 2.5s 정도 많이 걸리면 3.5s 정도 이후 다운로드가 진행되니 UX 측면에서 좋지 않았다. 실제 팀 내 에서도 "개선할 수 있는 방법이 없을까?" 라는 의견이 있었다.어떤 방법들을 시도했나?
1. 변환 & 다운로드 로직 분리
가장 먼저 시도한 것은
변환과 저장 로직을 분리
하는 작업을 진행하였다.- 컴포넌트가 마운트 완료되면 useEffect내 변환 로직 실행을 통해 이미지
dataUrl
미리 생성
- 생성된
dataUrl
은 옵션 변경 시 재활용할 수 있도록 memoization 적용
- 다운로드 버튼 클릭 시에는 저장만 수행 이렇게 하니 다운로드 시점에서는 딜레이가 없었지만 변환에 걸리는 시간 자체는 줄어들지 않았다. 물론 memoization된 값이 재사용되는 경우에는 빠르게 동작했다.
2. 라이브러리 교체 (html2canvas -> modern-screenshot)
변환 타이밍을 바꿔도 근본적인 속도 차이는 없었다. 원인을 찾아보니 다음과 같은 이유였다.
- DOM이 복잡할수록 렌더링 시간이 증가
- 사용 중인 폰트, 이미지, 노드 수, CSS 복잡도 모두 성능에 영향
이러한 구조적인 한계 때문에
html2canvas
는 시간이 걸릴 수밖에 없었다. 대안으로modern-screenshot
라이브러리도 테스트해봤다. 이 라이브러리를 사용해본 이유는, 기존html2canvas
처럼 DOM을 수작업으로 canvas에 다시 그리는 방식이 아니라, 브라우저가 렌더링한 결과를SVG
,foreignObject
, CSS Paint API 등을 활용해 더 가볍게 캡처할 수 있다는 점에서 더 나은 성능을 기대했기 때문이다.
- 단순한 구조라면
html2canvas
보다 빠르고 가볍게 동작
- 렌더링이 끝난 후의 결과를 활용하니, 이론상 더 정확하게 캡처 가능
하지만 변환 방식이 달라도 복잡한 DOM 구조에 따른 성능 문제는 동일하게 존재했다.
결국 복잡한 DOM, 많은 리소스를 캡처할 때는 브라우저가 어떤 방식으로든 렌더링을 다시 계산해야 하기 때문에 큰 차이는 없었다.
3. 캡처 전용 폰트 적용 (Base64 사용)
추가로 시도한 것은 캡처 전용 폰트를 별도로 Base64로 임베딩하는 방식이었다.
- 폰트를 Base64로 인라인 처리해 렌더링 시간을 단축
- 이를 통해 렌더링 시간을 조금 개선할 수 있었다 이 방식은 번들 사이즈가 늘어나는 단점이 있었다. 이를 보완하기 위해 다음과 같은 추가 조치를 취했다.
- 사용중인 font의 subset을 생성해 Base64로 변환 (latin 및 숫자&특수문자만 포함)
- 실제 사용 중인
font-weight
만 Base64 인라인 적용 이렇게 하여 불필요하게 번들 사이즈가 커지는 문제를 최소화할 수 있었다.
4. 저장 이후 일부 UI가 깨지는 현상
저장된 이미지에서 일부 UI가 깨지거나 누락되는 현상이 간헐적으로 발생했다. 이 문제는 일부 요소를 SVG로 교체하면서 해결할 수 있었다.
리서치를 해보니
modern-screenshot
사용 시 발생하는 이유는 다음과 같았다.- 렌더링 타이밍 문제
modern-screenshot
은 내부적으로SVG
의foreignObject
를 사용해 캡처를 수행하는데, 해당 시점에 브라우저가 완전히 렌더링하지 않은 요소가 있으면 누락되거나 깨져 보일 수 있다.
- 폰트, 이미지 로드 상태 외부 폰트나 이미지가 완전히 로드되지 않은 상태에서 캡처가 실행되면, 기본 폰트로 대체되거나 이미지가 비어 보일 수 있다.
- CSS 특성에 의한 한계
foreignObject
기반 캡처 특성상 일부 CSS 효과 (filter
,box-shadow
,clip-path
등)가 정상적으로 반영되지 않는 경우가 있다.
정리
HTML을 이미지로 변환하고 다운로드하는 기능은 예상보다 훨씬 많은 변수에 영향을 받았다.
- 복잡한 DOM 구조, 많은 리소스, 복잡한 CSS는 변환 속도에 직접적인 영향을 준다.
- 폰트, 이미지 로드 상태에 따라 변환 결과가 달라질 수 있다.
- 렌더링 타이밍에 민감해, 완전히 렌더링된 상태에서 캡처가 필요하다.
- 중요한 요소는 SVG로 교체하면 안정적인 캡처가 가능하다.
modern-screenshot
은 구조가 단순할 경우html2canvas
보다 빠르게 동작하지만, 복잡한 구조에서는 근본적인 성능 차이는 크지 않았다. 결국 품질과 속도를 위해서는 DOM을 단순화하고, 캡처 전 상태를 철저히 준비하는 것이 중요하다는 점을 다시 한 번 느꼈다.