컴포넌트를 이미지 파일로 저장해보자!
개요
이전 포스팅과 동일한 프로젝트에서 필요한 기능이다. 사용자가 문장 카드를 만든 후에 이 카드를 이미지 형태로 다운로드하는 기능을 구현해야 했다. 기획 단계에서는 어떻게든 구현할 수 있겠지라는 마인드로 기능을 생각했는데, 구현 단계에서 라이브러리 덕에 성공적으로 구현을 끝마칠 수 있었다. 지금부터 어떻게 구현했는지 적어보려고 한다.
사용한 라이브러리
라이브러리에 대한 설정 방법이 다양하니 공식 문서를 읽어보길 추천한다.
html-to-image
- 우선 컴포넌트를 이미지로 변경해주는 라이브러리이다. 다른 비슷한 유형의 라이브러리들이 있지만 모종의 이유로 이 라이브러리를 선택했다. 이유는 아래에 따로 작성했다.
file-saver
- 파일을 저장할때 사용하는 라이브러리이다. 다른 포맷의 파일도 저장이 가능한듯 한데, 나는 이미지 저장을 위해 사용했다. 지원되는 브라우저도 명시되어 있으니 참고하면 된다.
구현 과정
-
우선 이미지로 변환할 컴포넌트를 선택해야 한다.
useRef
를 사용하여 dom 요소를 선택해준다.const Detail = () => { const cardRef = useRef<HTMLDivElement>(null); return ( <Container> <BasicCard ref={cardRef}> <IconQuote /> <BasicCardContent /> <BasicCardBookInfo /> </BasicCard> </Container> ); }; export default Detail;
-
이미지 다운로드를 할 수 있는 버튼을 만들고 함수를 만들어준다. 나는 PNG로 만들기 위해
toPNG
를 사용했다. 추가적으로 옵션을 설정해줄 수 있는데, 고화질 이미지를 만들기 위해canvasWidth
와canvasHeight
설정을 사용했다. 설정한 픽셀의 2배 해상도의 이미지를 얻을 수 있다.import { toPng } from 'html-to-image'; const Detail = () => { const cardRef = useRef<HTMLDivElement>(null); const handleClickDownload = () => { const card = cardRef.current; if (card) { toPng(card, { canvasWidth: 1080, canvasHeight: data.imageSize === 'aspect-square' ? 1080 : 1920, }); } }; return ( <Container> <BasicCard ref={cardRef}> <IconQuote /> <BasicCardContent /> <BasicCardBookInfo /> </BasicCard> <button onClick={handleClickDownload}> <IconDownload /> <span>이미지 다운로드</span> </button> </Container> ); }; export default Detail;
-
file saver의 saveAs를 사용하여 이미지를 저장한다.
import { toPng } from 'html-to-image'; import { saveAs } from 'file-saver'; const Detail = () => { const cardRef = useRef<HTMLDivElement>(null); const handleClickDownload = () => { const card = cardRef.current; if (card) { toPng(card, { canvasWidth: 1080, canvasHeight: data.imageSize === 'aspect-square' ? 1080 : 1920, }).then((image) => { saveAs(image, 'card.png'); }); } }; return ( <Container> <BasicCard ref={cardRef}> <IconQuote /> <BasicCardContent /> <BasicCardBookInfo /> </BasicCard> <button onClick={handleClickDownload}> <IconDownload /> <span>이미지 다운로드</span> </button> </Container> ); }; export default Detail;
html-to-image를 사용한 이유
기존에는 dom-to-image
를 사용해 구현을 했는데 화질 설정을 하지 않으면 사용자가 설정한 화면 크기에 맞춰서 다운로드가 되기 때문에 언제든 고화질의 이미지를 다운로드 받게 해야 했다.
dom-to-image
에서의 문제는 ref
를 설정한 컴포넌트만 픽셀이 변경되고 하위의 자식 컴포넌트들은 사이즈가 변경되지 않았다. 이에 비해 html-to-image
는 canvasWidth
& canvasHeight
가 있어서 내부의 모든 dom 요소들의 사이즈를 바꿔줄 수 있었다.
적용화면
- 위에서 설정한 이름으로 파일이 다운로드가 되고 아래의 이미지를 얻을 수 있다.