문서 파싱 작업을 하면서 항상 고민이 하나 있었다.
최근 나온 VLM들을 보면 OCR 성능이 정말 많이 올라왔다. 복잡한 문서 구조도 예전보다 훨씬 잘 잡는다. 다만 학습이나 평가는 대부분 영어와 중국어 위주라 한국어에서는 가끔씩 세세한 오타가 발생한다.
보통은 그냥 넘어갈 만한 오타지만, 문서 파싱에서는 한 글자 차이가 치명적으로 다가올 때가 있다.
그래서 이미지로 된 스캔본이 아니라 텍스트가 들어 있는 PDF라면 최대한 PyPDF나 pypdfium2 같은 단순 파싱 라이브러리를 쓰고 싶었다. 이미 PDF 안에 원문 텍스트가 있는데 굳이 다시 이미지로 렌더링하고 OCR을 태우면서 오타 가능성을 만들 필요는 없기 때문이다.
문제는 자동화였다.
PDF를 직접 열어보기 전에는 이게 텍스트 기반인지, 스캔본인지, 중간에 이미지만 들어간 페이지가 섞였는지 판단하기가 애매하다. 문서가 들어올 때마다 눈으로 확인할 수도 없는 노릇이다. 이걸 앞단에서 판단해 줄 무언가가 필요했는데, 얼마 전 pdf-inspector가 눈에 띄었다.
pdf-inspector가 뭐냐면
pdf-inspector는 Firecrawl이 공개한 Rust 기반 PDF 분류 및 텍스트 추출 라이브러리다.
OCR이나 머신러닝 모델 없이 PDF 내부의 콘텐츠 스트림을 확인해 문서를 TextBased, Scanned, ImageBased, Mixed 네 가지로 나눈다. 텍스트 기반 PDF라면 위치 정보와 레이아웃을 살려 텍스트를 추출한 뒤 마크다운으로 바꿔준다.
쉽게 말해, PDF를 OCR에 보내기 전에 "이 문서는 그냥 텍스트를 뽑아도 되는지" 먼저 판단해 주는 도구다.
Firecrawl은 이 라이브러리를 텍스트 기반 PDF는 로컬에서 200ms 이내로 처리하고, OCR이 필요 없는 약 54%의 PDF에서 외부 OCR 호출을 건너뛰기 위해 만들었다고 한다.
PDF 타입을 어떻게 구분하나?
분류 방식은 생각보다 단순하다.
PDF의 xref 테이블과 페이지 트리를 읽고, 각 페이지의 콘텐츠 스트림에서 텍스트 연산자(Tj, TJ)와 이미지 연산자(Do)가 있는지 확인한다. 페이지 전체를 이미지로 렌더링하거나 모델을 실행하지 않으니 빠를 수밖에 없다.
결과에는 문서 타입과 함께 0.0~1.0 사이의 confidence, pages_needing_ocr가 들어온다. Mixed PDF라면 문서 전체가 아니라 텍스트가 없는 페이지만 골라 OCR로 넘길 수 있다는 얘기다.
개인적으로는 이 부분이 가장 마음에 들었다.
실제 문서에는 앞부분은 텍스트인데 중간에 서명된 스캔 페이지 하나가 섞이는 식의 경우가 꽤 있다. 기존에는 안전하게 처리하려고 문서 전체를 VLM에 태웠는데, 이제는 필요한 페이지만 골라낼 수 있다.
단순 텍스트 추출만 하는 건 아니다
pdf-inspector는 PDF 타입만 분류하는 도구가 아니다.
폰트 크기를 보고 H1~H4 제목을 구분한다. 글머리표와 번호 목록, 코드 블록, 링크, 굵게와 기울임 같은 서식도 마크다운으로 옮긴다. 여러 단으로 나뉜 문서의 읽기 순서와 RTL 텍스트도 처리한다.
표는 PDF 드로잉 연산으로 그려진 사각형을 찾는 방식과 텍스트 정렬을 이용한 휴리스틱 방식을 함께 사용한다. Type0/Identity-H 계열 CID 폰트도 ToUnicode CMap을 해석해 복원한다.
한국어 PDF를 다룰 때는 이 폰트 인코딩 처리가 특히 중요하다. 다만 모든 PDF가 정상적인 ToUnicode 정보를 가지고 있는 건 아니다. 인코딩이 깨졌거나 GID 기반으로 저장된 문서는 여전히 글자가 깨질 수 있는데, pdf-inspector는 이런 경우 has_encoding_issues나 needsOcr 신호를 반환해 OCR로 다시 보낼 수 있게 해준다.
쉽게 말해, 무조건 네이티브 파싱을 고집하는 게 아니라 결과를 믿어도 되는지 확인한 뒤 fallback을 걸 수 있는 구조다.
설치 및 사용법
Python 패키지는 PyPI에 올라와 있어서 바로 설치할 수 있다.
pip install pdf-inspector
import pdf_inspector
result = pdf_inspector.process_pdf("document.pdf")
print(result.pdf_type)
print(result.confidence)
print(result.pages_needing_ocr)
print(result.has_encoding_issues)
print(result.markdown)
간단히 분류만 하고 싶다면 detect_pdf를 사용하면 된다.
import pdf_inspector
result = pdf_inspector.detect_pdf("document.pdf")
if result.pdf_type == "text_based":
print("로컬 텍스트 추출 가능")
else:
print("OCR 필요 페이지:", result.pages_needing_ocr)
Node.js는 @firecrawl/pdf-inspector, Rust는 crates.io의 pdf-inspector 패키지를 설치하면 된다. CLI와 브라우저용 WebAssembly도 제공한다.
벤치마크
공식 저장소에는 opendataloader-bench의 PDF 200개로 테스트한 결과가 공개되어 있다. OCR과 모델 기반 파서는 빼고 로컬 텍스트 추출 엔진끼리 비교한 수치다.
| 엔진 | 종합 | 읽기 순서 | 표 | 제목 | 200개 처리 시간 |
|---|---|---|---|---|---|
| pdf-inspector | 0.875 | 0.915 | 0.814 | 0.788 | 2.8초 |
| LiteParse | 0.870 | 0.908 | 0.693 | 0.811 | 13.9초 |
| OpenDataLoader | 0.843 | 0.912 | 0.489 | 0.760 | 9.8초 |
| PyMuPDF4LLM | 0.735 | 0.886 | 0.401 | 0.424 | 15.5초 |
| MarkItDown | 0.583 | 0.879 | 0.000 | 0.000 | 6.7초 |
2026년 7월 16일 Apple M4 Pro에서 3번 실행한 중앙값이라고 한다. 속도도 가장 빨랐고, 특히 표 점수가 꽤 높게 나왔다.
다만 벤치마크는 참고만 하는 게 좋다. 한국어 문서만 따로 평가한 결과도 아니고, 실제 결과는 문서에 쓰인 폰트와 레이아웃에 따라 크게 달라질 수 있다.
나도 직접 한국어 보고서나 계약서 PDF를 넣어보고 어느 정도까지 안정적으로 나오는지 확인해 볼 생각이다.
실제 파이프라인에서는 이렇게 쓸 것 같다
내가 원했던 구조는 아래와 같다.
- PDF가 들어오면 pdf-inspector로 타입과 페이지별 상태를 확인한다.
- 텍스트 기반이고 인코딩 문제가 없으면 네이티브 텍스트를 그대로 사용한다.
- 스캔본이나 이미지 기반 페이지, 인코딩이 깨진 페이지만 OCR 또는 VLM로 보낸다.
- 표나 다단 문서처럼 구조가 복잡한 경우에만 VLM 결과와 비교한다.
이렇게 하면 한국어 OCR에서 생기는 자잘한 오타를 줄이면서, 스캔 문서와 복잡한 레이아웃은 기존 VLM 파이프라인으로 처리할 수 있다. 덤으로 OCR 비용과 처리 시간도 줄어든다.
물론 pdf-inspector가 VLM을 완전히 대체하는 도구는 아니다. 스캔본에는 여전히 OCR이 필요하고, 시각적으로 복잡한 표나 수식은 모델 기반 파서가 더 잘 처리할 수 있다.
그래도 모든 PDF를 무조건 OCR에 태우는 것보다는 훨씬 합리적인 앞단 필터로 보인다.
마무리
정리하면 아래와 같다.
- OCR 없이 PDF 타입을 빠르게 분류한다.
- 텍스트 기반 PDF는 위치와 레이아웃을 살려 마크다운으로 변환한다.
- Mixed 문서에서는 OCR이 필요한 페이지만 골라낼 수 있다.
- Python, Node.js, Rust, CLI, WebAssembly를 지원한다.
- 주의: 스캔본과 깨진 폰트 인코딩은 여전히 OCR fallback이 필요하다.
- 주의: 한국어 문서 정확도는 직접 테스트해 볼 필요가 있다.
문서 파싱을 하다 보면 구조를 잘 잡는 것보다 원문에 있는 글자를 틀리지 않고 가져오는 게 더 중요할 때가 있다. 나처럼 텍스트 PDF까지 전부 VLM에 태우는 게 찜찜했다면 한번 테스트해 볼 만한 라이브러리 같다.
나도 실제 파이프라인에 붙여서 한국어 문서와 Mixed PDF를 몇 개 돌려본 뒤 후기를 작성해 볼 예정이다.
참고 자료
- pdf-inspector GitHub: https://github.com/firecrawl/pdf-inspector
- pdf-inspector 공식 페이지: https://firecrawl.github.io/pdf-inspector/
- Python API 문서: https://github.com/firecrawl/pdf-inspector/blob/main/docs/python.md
- PyTorchKR 소개 글: https://discuss.pytorch.kr/t/pdf-inspector-ocr-pdf-rust-feat-firecrawl/11255
'알쓸신잡' 카테고리의 다른 글
| AI 글쓰기 티 없애기 - im-not-ai와 no-ai-slop 비교 및 사용법 (0) | 2026.07.24 |
|---|---|
| ClaudeCode 로컬 모델 사용법 (1) | 2026.02.25 |
| BOJ Mate 개발기 (0) | 2026.02.20 |
| Cloudflare R2 가입 & 설정 가이드 (0) | 2026.02.05 |
| Claudian-옵시디언에서 Claude Code 사용하기 (0) | 2026.02.04 |