[js] clip-path를 지원하는 이미지 다운로드 만들기(DOM을 이미지로)

·2025년 1월 27일
0

개발 기록

목록 보기
69/69

서비스에 HTML로 구현된 컴포넌트를 이미지로 다운로드할 수 있는 기능이 있다.
기존 코드는 html2canvas 라이브러리를 사용해 구현했었다.
그런데 최근 디자인 타입을 추가하며 기능을 테스트했더니 정상적으로 동작하지 않았다.🔥

문제

html2canvas

기존에 html2canvas 라이브러리를 선택했던 이유는 다음과 같다.

- html-to-image : 서체가 적용되지 않은 채로 이미지로 변환 됨
- dom-to-image : 사파리에서 이미지가 출력 안 됨, 이슈에 동일한 내용 많음, 리드미에 사파리 지원 안 한다고 적혀있음
- dom-to-image-more : dom-to-image와 동일하게 사파리 지원 안 한다고 적혀있음

많이 언급되는 라이브러리들을 검토했을 때 위와 같은 문제가 있었다.
html2canvas는 크기가 크다는 단점이 있지만, 이는 문제가 되지 않는다고 느껴졌고 테스트 결과 변환 속도도 빨랐다. 그리고 중요한 건 사파리를 무조건 지원해야 했다.

새로운 디자인에는 클리핑 패스 기능을 사용해 이미지를 하트 모양으로 크롭한 부분이 있는데, 이는 SVG의 clipPath 기능으로 구현했다.

			<div className='photo__box'>
				<svg
                    xmlns="http://www.w3.org/2000/svg"
                    viewBox="0 0 300 318"
                    className="mask-svg"
                  >
                    <defs>
                      <clipPath id="frame">
                        <path
                          fillRule="evenodd"
                          clipRule="evenodd"
                          d="M150 50.599C138.49..."
                        />
                      </clipPath>
                    </defs>
                  </svg>

                  <div className="image-wrapper">
                    <img
                      src={imageUrl}
                      alt="메인사진"
                    />
                  </div>
               </div>
          photo__box {
            clip-path: url(#frame);
            -webkit-clip-path: url(#frame);
           }

문제가 된 부분은 아래의 왼쪽처럼 하트 모양이 있는 부분을 clip-path 방식으로 구현했는데, 이미지로 다운로드하면 적용되지 않은 채로 다운로드되었다.

해결

modern-screenshot

dom-to-image로 테스트해보니 하트 모양은 정상적으로 이미지로 변환되었지만, 크롬에서만 동작했다.
역시나 사파리에서 정상적으로 동작하지 않았으며, 깃허브 이슈를 찾아봐도 해결 방법을 찾지 못했다.
그러던 중 발견한 한 개의 이슈:

Most of the issues have been fixed in forked library modern-screenshot


npm에 검색해보니 생각보다 다운로드 수도 많음... clip-path가 정상적으로 적용되는 것도 확인했다.

modern-screenshot의 문제

이미지로 변환이 잘되어 이 라이브러리를 사용하려 했으나, 이후에도 여러 문제를 해결해야 했다.

사파리에서 발생하는 이슈1

dom-to-image에서는 이미지 리소스를 다운로드하지 못하는 이슈가 있었는데, 이 라이브러리도 동일한 이슈가 있었다.

dom-to-image의 깃허브 이슈에서 두 번 실행하라는 내용이 많았는데, 그땐 동작하지 않았으나 modern-screenshot에서는 두 번 실행하면 정상적으로 작동했다.
그런데 두 번 실행하니까 속도가 느려진다.. 그래서 원래 4가지 이미지를 한 번에 다운받게 했었는데 필요한 사진만 각각 다운받을 수 있도록 수정했다.

?

사파리에서 리소스를 다운로드할 때 시간이 오래 걸리며 생기는 오류라는 것 같은데, 내부적으로 수정할 수 없어 이렇게 해결해야 하는 건가? 싶지만 왜 두 번 실행하면 제대로 동작하는지는 알 수 없었다.

사파리에서 발생하는 이슈2

문제 1을 해결하고 나니 사파리에서 Not allowed to load local resource라는 에러가 발생하며 이미지가 정상적으로 변환되지 않는 이슈가 발생했다.

그런데 어떤 디자인 타입은 정상적으로 동작하고, 어떤 타입은 동작하지 않았다.
오류에 적힌 data URL이 SVG라 동작하지 않는 케이스와 동작하는 케이스를 추려 사용된 SVG의 차이점을 비교했지만, 원인을 알 수 없었다.

결국 동작하지 않는 케이스의 코드를 하나씩 지워본 결과, SVG가 아닌 일반 텍스트 요소를 지우니 다시 동작했는데 알고 보니 Pretendard 서체를 사용하는 디자인 타입만 동작하지 않는 것이었다.

@import url('https://fastly.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard-dynamic-subset.min.css');

@import는 브라우저가 외부 CSS 파일을 가져오도록 요청하는 방식인데, 이 요청이 CORS 정책의 영향을 받습니다. 만약 Pretendard의 호스트(여기서는 fastly.jsdelivr.net)가 올바른 CORS 헤더를 설정하지 않았다면, 브라우저가 해당 리소스를 차단할 수 있습니다.
리소스를 로드하지 못하거나 403(Forbidden) 또는 다른 HTTP 오류가 발생합니다.
출처: GPT

global.css에 프리텐다드 서체가 @import 방식으로 불러오고 있었는데 이 부분이 이미지로 변환 시 브라우저 보안 문제가 발생한 것 같다. 해당 부분을 @font-face 방식으로 수정해 오류를 해결할 수 있었다.

?

GPT가 css 파일을 불러오는 방식으로 되어있어 이미지 변환 시 문제가 발생한 거라고 하는데 CSS 파일을 불러오는 것과 폰트 파일 경로로 불러오는 게 무슨 차이가 있는 걸까..?

모바일 크롬에서 발생하는 이슈

모바일 크롬에서 텍스트가 줄바꿈되어 버리는 문제가 있었는데, 이는 white-space: nowrap;을 사용해 해결할 수 있었다.
(나는 줄바꿈이 필요 없었지만 여러 줄의 텍스트인 경우 이 방법으로는 해결할 수 없지 않을까?)

마무리

html2canvas를 사용할 때보다 변환 속도가 느려진 점은 아쉬웠지만, 앞으로 추가될 수 있는 다양한 디자인 타입을 고려했을 때 꼭 해결해야 하는 과제였다.

이 과정에서 개별 다운로드 방식으로 UI를 수정하며 고객들에게 불편함을 줄 수 있을까 걱정했지만, 오히려 필요한 이미지만 선택적으로 다운로드할 수 있는 기능으로 개선되면서 예상치 못한 긍정적인 결과를 얻을 수 있었다.👍

0개의 댓글

관련 채용 정보