4 min read

Node.js + PDFμ™€μ˜ μ „μŸ

Table of Contents

PDF

졜근 업무λ₯Ό μ§„ν–‰ν•˜λ©΄μ„œ PDF νŒŒμΌμ„ μ²˜λ¦¬ν•  일이 λ§Žμ•˜κ³ , ν˜„μž¬λ„ μ§„ν–‰ μ€‘μž…λ‹ˆλ‹€.
생각보닀 μ½”λ“œλ‘œ PDF νŒŒμΌμ„ μ²˜λ¦¬ν•˜λŠ” 것은 쉽지 μ•Šμ€ μΌμ΄μ—ˆμŠ΅λ‹ˆλ‹€. μ‹œν–‰μ°©μ˜€λ₯Ό μƒλ‹Ήνžˆ 많이 κ±°μ³€κ³ , κ·Έ 과정을 κ°„λ‹¨νžˆ 적어 보렀 ν•©λ‹ˆλ‹€.
(Node.js둜 PDF 파일 μ²˜λ¦¬ν•  일이 ν”ν•˜μ§€λŠ” μ•Šκ² μ§€λ§Œ..)

1. PDF 파일 μ•”ν˜Έν™”

문제의 μ‹œμž‘μ€ ν•œ λ°±μ—”λ“œ νŒ€μ›μ΄ 파일 μ—…λ‘œλ“œ μ„œλ²„μ˜ λΆ€ν•˜λ₯Ό λ°œκ²¬ν•œ κ²ƒμ΄μ—ˆμŠ΅λ‹ˆλ‹€. ν•΄λ‹Ή μ„œλ²„λŠ” PDF, μ—‘μ…€ νŒŒμΌμ„ μž‘μ„±ν•΄ S3 μ €μž₯μ†Œμ— μ—…λ‘œλ“œν•˜λŠ” κ°„λ‹¨ν•œ μ—­ν• λ§Œ μˆ˜ν–‰ μ€‘μ΄μ—ˆλŠ”λ°, κ·ΈλŸΌμ—λ„ 메인 μ„œλ²„λ³΄λ‹€ CPU μ‚¬μš©λŸ‰μ΄ 높을 λ•Œλ„ μžˆμ—ˆμŠ΅λ‹ˆλ‹€.

μ½”λ“œλ₯Ό 뢄석해 λ³΄λ‹ˆ μ™ΈλΆ€ μ €μž₯μ†Œμ— μ—…λ‘œλ“œν•˜κΈ° μ „, μž„μ‹œ νŒŒμΌμ„ μ»¨ν…Œμ΄λ„ˆ 내뢀에 μ €μž₯ν•˜λŠ” 둜직이 μžˆμ—ˆμŠ΅λ‹ˆλ‹€. 이 뢀뢄이 문제라고 νŒλ‹¨ν•˜κ³  κ°œμ„ μ„ μœ„ν•΄ μž„μ‹œ νŒŒμΌμ„ μ €μž₯ν•˜μ§€ μ•Šκ³  버퍼 μƒνƒœμ—μ„œ 직접 μ—…λ‘œλ“œν•  수 μžˆλ„λ‘ 곡수λ₯Ό νŒŒμ•…ν•˜κΈ° μ‹œμž‘ν–ˆμŠ΅λ‹ˆλ‹€.
(λ‹€λ₯Έ κ°œμ„ μ λ„ μžˆμ—ˆμ§€λ§Œ, CPU에 큰 λΆ€ν•˜λ₯Ό 쀄 λ§Œν•œ μš”μ†ŒλŠ” μ•„λ‹ˆμ—ˆμŠ΅λ‹ˆλ‹€.)

그런데 μΆ”κ°€ 확인 κ³Όμ •μ—μ„œ PDF 파일 μ•”ν˜Έν™”μ— μ‚¬μš©ν•˜λŠ” node-qpdf2 λΌμ΄λΈŒλŸ¬λ¦¬κ°€ μ‹€μ œ 파일 경둜λ₯Ό ν•„μš”λ‘œ ν•œλ‹€λŠ” 것을 확인할 수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€.
ν•΄λ‹Ή λΌμ΄λΈŒλŸ¬λ¦¬λŠ” C++둜 μž‘μ„±λœ CLIλ₯Ό 감싼 κ²ƒμœΌλ‘œ, 이 λͺ¨μ²΄κ°€ 파일 경둜λ₯Ό μš”κ΅¬ν•˜κ³  라이브러리 μ½”λ“œλŠ” λͺ…λ Ήμ–΄λ₯Ό 생성해 μ£ΌλŠ” κ²ƒμ΄μ—ˆκΈ° λ•Œλ¬Έμ— 이λ₯Ό κ³ μΉ  μˆ˜λŠ” μ—†μ—ˆμŠ΅λ‹ˆλ‹€. (원본 μ½”λ“œ)

ν•΄λ‹Ή 라이브러리λ₯Ό μ‚¬μš©ν•  경우 μž„μ‹œ νŒŒμΌμ„ μ»¨ν…Œμ΄λ„ˆ 내뢀에 μ €μž₯ν•˜λŠ” 것은 ν˜„μ‹€μ μΈ μ„ νƒμ΄μ—ˆκ³ , μž„μ‹œ νŒŒμΌμ„ μ‚­μ œν•˜λŠ” λ‘œμ§μ„ μΆ”κ°€ν•˜λŠ” 정도가 ν•œκ³„μ˜€μŠ΅λ‹ˆλ‹€. λ‹Ήμ—°νžˆ 근본적인 문제 해결법이 μ•„λ‹ˆμ—ˆκΈ° λ•Œλ¬Έμ— 방법을 계속 μ°Ύμ•˜μ§€λ§Œ 쉽지 μ•Šμ•˜κ³ , ν•œ λ•ŒλŠ” λ°±μ—”λ“œ νŒ€λΌλ¦¬ μ˜€ν”ˆμ†ŒμŠ€ ν”„λ‘œμ νŠΈλ₯Ό ν• κΉŒ λ…Όμ˜κ°€ μ˜€κ°€κΈ°λ„ ν–ˆμŠ΅λ‹ˆλ‹€. 🀣

μ˜μ™Έλ‘œ 해결법은 λ‹€λ₯Έ κ³³μ—μ„œ λ‚˜μ™”μŠ΅λ‹ˆλ‹€. PDF 파일 생성에 μ‚¬μš©ν•˜κ³  있던 pdf-lib 라이브러리λ₯Ό λˆ„κ΅°κ°€ Fork ν•˜μ—¬ μ•”ν˜Έν™” κΈ°λŠ₯을 μΆ”κ°€ν•œ 것을 λ°œκ²¬ν•  수 μžˆμ—ˆκΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€. 이λ₯Ό μ μš©ν•΄ λ³΄λŠ” κ²ƒμœΌλ‘œ ν•©μ˜λ˜μ—ˆκ³ , 글을 μ“°λŠ” μ‹œμ μ—λŠ” λ‹€λ₯Έ νŒ€μ›μ—κ²Œ ν• λ‹Ήλ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.

2. PDF 파일 μ••μΆ•κ³Ό 생성 둜직

ν˜„μž¬ μ—…λ¬΄μ—μ„œλŠ” 주기적으둜 PDF ν˜•μ‹μ˜ λ³΄κ³ μ„œλ₯Ό λ°œν–‰ν•΄μ•Ό ν•˜λŠ”λ°, λ‹€λ₯Έ 업체와 ν˜‘μ—…μ„ ν•˜λ©΄μ„œ 300KB μ΄ν•˜μ˜ μ €μš©λŸ‰ PDFκ°€ ν•„μš”ν•œ μƒν™©μ΄μ—ˆμŠ΅λ‹ˆλ‹€. μ œκ°€ 업무λ₯Ό 받은 μ‹œμ μ—μ„œλŠ” 파일 μš©λŸ‰μ΄ 5MB 이상인 κ²½μš°λ„ μžˆμ—ˆκ³ , λŒ€μ‘μ„ μœ„ν•΄μ„œ μƒμ„±λœ νŒŒμΌμ„ μ••μΆ•ν•˜κ±°λ‚˜, λ‘œμ§μ„ κ°œμ„ ν•˜μ—¬ μ €μš©λŸ‰μ˜ νŒŒμΌμ„ λ§Œλ“œλŠ” 과정이 ν•„μš”ν–ˆμŠ΅λ‹ˆλ‹€.

κΈ°μ‘΄ 파일 μ••μΆ•μ˜ 경우 λͺ‡ κ°€μ§€λ₯Ό κ³ λ €ν•΄ λ³΄μ•˜μœΌλ‚˜ λΆ€μ μ ˆν•˜λ‹€λŠ” νŒλ‹¨μ„ λ‚΄λ ΈμŠ΅λ‹ˆλ‹€.

  • μ‹œμ€‘μ— κ²€μƒ‰ν•˜λ©΄ λ‚˜μ˜€λŠ” μ••μΆ• μ‚¬μ΄νŠΈλ‚˜ λ„κ΅¬λŠ” λ³΄μ•ˆ λ¬Έμ œμ™€ μˆ˜λ™ μž‘μ—…μ˜ λ¬Έμ œκ°€ μžˆμ—ˆμŠ΅λ‹ˆλ‹€.
  • pypdf λ“±μ˜ μ˜€ν”ˆμ†ŒμŠ€ λ„κ΅¬λŠ” κ·Έ μˆ˜λ„ 적고, ν…ŒμŠ€νŠΈ κ²°κ³Ό μ••μΆ• μ„±λŠ₯도 μ’‹λ‹€κ³  νŒλ‹¨ν•˜κΈ°λŠ” μ–΄λ €μ› μŠ΅λ‹ˆλ‹€.
  • GhostscriptλŠ” κ°€μž₯ 잘 μ•Œλ €μ Έ 있고, 만쑱슀러운 μ••μΆ• μ„±λŠ₯을 보여 μ£Όμ—ˆμŠ΅λ‹ˆλ‹€.
    ν•˜μ§€λ§Œ μΆ”κ°€ 쑰사 κ²°κ³Ό AGPL λΌμ΄μ„ μŠ€κ°€ κ±Έλ € μžˆμ–΄ 무료 버전을 μ‚¬μš©ν•˜κΈ° μœ„ν•΄μ„œλŠ” κ΄€λ ¨ μ†ŒμŠ€λ₯Ό κ³΅κ°œν•΄μ•Ό ν•˜κ³ , 상업적 μ΄μš©μ„ μœ„ν•΄μ„œλŠ” κ°œλ°œμ‚¬ 츑에 λ‘œμ—΄ν‹°λ₯Ό μ§€λΆˆν•΄μ•Ό ν–ˆμŠ΅λ‹ˆλ‹€.
    μ‹€μ œλ‘œ ν•œμ»΄ 츑이 이λ₯Ό μ§€ν‚€μ§€ μ•Šμ•˜λ‹€κ°€ μ†Œμ†‘μ— κ±Έλ € κ³Όμ§•κΈˆμ„ λ¬Έ 적이 μžˆμ—ˆμŠ΅λ‹ˆλ‹€.
    그리고 λ‘œμ—΄ν‹°κ°€ μ°Ύμ•„λ³΄λ‹ˆ 만만치 μ•Šμ•˜μŠ΅λ‹ˆλ‹€.. (수백 λ‹¬λŸ¬λΆ€ν„° μΆœλ°œν•œλ‹€κ³  ν•©λ‹ˆλ‹€)

κ²°κ΅­ PDF 생성 λ‘œμ§μ„ κ°œμ„ ν•  수 μžˆλŠ”μ§€ 쑰사에 λ“€μ–΄κ°”λŠ”λ°, λ‹€ν–‰νžˆλ„ 크게 2κ°€μ§€ κ°œμ„ μ μ„ 찾을 수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€.

  • 원본 ν…œν”Œλ¦Ώ 파일의 크기가 큰 νŽΈμ΄μ—ˆμŠ΅λ‹ˆλ‹€. 이λ₯Ό 압좕을 톡해 150KB μ΄ν•˜λ‘œ μ€„μ˜€μŠ΅λ‹ˆλ‹€.
  • PDF 처리 κ³Όμ •μ—μ„œ ν…μŠ€νŠΈλ₯Ό μž…λ ₯ν•  λ•Œ 폰트 파일 전체λ₯Ό λ‘œλ”©ν•˜κ³  μžˆμ—ˆμŠ΅λ‹ˆλ‹€.
    λ³΄κ³ μ„œ PDF 생성 과정이 크게 2λ‹¨κ³„λ‘œ λ‚˜λ‰˜μ–΄ μžˆμ—ˆλŠ”λ°, 이 2개 λ‹¨κ³„μ—μ„œ λͺ¨λ‘ λΉ„μŠ·ν•œ μˆ˜μ€€μ˜ 큰 μš©λŸ‰ 증가λ₯Ό ν™•μΈν–ˆμŠ΅λ‹ˆλ‹€. λ‹€λ₯Έ λΆ€λΆ„μ—μ„œ 원인을 μ°Ύμ§€ λͺ»ν•˜λ‹€κ°€ μ–‘μͺ½ 둜직 λͺ¨λ‘ 폰트λ₯Ό λ‘œλ”©ν•˜λŠ” 과정이 ν¬ν•¨λ˜μ–΄ μžˆμŒμ„ λ°œκ²¬ν–ˆκ³ , μ‹€μ œλ‘œ 폰트 자체 μš©λŸ‰μ΄ κ½€λ‚˜ 큰 νŽΈμ΄μ—ˆμŠ΅λ‹ˆλ‹€.
    κ΄€λ ¨ 라이브러리λ₯Ό μ‘°μ‚¬ν•œ κ²°κ³Ό, 폰트 파일 전체λ₯Ό λ‘œλ”©ν•˜λŠ” 것이 기본값이고, 이λ₯Ό μ˜΅μ…˜μ„ 톡해 μΌλΆ€λ§Œ λ‘œλ”©ν•  수 μžˆλ„λ‘ μ„€μ •ν•  수 μžˆλ‹€λŠ” 것을 λ°œκ²¬ν–ˆμŠ΅λ‹ˆλ‹€. (λ¬Έμ„œ)
    ν•˜μ§€λ§Œ 폰트 μ²˜λ¦¬μ— μ‚¬μš©ν•˜λŠ” @pdf-lib/fontkit λΌμ΄λΈŒλŸ¬λ¦¬λŠ” ν•œκΈ€μ„ ν¬ν•¨ν•œ νŠΉμ • μ–Έμ–΄ κΈ€κΌ΄μ—μ„œ μ˜΅μ…˜μ„ μ μš©ν•˜λ©΄ κΈ€μžκ°€ μ œλŒ€λ‘œ λ“€μ–΄κ°€μ§€ μ•ŠλŠ” λ¬Έμ œκ°€ μžˆμ—ˆμŠ΅λ‹ˆλ‹€. 이와 κ΄€λ ¨ν•˜μ—¬ 였λ₯˜λ₯Ό μˆ˜μ •ν•œ λΌμ΄λΈŒλŸ¬λ¦¬κ°€ μžˆμ–΄ 이λ₯Ό λ„μž…ν•΄ ν•΄κ²°ν–ˆμŠ΅λ‹ˆλ‹€. (이슈)

μœ„ 2κ°€μ§€ κ°œμ„ μ μ„ μ μš©ν•΄ 3 ~ 6MB 정도 λ‚˜μ˜€λ˜ κ²°κ³Ό 파일 μš©λŸ‰μ„ 150 ~ 200KBκΉŒμ§€ 크게 쀄일 수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€.

λŠλ‚€ 점

  • μ§€μ†μ μœΌλ‘œ κ΄€λ¦¬λ˜λŠ” λΌμ΄λΈŒλŸ¬λ¦¬λŠ” 이런 λ¬Έμ œκ°€ 적을 수 μžˆμ§€λ§Œ, λΉ„μ£Όλ₯˜ κΈ°λŠ₯μ΄κ±°λ‚˜ 방치된 라이브러리 문제λ₯Ό ν•΄κ²°ν•˜λŠ” 것이 쉽지 μ•Šμ•˜μŠ΅λ‹ˆλ‹€. 이 κ²½μš°λ„ PDF μ²˜λ¦¬κ°€ 생각보닀 ν”ν•œ κ²½μš°κ°€ μ•„λ‹ˆμ—ˆκ³ , κ΄€λ ¨ λΌμ΄λΈŒλŸ¬λ¦¬κ°€ 였랜 κΈ°κ°„ λ°©μΉ˜λ˜μ–΄ μžˆκ±°λ‚˜ 유료 μ „ν™˜μ΄ λ˜μ–΄ μžˆμ–΄ κ³΅μˆ˜κ°€ μ»€μ‘ŒμŠ΅λ‹ˆλ‹€.
  • μ˜€ν”ˆμ†ŒμŠ€λ₯Ό μ‚¬μš©ν•  λ•ŒλŠ” 항상 λΌμ΄μ„ μŠ€μ— κ΄€ν•΄ μ£Όμ˜κ°€ ν•„μš”ν•˜λ‹€λŠ” 것을 λ‹€μ‹œ λŠκΌˆμŠ΅λ‹ˆλ‹€. 개인 ν”„λ‘œμ νŠΈμ—μ„œλŠ” 이λ₯Ό κ³ λ €ν•  일이 거의 μ—†μ—ˆλŠ”λ°, μ‹€λ¬΄μ—μ„œ 이λ₯Ό 크게 λŠλ‚„ 수 μžˆμ—ˆλ˜ μ‚¬λ‘€μ˜€μŠ΅λ‹ˆλ‹€.