PDF 다운로드 기능 구현, puppeteer로 서버에서 자동 생성하기
웹페이지를 화면 그대로 PDF로 저장하려면 어떻게 할까요? puppeteer로 PDF 다운로드 기능 구현하는 방법, 똑개가 정리했습니다!

안녕하세요. 사랑받는 IT 프로덕트의 첫 스텝, 똑똑한개발자입니다 :)
3줄 요약
웹페이지를 화면 그대로 PDF로 내려주려면, 클라이언트 캡처 방식보다 서버에서 실제 브라우저를 띄우는 puppeteer 방식이 정확합니다.
PDF 다운로드 기능 구현의 핵심은 코드 3단계(설치 → API → 버튼)가 아니라, 인증·렌더링 타이밍·인쇄용 CSS·서버 리소스 4가지를 어떻게 처리하느냐입니다.
견적서·리포트·이력서처럼 동적 데이터를 문서로 남겨야 하는 서비스라면, puppeteer PDF 생성이 가장 안정적인 선택지입니다.
왜 웹사이트에 PDF 다운로드 기능이 필요할까?
사용자가 웹사이트에서 정보를 확인한 후, 'PDF로 저장' 버튼 하나로 문서를 내려받을 수 있다면 어떨까요?
이 기능은 다양한 상황에서 유용하게 쓰이는데요!
견적서, 신청서, 주문내역 출력
이력서·포트폴리오 미리보기 저장
보고서나 리포트 페이지의 문서화
이처럼 웹페이지의 내용을 그대로 PDF로 변환해 사용자에게 다운로드 링크를 제공하는 기능은 단순해 보이지만, 기술적으로는 몇 가지 고려사항이 필요해요.
특히 PDF는 "화면과 조금 다르게 나왔다"가 곧 컴플레인이 되는 결과물입니다. 계약서 금액이 잘리거나, 표 한 줄이 다음 페이지로 넘어가거나, 한글이 □□□로 깨지는 순간 신뢰를 잃죠. 그래서 PDF 다운로드 기능 구현은 어떤 방식으로 렌더링할지를 처음에 잘 고르는 것이 가장 중요합니다.
이 기능을 안정적으로 구현할 수 있는 대표적인 도구가 바로 puppeteer인데요! 자세히 설명해볼게요.
PDF 다운로드 기능 구현, 방식부터 고르세요
웹에서 PDF를 만드는 방법은 크게 네 가지입니다. 무엇을 선택하느냐에 따라 결과물의 품질과 유지보수 비용이 완전히 달라져요.
방식 | 렌더링 정확도 | 텍스트 검색·복사 | 서버 리소스 | 적합한 용도 |
|---|---|---|---|---|
puppeteer (서버, 헤드리스 크롬) | 높음 — 실제 크롬이 그린 결과 | 가능 (진짜 텍스트로 출력) | 큼 — 브라우저 프로세스 상주 | 견적서·계약서·리포트 등 정식 문서 |
html2canvas + jsPDF (클라이언트) | 중간 — CSS 미지원 속성 다수 | 불가 — 이미지 한 장으로 박힘 | 없음 (사용자 기기 부담) | 프로필 카드, 결과 이미지 저장 |
브라우저 인쇄 (window.print) | 높음 | 가능 | 없음 | 사용자가 직접 저장해도 되는 페이지 |
서버 PDF 라이브러리 (pdfkit 등) | 낮음 — 화면 재현 아님, 직접 그림 | 가능 | 작음 | 고정 양식 대량 생성 |
핵심 갈림길은 "화면 그대로"가 필요한지입니다.
html2canvas는 DOM을 캔버스로 래스터화해 이미지를 만들고 jsPDF가 그 이미지를 문서에 넣는 구조라, 결과 PDF가 사실상 이미지 래퍼가 됩니다. 텍스트 선택도 검색도 안 되고, 페이지 나누기 기능이 없어 여러 장짜리 문서는 영역을 미리 잘라놔야 하죠.
반면 puppeteer는 서버에서 실제 크롬을 띄워 페이지를 렌더링한 뒤 인쇄 엔진으로 PDF를 뽑습니다. 웹폰트·복잡한 레이아웃·인증이 걸린 화면까지 화면 그대로 담아내면서, 텍스트는 텍스트로 남습니다.
puppeteer로 PDF를 생성하는 원리는 무엇인가요?
puppeteer는 Node.js에서 구글 크롬 브라우저를 자동으로 조작할 수 있게 해주는 헤드리스 브라우저 자동화 도구인데요.
HTML 페이지를 실제 브라우저처럼 렌더링해서 PDF로 변환할 수 있어, 화면 그대로의 디자인을 유지하며 PDF 출력이 가능하죠!
puppeteer의 장점
CSS 스타일이 적용된 상태로 PDF 출력 가능
JavaScript 렌더링 완료된 상태 기준으로 캡처
다양한 페이지 크기, 여백, 배경 설정 가능
백엔드 서버에 통합 가능 (Express, Nest.js 등)
즉 puppeteer PDF 생성은 "HTML을 PDF 포맷으로 번역"하는 게 아니라, "크롬에게 인쇄를 시키는" 방식입니다. 그래서 개발자가 이미 알고 있는 CSS 지식이 그대로 통하고, 디자인 QA도 브라우저에서 미리 확인할 수 있습니다.
웹사이트에서 실시간 PDF 다운로드 구현하기
실전 예제로, 사용자가 버튼 클릭 → 서버 요청 → PDF 생성 → 다운로드 응답을 받는 전체 흐름을 만들어 볼게요.
1. Node.js 서버에 puppeteer 설치
npm install puppeteer express
2. PDF 생성용 API 만들기 (예: /api/download-pdf)
const express = require('express');
const puppeteer = require('puppeteer');
const app = express();
app.get('/api/download-pdf', async (req, res) => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
// PDF로 만들 HTML 페이지 주소 (로그인 필요 시 쿠키 세팅 필요)
await page.goto('https://your-service.com/print-view', {
waitUntil: 'networkidle0',
});
const pdfBuffer = await page.pdf({
format: 'A4',
printBackground: true,
margin: { top: '40px', bottom: '40px' },
});
await browser.close();
res.set({
'Content-Type': 'application/pdf',
'Content-Disposition': 'attachment; filename="download.pdf"',
});
res.send(pdfBuffer);
});
app.listen(3000, () => {
console.log('PDF download service running on port 3000');
});
여기서 놓치기 쉬운 옵션이 두 개 있어요. 배경 인쇄(printBackground)를 켜지 않으면 브랜드 색이 깔린 헤더가 하얗게 비고, 대기 조건(networkidle0)은 열린 네트워크 연결이 500ms 이상 없을 때를 "로딩 완료"로 판단합니다. 데이터 요청이 늦게 붙는 화면이라면 이 조건만으로는 부족할 수 있습니다.
3. 프론트엔드 버튼에서 API 호출
<a href="/api/download-pdf" download>
<button>PDF 다운로드</button>
</a>

puppeteer PDF 다운로드에서 실무자가 꼭 막히는 5가지
서비스에서 PDF 다운로드 기능을 안정적으로 제공하려면 다음과 같은 점들을 고려해야 해요.
1. 인증 처리
PDF에 로그인된 사용자 데이터가 들어가야 한다면, 쿠키나 세션을 puppeteer 쪽에 전달해야 합니다. 그렇지 않으면 로그인 페이지가 그대로 PDF로 찍혀 나옵니다. 서버에서만 접근 가능한 전용 인쇄 라우트를 두고 짧은 만료 토큰으로 보호하는 방식이 안전합니다.
2. 데이터 동기화
JS로 렌더링된 SPA라면, puppeteer가 모든 콘텐츠를 로드한 시점까지 기다려야 합니다. 대기 조건만 믿지 말고, "표의 마지막 행이 그려졌다"처럼 렌더링 완료를 알리는 특정 요소를 기다리게 하는 편이 훨씬 안정적입니다.
3. 인쇄용 디자인 최적화
인쇄용 CSS(@media print)로 PDF에 맞는 레이아웃을 따로 구성해야 합니다. 화면용 그림자·고정 헤더·스크롤 영역은 인쇄에서 대부분 문제를 일으키니 정리하고, 페이지가 잘리면 안 되는 블록에는 페이지 나눔 회피 속성을 지정하세요. 참고로 puppeteer는 @media print 안의 일부 규칙을 무시하는 사례가 보고돼 있어(puppeteer #3724), 인쇄 전용 클래스로 명시하는 편이 예측 가능합니다.
4. 한글 폰트 깨짐
로컬에서는 잘 나오다가 배포하면 한글이 □□□로 깨지는 대표적인 사고입니다. 원인은 서버 이미지에 한글 폰트가 아예 없기 때문이에요. 컨테이너에 나눔·Noto 계열 한글 폰트를 설치하고 로케일 환경변수를 맞춰주면 해결됩니다. 웹폰트를 쓴다면 폰트 로딩이 끝난 뒤 인쇄되도록 대기 처리도 함께 넣어주세요.
5. 서버 리소스
puppeteer는 브라우저를 띄우기 때문에 서버 CPU·메모리 사용량을 반드시 고려해야 합니다. 요청마다 브라우저를 새로 실행하면 동시 요청 몇 건에도 서버가 흔들립니다. 브라우저 인스턴스를 재사용하고 동시 실행 개수를 제한하는 구조가 기본입니다.
대용량·다건 PDF는 어떻게 처리하나요?
사용자가 기다리는 동안 서버가 브라우저를 띄우는 구조는 트래픽이 몰리면 그대로 병목이 됩니다. 그래서 규모가 커지면 흐름을 바꿔야 해요.
1. 요청을 큐에 넣고 즉시 응답
사용자에게는 "생성 중"을 보여주고, 워커가 순차적으로 PDF를 만듭니다.
2. 결과 파일을 S3에 저장
만들어진 PDF를 AWS S3 등에 올리고, 만료 시간이 있는 다운로드 링크를 내려줍니다. 공유·재다운로드가 자연히 해결됩니다.
3. 동일 요청은 캐싱
데이터가 바뀌지 않았다면 같은 PDF를 다시 만들 이유가 없습니다.
4. PDF 생성 서버를 분리
브라우저를 띄우는 무거운 작업을 API 서버와 떼어놓으면, PDF 트래픽이 서비스 전체를 흔들지 않습니다.
또한 다운로드 전에 서버에서 파일을 임시 저장하는 방식도 가능하니, 서비스 성격에 맞게 선택하시면 됩니다.

웹 서비스의 문서 출력, 자동화로 해결!
사용자 요청에 따라 동적으로 생성된 데이터를 정확하게 문서화해서 제공해야 하는 서비스라면, puppeteer는 가장 좋은 선택지가 되어준다고 생각하는데요!
HTML → PDF로 자동 전환
기존 화면 디자인 유지
다양한 사용자 시나리오에 대응 가능
한 번 잘 만들어두면 견적서·리포트·이력서·수료증까지 같은 파이프라인으로 확장할 수 있다는 점도 큰 장점입니다.
자주 묻는 질문
1. puppeteer PDF 생성과 html2canvas, 결국 뭘 써야 하나요?
문서로서 의미가 있는 결과물(견적서·계약서·리포트)이면 puppeteer입니다. 텍스트가 검색·복사되고 여러 페이지로 자연스럽게 나뉘어야 하니까요. 반대로 프로필 카드나 결과 이미지처럼 "그림 한 장 저장"에 가깝고 서버를 쓰지 않는 게 중요하다면 html2canvas가 더 가볍습니다.
2. PDF에 한글이 깨져서 나옵니다. 왜 그런가요?
서버 환경에 한글 폰트가 설치되지 않은 것이 대부분의 원인입니다. 코드 문제가 아니라 인프라 문제라서, 배포 이미지에 한글 폰트를 설치하고 로케일을 UTF-8 한국어로 맞추면 해결됩니다. 웹폰트를 사용한다면 폰트 로딩 완료 후 인쇄되도록 대기 조건도 확인해 주세요.
3. 로그인해야 보이는 화면도 PDF로 만들 수 있나요?
가능합니다. puppeteer에 쿠키나 세션 정보를 주입하거나, 인증 헤더를 붙여 인쇄 전용 페이지에 접근하게 하면 됩니다. 다만 그 페이지가 외부에 노출되지 않도록 접근 제어를 반드시 함께 설계해야 합니다.
4. PDF 생성이 느립니다. 얼마나 걸리는 게 정상인가요?
브라우저를 띄우고 페이지를 렌더링하는 시간이 있어 즉시 응답과는 거리가 있습니다. 브라우저 인스턴스를 재사용하고, 불필요한 이미지·추적 스크립트를 인쇄 페이지에서 걷어내고, 대기 조건을 필요한 요소 기준으로 좁히는 것이 가장 효과가 큽니다. 그래도 부족하면 비동기 큐 방식으로 전환하세요.
서비스에 PDF 다운로드 기능을 붙이고 싶다면?
보고서, 영수증, 리포트, 이력서 등 웹 페이지를 실시간으로 PDF로 만들어 다운로드하게 해주는 기능, 직접 구현하면 많은 테스트와 안정화가 필요한데요.
puppeteer 기반의 PDF 자동 생성 시스템 구축,
프론트와 백엔드를 모두 고려한 PDF 출력 환경 설계,
실제 서비스에 적용 가능한 구조로 완성해주는 외주 개발사 똑똑한개발자와 함께하세요!
제미나이, GPT, 클로드를 활용해 AI Agent 탑재 및 AX 전환의 전문성을 갖춘 IT 프로덕트 에이전시.
https://www.toktokhan.dev/?utm_source=landing&utm_medium=puppeteer&utm_campaign=puppeteer&utm_term=puppeteer