[글마디 프로젝트] 글마디 카드 PNG 다운로드 및 공유 기능 구현 회고

Minsu Han·2023년 5월 9일
0

Side Projects

목록 보기
6/10

시작하며

글마디 프로젝트를 기획하면서 유저가 자신이 기록해 두고 싶은 구절을 업로드하는 것 뿐만 아니라 카드 형태의 사진으로 저장하기도 하고, 주변 사람들에게 공유하기도 할 수 있다면 좋을 것 같다는 생각을 했습니다.

이 글은 자바스크립트의 blob과 html-to-image, downloadjs 라이브러리를 활용하여 글마디 카드를 PNG 파일로 다운로드하고 사진을 외부에 공유하는 기능을 구현한 회고입니다.

카드 요소 구성

위 사진은 글마디 목록 중 하나를 선택했을 때 표시되는 카드입니다.
카드 요소는 card, 왼쪽 하단의 3개의 아이콘들을 묶은 요소는 tabs, 다운로드 아이콘은 download, 종이비행기처럼 생긴 공유 아이콘은 share라고 클래스 이름을 지정했습니다.

PNG 다운로드 구현 (html-to-image, downloadjs 라이브러리)

DOM 요소를 이미지로 변환해주는 라이브러리를 찾아보니 html2canvas와 html-to-image 두 라이브러리를 주로 사용하는 것 같았습니다. 둘 중에 html-to-image의 공식 문서가 저에게 좀 더 쉽게 설명되어있어서 써보기로 했습니다.

html-to-image
https://www.npmjs.com/package/html-to-image

사용방법은 간단했습니다. html-to-image의 toPng() 메서드에 DOM node 객체를 전달하면 base64로 인코딩된 data URL을 만들어줍니다. 이 dataURL을 가지고 dataURL을 src로 하는 img 노드로 만들거나, downloadjs라는 라이브러리의 download() 메서드 인자로 전달하여 PNG 파일로 다운로드할 수 있습니다.

downloadjs
https://github.com/rndme/download

html-to-image는 이미지로 변환하려는 DOM 요소의 child 요소들 중 이미지로 변환하고 싶지 않은 요소를 필터링할 수 있습니다. toPng() 메서드의 filter 파라미터 값으로 boolean 값을 반환하는 콜백 메서드를 전달하면 됩니다. 아래는 i 태그 요소를 제외하고 DOM 요소를 SVG 파일로 변환하는 예시입니다.

이를 참고하여 다운로드 버튼('.download')을 클릭하면 글마디 카드('.card')를 이미지(dataURL)로 변환하고, 다운로드하되, 카드 왼쪽 하단의 세 아이콘들('.tabs')은 제외하는 코드를 작성하였습니다.

import download from 'downloadjs';
import * as htmlToImage from 'html-to-image';

/* 이미지 다운로드 (html-to-image, downloadjs library) */
  async #downloadImage() {
    document.querySelector('body').addEventListener('click', e => {
      if (e.target.closest('.download')) {
        // 카드 DOM을 png로 다운로드 (아이콘 제외)
        const node = document.querySelector('.card');
        htmlToImage
          .toPng(node, {
            filter: node => node.className !== 'tabs',
          })
          .then(dataUrl => {
            download(dataUrl, 'geulmadi.png');
          })
          .catch(e => {});
      }
    });
  }

PNG 이미지 외부 공유 구현 (blob, navigation.share)

글마디 카드를 PNG 이미지로 변환하는 데 (이렇게 쉽게도!) 성공했으니 어떠한 파일을 외부 애플리케이션으로 공유할 수 있게 해 주는 API나 라이브러리를 찾아보았습니다. 그 중에서 찾은 것이 Web Share API를 사용하는 방법입니다.

Web Share API는 웹사이트나 애플리케이션에서 컨텐츠를 직접 공유하기 위한 네이티브 공유 다이얼로그를 열어주는 방법을 제공합니다 (참고).

navigator.share 메서드에 data array를 전달하면 공유 다이얼로그를 표시해준다길래 data array로 위에서 toPng 메서드가 만들어준 dataURL을 전달했더니 제대로 동작하지 않았습니다.

몇 번의 구글링을 해 보니 이유는, dataURL이 이미지 데이터이긴 하나 그 자체로 이미지 파일은 아니기 때문에 아래와 같이 dataURL을 자바스크립트의 blob 객체로 변환한 후 File 객체로 만들어야 한다고 합니다. blobBinary Large Object의 약자로, 이미지, 비디오, 사운드와 같은 멀티미디어 객체를 나타내는 데 사용됩니다.

const blob = await (await fetch(dataUrl)).blob();

dataURL도 리소스이기 때문에 fetch하면 다음과 같은 응답을 받을 수 있습니다. 이 응답에 위와 같이 blob 메서드를 호출하면 dataURL을 blob 객체로 변환합니다.

그다음 blob 객체를 File 객체로 아래와 같이 변환해 주면 비로소 공유할 이미지 파일 객체를 얻을 수 있습니다.

const file = new File([blob], 'geulmadi.png', {
  type: 'image/png',
  lastModified: new Date(),
});

마지막으로 navigation.share 메서드에 file을 전달하면 브라우저의 네이티브 공유 다이얼로그를 표시할 수 있습니다.

navigator.share({ files: [file] }).then(() => { 
	// ...
});

아래는 공유('.share') 버튼을 클릭하면 글마디 카드의 PNG 이미지를 외부로 공유할 수 있는 다이얼로그를 띄우는 코드입니다.

/* 사진 공유하기 */
async #shareImage() {
  document.querySelector('body').addEventListener('click', e => {
    if (e.target.closest('.share')) {
      // 1. 카드 DOM을 png dataUrl로 변환
      // 2. dataUrl을 blob 객체로 변환
      // 3. blob 객체를 File 객체로 변환
      // 4. navigator.share(file객체)로 공유
      const node = document.querySelector('.card');
      htmlToImage
        .toPng(node, {
        filter: node => node.className !== 'tabs',
      })
        .then(async dataUrl => {
        if (navigator.share) {
          // https://developer.mozilla.org/en-US/docs/Web/API/Response/blob
          const blob = await (await fetch(dataUrl)).blob();
          const file = new File([blob], 'geulmadi.png', {
            type: 'image/png',
            lastModified: new Date(),
          });
          navigator.share({ files: [file] }).then(() => {});
        }
      })
        .catch(e => {});
    }
  });
}

DEMO

아이폰(safari 브라우저)에서 글마디 카드를 다운로드하고 공유하는 시연 영상입니다.

profile
기록하기

0개의 댓글