Ari.dev

썸네일을 저장할 때마다 가운데 선이 하나 생겼다 — html2canvas 캔버스 좌표 결함

브라우저에서 만든 썸네일을 PNG로 저장하면 이미지 정중앙에 원치 않는 가로선이 렌더링됐다. 도구의 핵심 산출물이 손상되는 결함을, 증상을 가리는 대신 캔버스 렌더 좌표라는 근본 원인까지 따라가 고친 과정.

트러블슈팅

관련 프로젝트: blogsumnail

썸네일 메이커의 핵심 기능은 "만든 이미지를 PNG로 저장한다"는 한 문장이다. 그런데 그 저장 결과물 정중앙에 원치 않는 가로선이 찍혀 나왔다. 편집 화면에서는 멀쩡한데, 다운로드한 파일에만 나타났다.

문제 — 화면은 정상, 산출물만 깨진다

이 결함이 까다로웠던 이유는 화면에서 보이는 상태와 저장된 파일의 상태가 달랐다는 것이다. 편집 중인 미리보기는 정상으로 렌더된다. 다운로드 버튼을 눌러 html2canvas로 캡처한 PNG에만 정중앙에 가로선이 생긴다.

화면이 정상이니 눈으로만 봐서는 뭐가 문제인지 짐작하기 어렵다. 문제는 렌더링 결과 자체가 아니라, 캡처 시점에 캔버스가 좌표를 읽는 방식에 있었다.

원인 추적 — 증상이 아니라 좌표를 의심한다

가로선이 "정중앙"에 정확히 생긴다는 점이 단서였다. 무작위 위치가 아니라 일정한 지점에 매번 나타난다는 건, 렌더링이 깨진 게 아니라 어떤 계산이 특정 지점에서 어긋나고 있다는 뜻이다.

두 가지를 의심했다.

  • 스크롤 오프셋 — html2canvas는 캡처 시점의 스크롤 위치를 기준으로 좌표를 계산한다. 캡처 대상 요소가 뷰포트 안에서 스크롤된 상태였다면, 캔버스가 읽는 좌표와 실제 요소의 좌표가 어긋난다.
  • aspect-ratio — CSS aspect-ratio로 크기가 계산되는 요소는, 브라우저가 실제 렌더링 크기를 결정하는 시점과 html2canvas가 그 크기를 읽는 시점이 미묘하게 어긋날 수 있다.

이 두 지점을 캡처 직전에 보정했다. 스크롤 오프셋을 명시적으로 0으로 맞추고, aspect-ratio 대신 캡처 시점엔 계산된 고정 크기를 직접 넣어 브라우저가 다시 계산할 여지를 없앴다.

왜 증상만 가리지 않았나

정중앙 가로선을 없애는 손쉬운 방법도 있었다 — 결과 이미지 정중앙에 덧그리기를 해서 가리는 식이다. 하지만 그건 원인을 모른 채 결과만 손보는 것이고, 다른 비율의 썸네일이나 다른 레이아웃에서 같은 결함이 다른 위치에 나타날 수 있다는 뜻이기도 하다.

캔버스가 좌표를 잘못 읽는 근본 원인을 고치니, 정중앙이라는 특정 케이스뿐 아니라 좌표 오차로 생길 수 있는 다른 위치의 결함까지 함께 사라졌다.

또한 html2canvas 자체는 npm 의존성으로 번들에 포함하지 않고 CDN(v1.4.1)으로 afterInteractive 시점에 지연 로드했다 — 썸네일을 저장하는 사용자만 필요한 기능이라, 모든 방문자의 초기 로드에 이 무게를 지울 이유가 없었다.

정리

구분beforeafter
저장된 PNG정중앙에 가로선가로선 없이 정상
원인 대응(고려했던 대안) 결과물에 덧그려 가림캔버스 좌표 계산 자체를 보정
스크롤 오프셋캡처 시점 상태 그대로 사용캡처 직전 명시적으로 정렬
aspect-ratio브라우저 재계산 시점에 의존캡처 시점엔 고정 크기로 대체

버그가 "항상 같은 위치"에 나타난다는 건 무작위 결함이 아니라 계산 로직 어딘가가 일정하게 어긋나고 있다는 신호다. 위치의 규칙성을 먼저 관찰한 것이, 증상을 가리는 대신 원인을 좁혀가는 첫 단서였다.

댓글