
PDF의 총 페이지 수라는 단일 정수는 업로드 제한, 인쇄용 용지 추산, 분할 작업, 진행률 표시줄 등 현실 세계의 놀라울 정도로 많은 결정 뒤에 자리 잡고 있습니다. 대부분의 PDF 렌더링 라이브러리는 페이지만 그릴 뿐 간단한 페이지 수를 노출하지 않으며, 단지 페이지 수를 읽기 위해 파일을 백엔드로 보내는 것은 지연 시간과 개인정보 보호 문제를 야기합니다.
JavaScript용 Spire.PDF는 WebAssembly를 통해 브라우저에서 직접 PDF 문서를 로드하고 구문 분석하므로 파일이 클라이언트를 떠나지 않습니다. 페이지 수는 단일 속성으로 제공됩니다. 루프도, 서버 왕복도, 렌더링 우회도 필요 없습니다. 이 문서에서는 해당 페이지 수를 가져오는 방법과 세 가지 실용적인 고려 사항을 살펴봅니다: 실제 페이지 수와 표시 레이블 구분하기, 암호로 보호된 파일 처리하기, 페이지를 반복할 때 오프바이원 오류 피하기.
설치 및 프로젝트 설정은 React 프로젝트에 JavaScript용 Spire.PDF 통합하기를 참조하세요. 아래 예제는 Spire.PDF가 설치되어 있고 WebAssembly 모듈이 초기화되었다고 가정합니다.
PDF 문서의 페이지 수 가져오기
PdfDocument 개체가 파일을 로드하면 Pages 속성이 페이지 컬렉션을 노출하고, 해당 컬렉션의 Count 속성이 총 페이지 수를 반환합니다. 페이지를 개별적으로 반복할 필요가 없으며, 로드 직후 페이지 수를 사용할 수 있습니다.
다음 React 컴포넌트는 전체 워크플로를 보여줍니다: PDF를 가상 파일 시스템으로 가져오고, PdfDocument를 만들고, 파일을 로드하고, Pages.Count를 읽고, 결과를 다운로드 가능한 텍스트 파일에 작성합니다.
function App() {
const getPageCount = async () => {
// Get the Spire.PDF WASM module
const pdfModule = window.wasmModule?.spirepdf;
// Check whether the module is ready
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// Load the PDF file to be counted into the VFS
const inputFileName = 'Multipage_Document.pdf';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}/data/`);
// Create a PdfDocument object and load the PDF document
const doc = new pdfModule.PdfDocument();
doc.LoadFromFile(inputFileName);
// Pages is the document's page collection; Count is the total page count
const pageCount = doc.Pages.Count;
// Write the result to the VFS
const outputFileName = 'PageCountResult.txt';
const report = `Document: ${inputFileName}\r\nTotal pages: ${pageCount}`;
window.dotnetRuntime.Module.FS.writeFile(outputFileName, report);
doc.Close();
// Read the generated file from the VFS and trigger the download
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Get PDF Page Count</h1>
<button onClick={getPageCount}>
Count Pages
</button>
</div>
);
}
export default App;
결과는 문서의 총 페이지 수를 기록하는 텍스트 파일에 작성됩니다:

프로덕션 애플리케이션에서는 일반적으로 pageCount 값을 파일에 작성하기보다 직접 사용합니다. 예를 들어 업로드를 검증하거나, 루프 경계를 설정하거나, UI에 메타데이터를 표시할 때 사용합니다. 여기서 보여 주는 파일 출력 방식은 테스트와 데모에 유용합니다.
실제 페이지 수와 페이지 레이블
개발자를 당황하게 만드는 상황이 있습니다. Pages.Count를 읽었는데 12가 나오지만, 사용자 화면의 PDF 리더는 마지막 페이지를 "8페이지"로 표시합니다. 어느 숫자가 맞을까요?
둘 다 맞습니다. 서로 다른 것을 측정하기 때문입니다. Pages.Count는 문서의 실제 페이지 수를 그대로 반환합니다. 반면 리더에 표시되는 숫자는 페이지 레이블(PDF 사양의 /PageLabels 항목)에서 나옵니다. 페이지 레이블은 게시자가 독자에게 페이지 번호가 표시되는 방식을 제어하기 위해 사용하는 표현 계층입니다. 책 출판사는 표지를 번호 매기기에서 제외하고, 앞부분에 로마 숫자(i, ii, iii)를 사용하고, 본문을 1부터 다시 시작할 수 있습니다. 그렇게 하면 다섯 번째 실제 페이지는 레이블 구성 방식에 따라 iii 또는 1로 표시될 수 있습니다.
이 구분은 애플리케이션이 사용자에게 리더에서 보는 것과 일치하는 페이지 번호를 표시해야 할 때 중요합니다. Pages.Count를 "현재 페이지"로 표시하면 페이지 레이블이 사용되는 경우 리더의 번호 매기기와 일치하지 않습니다.
실제 인덱스가 아니라 표시되는 레이블이 필요할 때는 개별 페이지 개체의 PageLabel 속성을 읽습니다:
// What label the 5th physical page displays in a reader
const page = doc.Pages.get_Item(4);
console.log(page.PageLabel);
0부터 시작하는 인덱스에 유의하세요. get_Item(4)는 다섯 번째 실제 페이지를 가져옵니다. 문서에 구성된 페이지 레이블이 없으면 PageLabel은 빈 문자열을 반환합니다. 이러한 일반적인 경우 표시되는 숫자가 실제 페이지 순서와 일치하므로 Count가 원하는 값입니다.
두 시나리오를 모두 처리하는 실용적인 방법은 먼저 PageLabel을 확인하고 비어 있으면 실제 인덱스로 대체하는 것입니다. 이렇게 하면 문서가 사용자 지정 레이블을 사용하든 아니든 애플리케이션이 항상 사용자가 보는 것과 일치하는 페이지 번호를 제공할 수 있습니다.
암호화된 PDF의 페이지 수 세기
비즈니스 환경의 많은 PDF는 열기 암호로 보호됩니다. 이는 올바른 자격 증명 없이는 문서를 읽을 수 없게 하는 보안 조치입니다. 이러한 파일을 일반 LoadFromFile 호출로 로드하려고 하면 WASM 런타임이 Pages.Count에 도달하기도 전에 오류를 throw합니다:
암호화된 문서를 열 수 없습니다. 암호가 올바르지 않습니다.
이 문제는 페이지 수를 읽는 시점이 아니라 로드 시점에 발생합니다. 문서의 내용(페이지 구조 포함)이 암호화되어 있으므로 라이브러리는 암호 없이 구문 분석할 수 없습니다. 먼저 문서의 잠금을 해제하지 않고는 페이지 수를 셀 방법이 없습니다.
해결 방법은 간단합니다. 열기 암호를 LoadFromFile의 두 번째 인수로 전달하세요. 문서의 잠금이 해제되면 암호화되지 않은 파일과 마찬가지로 페이지 수를 사용할 수 있습니다:
// The second argument is the open password
doc.LoadFromFile(inputFileName, 'spire123');
const pageCount = doc.Pages.Count;
실제 애플리케이션에서는 일반적으로 양식 필드를 통해 사용자로부터 암호를 수집하고 하드코딩하는 대신 동적으로 전달합니다. 사용자가 잘못된 암호를 입력하면 동일한 오류가 throw되므로 LoadFromFile 호출을 try/catch 블록으로 감싸고 친숙한 "잘못된 암호" 메시지를 표시하는 것이 좋습니다.
한 가지 더 주목할 점은 이 암호가 열기 암호(사용자 암호라고도 함)라는 것입니다. 이는 누가 문서를 볼 수 있는지 제어합니다. PDF에는 보기 차단 없이 편집, 인쇄 또는 복사를 제한하는 권한 암호(소유자 암호)가 있을 수도 있습니다. 페이지 수를 세는 목적에서는 열기 암호만 관련이 있습니다. 문서가 열리면 Pages.Count는 권한 제한과 관계없이 작동합니다.
페이지 수를 루프 경계로 사용하기
페이지 수를 구한 후 자연스러운 다음 단계는 모든 페이지를 반복하는 것입니다. 텍스트 추출, 썸네일 렌더링, 문서 분할 또는 일부 변환 적용 등이 있습니다. 여기에서 미묘하지만 흔한 버그가 나타납니다. Count를 포함 상한으로 사용하는 것입니다.
Pages 컬렉션은 0부터 시작하는 인덱스를 사용하므로 유효한 인덱스는 0부터 Count - 1까지입니다. 루프 조건을 < 대신 <=로 작성하면 마지막 반복에서 존재하지 않는 인덱스 Count의 페이지에 접근하려고 합니다. WASM 런타임은 기본 .NET ArgumentOutOfRangeException을 다음과 같은 메시지의 JavaScript Error로 래핑합니다:
ArgumentOutOfRange_IndexMustBeLess Arg_ParamName_Name, index
오류의 name 속성이 일반 Error일 뿐이므로 이름만으로 구분할 수 없습니다. 특별히 처리하려면 메시지 문자열을 일치시켜야 합니다.
올바른 루프는 <를 사용하므로 마지막으로 접근하는 인덱스는 Count - 1입니다:
// The upper bound is Count - 1, so use < rather than <=
for (let i = 0; i < doc.Pages.Count; i++) {
const page = doc.Pages.get_Item(i);
}
이 오프바이원 패턴은 페이지 컬렉션으로 작업할 때 가장 흔한 런타임 오류 원인 중 하나입니다. 샘플 문서가 우연히 한두 페이지뿐이라면 테스트에서 놓치기 쉽습니다. 오류는 마지막 반복에서만 나타나므로 한 페이지 문서에서는 전혀 발생하지 않습니다. 경계 조건이 올바른지 확인하려면 항상 최소 세 페이지가 있는 문서로 루프 로직을 테스트하세요.
참고 항목
- React 프로젝트에 JavaScript용 Spire.PDF 통합하기 — Spire.PDF 설치 및 React 앱에서 WebAssembly 모듈 초기화를 위한 설정 가이드입니다.
- JavaScript용 Spire.PDF 제품 페이지 — 기능, 지원되는 작업 및 브라우저 기반 PDF 처리 기능 개요.
- npm의 spire.pdf — npm을 통해 라이브러리를 설치하기 위한 패키지 페이지입니다.