전처리 전략 (Document Preprocessing)
PDF, DOCX, PPTX 등 비정형 문서는 텍스트 추출 → 구조 변환 → 청킹의 전처리 파이프라인이 필요합니다. 특히 이미지, 표, 수식 등 비텍스트 요소를 포함하는 문서는 전처리 품질이 RAG 성능에 직접적인 영향을 미칩니다.
Docling — 멀티포맷 문서 변환
Docling은 IBM Research에서 개발한 문서 변환 라이브러리로, PDF, DOCX, PPTX, HTML, 이미지 등 다양한 형식을 구조화된 문서 모델(DoclingDocument)로 변환합니다. OCR, 레이아웃 분석, 표 인식, 이미지 추출을 포함하는 종합 파이프라인을 제공합니다.
기본 변환
이미지 추출 (Markdown 링크)
표 추출
OCR 활성화
from docling.document_converter import DocumentConverter
converter = DocumentConverter()
result = converter.convert("document.pdf")
# Markdown으로 내보내기
markdown_text = result.document.export_to_markdown()
print(markdown_text)
Docling은 문서 내 이미지를 추출하여 별도 파일로 저장하고, Markdown에서 **이미지 링크(![]())**로 참조합니다.from docling.document_converter import DocumentConverter
from docling.datamodel.base_models import InputFormat
from docling.document_converter import PdfFormatOption
from docling.datamodel.pipeline_options import PdfPipelineOptions
from pathlib import Path
# 이미지 추출 옵션 설정
pipeline_options = PdfPipelineOptions()
pipeline_options.images_scale = 2.0 # 이미지 해상도 배율
pipeline_options.generate_page_images = False
pipeline_options.generate_picture_images = True # 그림/차트 이미지 추출
converter = DocumentConverter(
format_options={
InputFormat.PDF: PdfFormatOption(
pipeline_options=pipeline_options,
)
}
)
result = converter.convert("document.pdf")
# 이미지를 파일로 저장하면서 Markdown 내보내기
output_dir = Path("output")
output_dir.mkdir(exist_ok=True)
image_dir = output_dir / "images"
image_dir.mkdir(exist_ok=True)
# 문서 내 이미지(Picture) 요소 순회 및 저장
for idx, (element, _level) in enumerate(result.document.iterate_items()):
if hasattr(element, "image") and element.image is not None:
image_path = image_dir / f"figure_{idx}.png"
element.image.pil_image.save(str(image_path))
print(f"이미지 저장: {image_path}")
# Markdown 내보내기 (이미지 경로 포함)
markdown_text = result.document.export_to_markdown(
image_mode="referenced", # 이미지를 참조 형태로 내보내기
)
# 결과 Markdown에 이미지가 링크로 포함됨:
# 
print(markdown_text)
내보낸 Markdown에서 이미지는 다음과 같은 형태로 표현됩니다:## 2.1 Architecture
The Transformer architecture uses self-attention...

As shown in the figure above, the encoder-decoder...
Docling은 PDF 내 표를 자동으로 인식하고 Markdown 표로 변환합니다.from docling.document_converter import DocumentConverter
converter = DocumentConverter()
result = converter.convert("report.pdf")
# 표 요소만 추출
for element, _level in result.document.iterate_items():
if hasattr(element, "label") and element.label == "table":
# Markdown 표로 내보내기
print(element.export_to_markdown())
print("---")
복잡한 병합 셀, 다단 헤더도 처리하며 구조를 보존합니다. 스캔된 PDF나 이미지 기반 문서는 OCR을 활성화하여 텍스트를 추출합니다.from docling.document_converter import DocumentConverter
from docling.datamodel.pipeline_options import PdfPipelineOptions
from docling.datamodel.base_models import InputFormat
from docling.document_converter import PdfFormatOption
pipeline_options = PdfPipelineOptions()
pipeline_options.do_ocr = True # OCR 활성화
pipeline_options.ocr_options.lang = ["ko", "en"] # 한국어 + 영어
converter = DocumentConverter(
format_options={
InputFormat.PDF: PdfFormatOption(
pipeline_options=pipeline_options,
)
}
)
result = converter.convert("scanned_document.pdf")
markdown_text = result.document.export_to_markdown()
Docling + 청킹 파이프라인
Docling의 내장 HybridChunker는 문서 구조를 인식하면서 토큰 기반 크기 제어를 동시에 수행합니다.
from docling.document_converter import DocumentConverter
from docling.chunking import HybridChunker
# 1. 문서 변환
converter = DocumentConverter()
result = converter.convert("document.pdf")
# 2. 구조 인식 + 토큰 기반 청킹
chunker = HybridChunker(
tokenizer="gpt-4o", # 토크나이저
max_tokens=512, # 청크당 최대 토큰
merge_peers=True, # 같은 레벨의 짧은 섹션 병합
)
chunks = list(chunker.chunk(result.document))
for chunk in chunks:
print(f"[{chunk.meta.headings}] ({len(chunk.text)} chars)")
print(chunk.text[:200])
print("---")
| 파라미터 | 설명 | 기본값 |
|---|
tokenizer | 토크나이저 (모델명 또는 객체) | "gpt2" |
max_tokens | 청크당 최대 토큰 수 | 512 |
merge_peers | 같은 레벨의 짧은 섹션을 병합 | True |
HybridChunker는 헤딩, 단락, 표, 리스트 등 문서 구조를 인식하여 의미 단위를 존중합니다. 단순히 토큰 수로 자르지 않으므로, Docling 변환 결과에 가장 적합한 청킹 도구입니다.
지원 형식
| 입력 형식 | 레이아웃 분석 | 표 인식 | 이미지 추출 | OCR |
|---|
| PDF | O | O | O | O |
| DOCX | O | O | O | - |
| PPTX | O | - | O | - |
| HTML | O | O | O | - |
| 이미지 (PNG, JPG) | - | - | - | O |
| Markdown | O | O | - | - |
PyMuPDF4LLM — PDF 전용 변환
PyMuPDF4LLM은 PyMuPDF(fitz) 기반의 PDF 전용 변환 도구로, PDF를 LLM에 최적화된 Markdown으로 변환합니다. 빠른 속도와 정확한 텍스트 추출이 강점입니다.
기본 변환
이미지 추출 (Markdown 링크)
페이지별 변환
표 추출
import pymupdf4llm
# PDF -> Markdown 변환
md_text = pymupdf4llm.to_markdown("document.pdf")
print(md_text)
write_images=True 옵션으로 PDF 내 이미지를 별도 파일로 추출하고, Markdown에서 ![]()로 참조합니다.import pymupdf4llm
from pathlib import Path
# 이미지 저장 디렉토리 생성
image_dir = Path("output/images")
image_dir.mkdir(parents=True, exist_ok=True)
# 이미지 추출 + Markdown 변환
md_text = pymupdf4llm.to_markdown(
"document.pdf",
write_images=True, # 이미지를 파일로 저장
image_path=str(image_dir), # 이미지 저장 경로
image_format="png", # 이미지 형식 (png, jpg)
dpi=150, # 이미지 해상도
)
# Markdown 저장
output_path = Path("output/document.md")
output_path.write_text(md_text, encoding="utf-8")
생성된 Markdown 파일 내에서 이미지는 다음과 같이 참조됩니다:# Introduction
This paper presents a novel approach to...

The architecture shown above demonstrates...
## Related Work
Previous studies have explored...

이미지 파일명은 {pdf파일명}-{페이지번호}-{이미지인덱스}.{확장자} 형태로 자동 생성됩니다.
특정 페이지만 선택적으로 변환할 수 있습니다.import pymupdf4llm
# 특정 페이지만 변환 (0-indexed)
md_text = pymupdf4llm.to_markdown(
"document.pdf",
pages=[0, 1, 2], # 첫 3페이지만
)
# 페이지 단위로 분리된 결과 (LangChain Document 형태)
docs = pymupdf4llm.to_markdown(
"document.pdf",
page_chunks=True, # 페이지별 분리
)
for doc in docs:
print(f"Page {doc['metadata']['page']}: {len(doc['text'])} chars")
PyMuPDF4LLM은 PDF 내 표를 Markdown 표 형식으로 변환합니다.import pymupdf4llm
md_text = pymupdf4llm.to_markdown(
"report.pdf",
show_progress=True, # 변환 진행률 표시
)
# 변환된 Markdown에서 표가 자동으로 감지됨:
# | Column A | Column B | Column C |
# |----------|----------|----------|
# | Value 1 | Value 2 | Value 3 |
PyMuPDF4LLM + 청킹 파이프라인
변환된 Markdown을 LangChain 또는 Chonkie로 청킹하는 전체 파이프라인입니다.
import pymupdf4llm
from langchain_text_splitters import MarkdownHeaderTextSplitter
from langchain_core.documents import Document
from pathlib import Path
# 1. PDF -> Markdown 변환 (이미지 포함)
image_dir = Path("output/images")
image_dir.mkdir(parents=True, exist_ok=True)
md_text = pymupdf4llm.to_markdown(
"document.pdf",
write_images=True,
image_path=str(image_dir),
image_format="png",
dpi=150,
)
# 2. Markdown 헤딩 기반 청킹
splitter = MarkdownHeaderTextSplitter(
headers_to_split_on=[
("#", "h1"),
("##", "h2"),
("###", "h3"),
],
)
chunks = splitter.split_text(md_text)
# 3. 이미지 링크가 포함된 청크 확인
for chunk in chunks:
has_image = "![" in chunk.page_content
print(f"[{chunk.metadata}] {'(이미지 포함)' if has_image else ''}")
print(chunk.page_content[:200])
print("---")
이미지 링크가 포함된 청크는 멀티모달 RAG 파이프라인에서 활용할 수 있습니다. 이미지 경로를 메타데이터로 추출하여, 검색 시 관련 이미지를 함께 제공하면 답변 품질이 향상됩니다.
주요 옵션
| 옵션 | 설명 | 기본값 |
|---|
write_images | 이미지를 파일로 추출 | False |
image_path | 이미지 저장 디렉토리 | "." |
image_format | 이미지 형식 ("png", "jpg") | "png" |
dpi | 이미지 해상도 (DPI) | 150 |
pages | 변환할 페이지 목록 (0-indexed) | 전체 |
page_chunks | 페이지별 분리 반환 | False |
show_progress | 진행률 표시 | False |
Docling vs PyMuPDF4LLM
| 항목 | Docling | PyMuPDF4LLM |
|---|
| 지원 형식 | PDF, DOCX, PPTX, HTML, 이미지 | PDF 전용 |
| 변환 속도 | 느림 (레이아웃 분석 포함) | 매우 빠름 |
| 레이아웃 분석 | AI 기반 레이아웃 분석 | 규칙 기반 |
| 표 인식 | 복잡한 병합 셀 처리 | 기본 표 변환 |
| 이미지 추출 | 구조화된 메타데이터 포함 | 파일 추출 + MD 링크 |
| OCR | 내장 (다국어 지원) | 미지원 |
| 청킹 | HybridChunker 내장 | 외부 도구 필요 |
| 의존성 | 무거움 (torch 등) | 가벼움 (PyMuPDF만) |
| 적합한 경우 | 복잡한 레이아웃, 다양한 형식 | PDF 빠른 처리, 단순 레이아웃 |
이미지 추출 시 저작권에 유의하세요. 추출된 이미지를 포함한 청크를 벡터 DB에 저장할 때, 이미지 파일의 저장 경로가 프로덕션 환경에서도 유효한지 확인해야 합니다.
도구 종합 비교
| 도구 | 입력 형식 | 핵심 기능 | 이미지 추출 | 적합한 경우 |
|---|
| LangChain Splitters | 텍스트, MD, HTML, Code | 구조 기반 분할 | - | LangChain 파이프라인 |
| Chonkie | 텍스트 | 토큰 기반 경량 청킹 | - | 토큰 관리 + 다양한 전략 |
| Docling | PDF, DOCX, PPTX, HTML | 문서 변환 + 청킹 | O (MD 링크) | 복잡한 레이아웃, 다형식 |
| PyMuPDF4LLM | PDF | PDF -> MD 변환 | O (MD 링크) | 빠른 PDF 전처리 |
추천 조합: 단순 PDF는 PyMuPDF4LLM → MarkdownHeaderTextSplitter, 복잡한 PDF(표, 다단, 스캔)는 Docling → HybridChunker, 텍스트 문서는 Chonkie RecursiveChunker를 사용하세요.