데모로 파헤치는 PDF.js 1: 커스텀 viewer로 PDF 렌더링하기
2026년 10월 19일 · StreamPDF Team

들어가며
이전 시리즈 글에서 우리는 PDF.js의 아키텍처와 렌더링 계층, 메시지 통신 같은 기술적 주제를 탐구했습니다. 하지만 글을 쓰는 과정에서 몇 가지 문제를 정리하게 되었습니다:
- 글들 사이가 이어지지 않아 완전한 지식 체계를 형성할 수 없었다
- PDF.js 코드와 대응시키지 못해 글을 따라가는 과정에서 부담이 컸다
그래서 새로운 시리즈를 시작하기로 했습니다. 데모 코드에서 출발해 호출 체인을 따라가며 조금씩 PDF.js 내부로 들어가는 방식입니다. 그리고 코드의 원리를 두 가지 관점에서 설명합니다:
- 데모 해석: 사례별로 해석하고, 얕은 곳에서 깊은 곳으로 연결하며 깊이 탐구한다
- 코드 저장소 해석: 일반화된 해석으로, 블록 단위로 읽을 수 있고 각각 독립된 글이 된다
자, 처음부터 시작해 봅시다.
커스텀 viewer로 PDF 렌더링하기
아주 간단한 커스텀 viewer를 만들어 PDF를 렌더링한 뒤, 한 줄씩 코드를 따라가며 PDF.js 내부로 들어가 보겠습니다. 따라가는 과정에서는 변수 값을 코드에 대입해 보면서 실행 흐름을 더 쉽게 이해할 것입니다.
import * as pdfjsLib from 'https://cdn.jsdelivr.net/npm/pdfjs-dist@6.3.289/build/pdf.mjs';
pdfjsLib.GlobalWorkerOptions.workerSrc =
'https://cdn.jsdelivr.net/npm/pdfjs-dist@6.3.289/build/pdf.worker.mjs';
const loadingTask = pdfjsLib.getDocument({
url: 'https://assets-blog.streampdf.com/PDF32000_2008.pdf',
});
const pdf = await loadingTask.promise;
// 첫 페이지의 렌더링 데이터 가져오기
const page = await pdf.getPage(1);
// 첫 페이지의 뷰포트 데이터 가져오기
const viewport = page.getViewport({ scale: 0.5 });
// canvas를 가져와 이 canvas에 렌더링한다
const canvas = document.getElementById('canvas-id');
const context = canvas.getContext('2d');
// canvas 크기를 PDF 뷰포트 크기에 맞춘다
canvas.width = Math.floor(viewport.width);
canvas.height = Math.floor(viewport.height);
canvas.style.width = Math.floor(viewport.width) + 'px';
canvas.style.height = Math.floor(viewport.height) + 'px';
// 렌더링 파라미터 준비하기
const renderContext = {
canvasContext: context,
viewport,
};
// render 메서드를 호출해 실제 렌더링을 수행하고, await로 완료를 기다린다
await page.render(renderContext);demo: https://streampdf.com/blog/demos/a-simple-pdf-render-demo.html
1~3행: Worker 파일 경로 설정
PDF.js 라이브러리의 엔트리 파일을 임포트하고 Worker 파일의 경로를 설정합니다.
이는 PDF.js를 사용할 때 인터페이스 규격만 지킨다면 Worker를 완전히 자체 구현으로 바꿀 수도 있다는 뜻입니다.
여기서는 기본 Worker 파일 경로로 설정합니다:
GlobalWorkerOptions.workerSrc = https://cdn.jsdelivr.net/npm/pdfjs-dist@6.3.289/build/pdf.worker.mjs
4~7행: PDF 문서 로딩
getDocument 메서드를 호출해 PDF 문서를 로딩합니다. object 파라미터를 전달합니다:
- url: PDF 문서의 URL. 값은
https://assets-blog.streampdf.com/PDF32000_2008.pdf
getDocument 메서드는 Promise를 반환하며, PDF 문서 로딩이 완료되면 PDF 문서 객체로 resolve됩니다.
const loadingTask = pdfjsLib.getDocument(...)는 동기 호출입니다: 호출 살펴보기
loadingTask.promise는 getter로, Promise.withResolvers() 메서드로 생성된 Promise를 반환합니다.
여기서는 await loadingTask.promise로 PDF 문서 로딩이 완료되기를 기다린 뒤, 그 결과를 pdf 변수에 할당합니다.
8~9행: PDF 문서의 첫 페이지 가져오기
문서 초기화가 끝나면 getPage 메서드를 호출해 첫 페이지의 렌더링 데이터를 가져올 수 있습니다.
getPage 메서드는 Promise를 반환하며, 페이지 데이터 로딩이 완료되면 페이지 객체로 resolve됩니다.
const page = await pdf.getPage(1);는 비동기 호출입니다: 호출 살펴보기
첫 페이지 데이터 로딩이 완료되면 이를 page 변수에 할당합니다.
10~11행: 첫 페이지의 뷰포트 데이터 가져오기
page 데이터 로딩이 완료되면 getViewport 메서드를 호출해 첫 페이지의 뷰포트 데이터를 가져올 수 있습니다.
const viewport = page.getViewport({ scale: 0.5 });는 동기 호출입니다: 호출 살펴보기
첫 페이지의 뷰포트 데이터를 가져옵니다. object 파라미터를 전달합니다:
- scale: 확대/축소 비율. 값은
0.5
반환되는 viewport는 뷰포트의 너비와 높이를 담은 객체입니다. 값은 다음과 같습니다:
{
width: 297.5,
height: 421,
// 그 외 값은 여기서 사용하지 않으므로 생략...
}이를 viewport 변수에 할당합니다.
12~19행: canvas를 가져와 이 canvas에 렌더링하기
viewport 변수를 얻은 뒤, canvas의 width와 height를 PDF 뷰포트 크기에 맞춥니다.
canvas.width = 297; // Math.floor(viewport.width);
canvas.height = 421; // Math.floor(viewport.height);canvas의 CSS 너비와 높이도 PDF 뷰포트 크기로 설정합니다.
canvas.style.width = 297 + 'px'; // Math.floor(viewport.width) + 'px';
canvas.style.height = 421 + 'px'; // Math.floor(viewport.height) + 'px';왜 canvas의 CSS 너비와 높이를 설정해야 할까? canvas의 width, height 속성이 제어하는 것은 canvas의 픽셀 크기이고, CSS 너비와 높이는 canvas가 페이지에 표시되는 크기입니다. 일반적으로 이 둘은 같습니다. 하지만 고해상도 화면에서는 다릅니다. 예를 들어 window.devicePixelRatio가 2일 때 canvas의 픽셀 크기는 594, 842로 설정하고 CSS 너비와 높이는 297, 421로 유지해야 렌더링이 흐려지는 것을 막을 수 있습니다.
20~26행: 첫 페이지 렌더링
canvas가 준비되면 render 메서드를 호출해 첫 페이지를 렌더링할 수 있습니다.
canvas 컨텍스트, 뷰포트 데이터, 렌더링 파라미터를 준비해 render 메서드를 호출할 수 있도록 합니다.
await page.render(renderContext);는 비동기 호출입니다: 호출 살펴보기
이로써 코드 실행이 끝납니다.
정리
이번 장에서는 커스텀 viewer를 따라가며 PDF 파일의 첫 페이지를 렌더링했습니다.
다른 글과 가장 큰 차이는 변수 값을 분석 중인 코드 체인에 대입해 서로 다른 실행 경로를 확인할 수 있게 했다는 점입니다. 서로 다른 파일과 분석 글로 서로 다른 분기 경로로 들어가므로, 글을 읽는 과정이 마치 코드를 디버깅하는 것처럼 느껴집니다.
새로운 소스 코드 읽기 경험을 전해 드릴 수 있기를 바랍니다.
happy coding!
About me: StreamPDF 창작자로, AI와 PDF 분야의 연구 개발에 집중하고 있습니다. Big PDF, slow viewer? StreamPDF opens 1GB+ PDFs in seconds. Get a 6-month free tier: VIBLO_PRO_6