프론트엔드 개발/React.js

리액트 컴포넌트를 pdf 파일로 저장하기 (html2canvas, jspdf)

snowman95 2023. 7. 12. 17:47
728x90
반응형

react-pdf 라이브러리를 사용해서 전용컴포넌트로 한땀한땀 쌓아올려서 pdf 파일을 만들어도 되지만 그건 너무나 귀찮습니다.

저희는 웹페이지의 화면을 캡쳐해서 pdf 파일로 다운로드 받는 편한 방법을 사용할 것입니다.

 

아래의 두 패키지를 설치합니다.

yarn add html2canvas jspdf

html2canvas 는 리액트 컴포넌트를 이미지로 변환해주는 라이브러리고

jspdf는 그 이미지를 pdf파일로 만들어주는 라이브러리 입니다.

 

 

이슈들

그런데 사용해보면 바로 이슈가 터져 나옵니다. 이슈 해결하면서 해결된 코드도 같이 보겠습니다.

이슈1 : 페이지 높이가 너무 높아서 (내용이 너무 길어서) pdf 파일안에 다 담기지 않고 잘리는 이슈

처음에 인터넷에서 흔한 코드 긁어와서 pdf파일의 width, height 를 설정해주었습니다.

잘 된 줄알고 기뻐하고 있는데 페이지 하단을 보니 내용이 잘려있습니다. 그래서 높이 부분을 무작정 건드렸더니 해상도가 깨집니다.

 

이것은 페이지 높이가 너무 길어서 발생하는 이슈였으며,

아래와 같이 코드 수정해주었더니 페이지 잘리지 않고 전체 페이지 내용이 잘 보여집니다.

 

import { useEffect, useRef, useState } from 'react'
import html2canvas from 'html2canvas'
import { jsPDF } from 'jspdf'

const 컴포넌트 = ()=>{
  const printRef = useRef<HTMLElement>(null)

  const handleDownloadPdf = async () => {
    const element = printRef.current
    if (!element) {
      return
    }
    const canvas = await html2canvas(element)
    const componentWidth = element.offsetWidth
    const componentHeight = element.offsetHeight

    const orientation = componentWidth >= componentHeight ? 'l' : 'p'

    const imgData = canvas.toDataURL('image/png')
    const pdf = new jsPDF({
      orientation,
      unit: 'px',
    })

    pdf.internal.pageSize.width = componentWidth
    pdf.internal.pageSize.height = componentHeight

    pdf.addImage(imgData, 'PNG', 0, 0, componentWidth, componentHeight)
    pdf.save('대출제안서.pdf')
  }
  
   return (
     <>
       <section className="flex flex-col gap-20 py-20 px-30 bg-piper-grey-100" 
         ref={printRef}>
         // 컴포넌트 내용...
       </section>
       <button onClick={handleDownloadPdf}>
         <span>PDF 다운로드</span>
       </button>       
     </>
   )
 }

 

이슈2 : 일부 글자가 바닥에 붙어버리는 이슈

보통 div 박스 안에 글자를 중앙에 정렬한 경우 공통적으로 발생했습니다. (아래 이미지 참고)

무슨 수를 써도 해결되지 않았는데 원인은 정말 뜬금없이 Tailwind.css 에 있었습니다.

이것이 마법입니다.

img 의 기본 스타일에 inline-block을 추가해주어야 했습니다. 아무래도 기본 스타일이 날라가버리면서 문제가 터졌던 것 같습니다.

/* global.css */
@tailwind base;
@layer base {
  img {
    @apply inline-block;
  }
}
@tailwind components;
@tailwind utilities;

 

 

참고

- https://stackoverflow.com/a/64761137

- https://github.com/niklasvh/html2canvas/issues/2775#issuecomment-1204988157

반응형