OpenDataLoader PDF는 두 가지 일을 합니다. 첫째, PDF에서 제목·문단·표·리스트·이미지를 올바른 읽는 순서(reading order)로 뽑아 Markdown·JSON·HTML로 내보냅니다. 이때 모든 요소에 경계상자(bounding box, 화면상 위치 좌표)가 붙어, RAG에서 "이 답의 근거가 원문 몇 페이지 어디"인지 짚어 줄 수 있습니다. 둘째, 구조 정보가 없는 PDF를 시각장애인용 스크린리더가 읽을 수 있는 Tagged PDF로 자동 변환합니다.
코어는 Java(JVM)로 작성됐고, 파이썬·Node.js·자바용 SDK를 제공합니다. 만든 곳은 한글과컴퓨터, 즉 한컴(Hancom Inc.)입니다(소스 저작권 헤더와 연락처 open.dataloader@hancom.com으로 확인). 라이선스는 Apache-2.0로, 상업적 사용에 매우 너그럽습니다.
책상 위에 뒤죽박죽 쌓인 서류 더미가 PDF라면, OpenDataLoader는 그걸 받아 "이건 제목, 이건 표의 셀, 이건 각주"라고 라벨을 붙이고 읽을 순서대로 정리해 주는 비서입니다. 정리된 결과는 컴퓨터(LLM)가 한눈에 알아보고, "몇 번째 서류 어느 줄에서 나온 정보"인지도 적어 둡니다. 게다가 이 비서는 같은 서류를 시각장애인도 읽을 수 있는 점자책 버전으로도 동시에 만들어 줍니다.
이 프로젝트가 약 2.3만 개의 별을 모은 건 운이 아니라 타이밍과 실력입니다. 지금 두 가지 거대한 수요가 동시에 폭발하고 있습니다. ① 모든 회사가 사내 PDF로 RAG·챗봇을 만들려 하는데 PDF 전처리가 병목이고, ② 유럽 접근성법(EAA, 2025-06-28 발효)·미국 ADA/Section 508·한국 디지털포용법 등으로 PDF 접근성이 법적 의무가 됐습니다(수동 보정 비용은 문서당 $50~200). OpenDataLoader는 이 둘을 한 번에 풉니다.
자체 벤치마크(opendataloader-bench, 실문서 200개)에서 하이브리드 모드가 종합 1위(0.907)를 주장합니다. 경쟁 오픈소스 파서와 비교하면:
| 엔진 | 종합점수 | 표 추출 |
|---|---|---|
| opendataloader [hybrid] | 0.907 (1위) | 0.928 |
| docling | 0.882 | 0.887 |
| marker | 0.861 | 0.808 |
| unstructured | 0.841 | 0.588 |
| opendataloader (로컬) | 0.831 | 0.489 |
| markitdown | 0.589 | 0.273 |
대부분의 오픈소스 파서는 텍스트만 뽑고 버립니다. OpenDataLoader는 모든 요소에 [left, bottom, right, top] 좌표를 붙입니다. 그래서 챗봇이 답할 때 "이 근거는 원문 3페이지 여기"라고 정확한 위치를 짚어 인용할 수 있습니다. 신뢰가 생명인 법률·의료·금융 문서에서 결정적입니다.
로컬 모드는 GPU가 필요 없고, 페이지당 약 0.015초로 매우 빠릅니다(marker는 GPU 필요 + 약 1,000배 느린 53.9초/페이지). 게다가 같은 입력이면 항상 같은 출력이 나오는 결정론적(deterministic) 동작이라, 데이터가 외부로 나가지 않습니다. 복잡한 페이지만 AI 백엔드로 보내는 '하이브리드'로 정확도를 더 끌어올릴 수 있죠.
docling 같은 도구는 Markdown·JSON만 내보내고 Tagged PDF는 못 만듭니다(보통 태그 쓰기 단계에 상용 SDK가 필요). OpenDataLoader는 미태깅 PDF→Tagged PDF 전 과정을 Apache-2.0 오픈소스로 처리합니다. 이 점이 접근성 시장에서 독보적입니다.
악의적인 PDF는 사람 눈에 안 보이는 글자(투명·0크기 폰트, 페이지 밖 텍스트)에 "이전 지시를 무시하고 ~하라" 같은 명령을 숨겨 둘 수 있습니다. 그냥 텍스트를 다 긁는 파서는 이 독을 그대로 LLM에 먹입니다.
OpenDataLoader는 투명/0크기 폰트, 페이지 밖 콘텐츠, 의심 레이어를 자동으로 필터링해 프롬프트 인젝션을 방어합니다(--sanitize). "AI가 먹을 데이터"를 만드는 도구답게, 안전을 파이프라인에 내장한 점이 돋보입니다.
이 프로젝트는 한 저장소 안에 여러 언어 패키지를 담은 모노레포입니다. 코드 바이트 비율은 Java가 압도적(약 168만 줄 분량)이고 Python·TypeScript·JavaScript·Shell이 보조합니다. 즉 진짜 엔진은 Java이고, 다른 언어는 그 Java를 호출하는 얇은 껍데기(wrapper)입니다.
java/, Maven 멀티모듈)| 부품 | 역할 |
|---|---|
| Maven (멀티모듈) | 빌드 도구. core(파싱 엔진) + cli(명령행) 모듈. 컴파일 타깃 Java 11 |
| veraPDF wcag-algorithms | 엔진의 심장. 레이아웃·접근성 알고리즘 라이브러리(아래 용어 박스) |
| jackson-databind | JSON 직렬화(결과를 JSON으로 출력) |
| commons-cli | 명령행 옵션 파싱 |
| okhttp | 하이브리드 모드에서 AI 백엔드와 HTTP 통신 |
wcag-algorithms는 "이 글자 덩어리가 제목인가 본문인가, 읽는 순서는 무엇인가" 같은 레이아웃·접근성 판단 알고리즘을 담고 있죠. OpenDataLoader가 "어떻게 오픈소스 단독으로 접근성·Tagged PDF까지 해내는가"의 기술적 비밀이 바로 이 라이브러리에 기대고 있다는 점입니다.python/opendataloader-pdf/는 "Java CLI를 위한 파이썬 래퍼"입니다. 기본 의존성이 아예 없습니다(dependencies = []) — JAR 파일을 품고 있다가 JVM 프로세스를 띄워 일을 시키는 구조죠. 다만 선택 의존성 [hybrid]를 깔면 하이브리드 백엔드 서버가 따라옵니다.
| 구성 | 내용 |
|---|---|
| 빌드 백엔드 | hatchling (커스텀 빌드로 JAR을 휠에 포함) |
| 콘솔 명령 | opendataloader-pdf · opendataloader-pdf-hybrid(서버) |
| [hybrid] 추가분 | docling[easyocr] + FastAPI + uvicorn → 하이브리드 백엔드 서버 |
| MCP 서버 패키지 | opendataloader-pdf-mcp — LLM이 도구로 호출 |
node/opendataloader-pdf/는 TypeScript로 작성된 npm 패키지(@opendataloader/pdf)로, 역시 번들된 JAR로 JVM을 띄웁니다. 배포는 네 갈래입니다.
| 채널 | 패키지 |
|---|---|
| PyPI | opendataloader-pdf, [hybrid], langchain-opendataloader-pdf |
| npm | @opendataloader/pdf |
| Maven Central | org.opendataloader:opendataloader-pdf-core |
세 SDK의 옵션이 어떻게 항상 같을까요? Java CLI가 --export-options로 옵션 명세(options.json)를 내보내면, 스크립트(generate-options.mjs)가 그걸 읽어 Python·Node용 옵션 코드를 자동 생성합니다(*_generated). "한 곳에서 정의하면 나머지는 자동"이라, 옵션이 어긋날 일이 구조적으로 없습니다. 멀티언어 프로젝트의 모범 패턴입니다.
코어의 동작은 processors 패키지로 짜인 파이프라인입니다. PDF 한 장이 들어와 여러 단계를 거쳐 구조화 데이터로 나오는 길을, 위에서 아래로 따라가 봅시다.
입력 PDF에 이미 구조 태그가 들어 있으면(작성자가 잘 만든 PDF), TaggedDocumentProcessor가 작성자의 의도를 그대로 추출합니다(--use-struct-tree). 태그가 없으면 휴리스틱(처리기들)이 글자 좌표·크기·간격을 보고 "이건 제목, 이건 표"를 추론합니다. 좋은 재료가 있으면 살리고, 없으면 손수 분석하는 영리한 분기입니다.
모든 페이지를 AI에 보내면 느리고 비쌉니다. 그래서 TriageProcessor가 페이지별 복잡도를 판정해 "간단한 페이지=로컬 결정론(빠름·무료), 복잡한 페이지(무테두리 표·스캔·수식)=AI 백엔드"로 자동 분기합니다. 응급실에서 환자 중증도를 분류(triage)하듯, 자원을 꼭 필요한 곳에만 쓰는 전략입니다. AI 백엔드는 별도 로컬 서버(FastAPI+Docling)로 떠 있고 okhttp로 통신합니다.
XYCutPlusPlusSorter가 담당합니다(--reading-order xycut).분석이 끝나면 시맨틱 요소(의미 단위)가 만들어지고, 그걸 JsonWriter·MarkdownGenerator·HtmlGenerator·AutoTagger 등 포맷별 생성기가 각자 변환합니다. 분석과 출력을 분리했기에 --format json,markdown처럼 여러 포맷을 한 번에 뽑을 수 있죠. 로컬 모드는 같은 입력이면 항상 같은 출력(결정론)이라, 테스트·재현이 쉽습니다.
JSON 출력은 이런 모양입니다. 각 요소가 타입·페이지·좌표·내용을 모두 담습니다.
// heading(제목) 요소 하나의 JSON (README 예시) { "type":"heading", "id":42, "level":"Title", "page number":1, "bounding box":[72.0, 700.0, 540.0, 730.0], // [좌,하,우,상] PDF 포인트 "font":"Helvetica-Bold", "font size":24.0, "content":"Introduction" } // 72pt = 1인치. 이 좌표 덕에 원문 위치를 정확히 짚을 수 있다
코어 패키지를 열면 processors/ 안에 XxxProcessor가 잔뜩 보입니다. 이걸 "파이프라인의 한 공정"으로 읽으면 됩니다 — 컨베이어 벨트 위에서 각 기계가 한 가지 일(표 찾기·제목 찾기·읽는 순서 정렬)만 합니다. 그리고 json/markdown/html/pdf/ 폴더는 "출구"이고, hybrid/는 "복잡한 건 외주 주는 창구"입니다.
java/.../core최상위는 언어별 폴더(java/·python/·node/)로 갈리고, 빌드를 묶는 루트 package.json이 모노레포 오케스트레이션을 합니다. 진짜 로직은 java/opendataloader-pdf-core/에 있습니다.
| 위치 | 역할 |
|---|---|
| java/.../api/ | 외부에서 호출하는 공개 진입점(OpenDataLoaderPDF·Config) — 여기부터 읽기 |
| java/.../processors/ | 표·제목·리스트·문단·숨김텍스트 등 파이프라인 단계 + 읽는 순서 정렬 |
| java/.../hybrid/ | Docling·Hancom AI 클라이언트, Triage, 스키마 변환 |
| java/.../autotagging/ | Tagged PDF·접근성 태그 생성(이 프로젝트의 차별 무기) |
| python / node 래퍼 | JAR을 품고 JVM을 띄우는 얇은 껍데기. 옵션 코드는 자동 생성 |
"PDF는 왜 그냥 긁으면 안 되는가"를 코드로 이해할 수 있습니다. 좌표·폰트 정보로 제목/표/문단을 분류하고, XY-Cut++로 다단 레이아웃을 바로잡는 과정은, 문서 AI(Document Intelligence) 분야의 핵심 기술입니다. RAG 파이프라인을 제대로 만들려는 사람에게 특히 값집니다.
두 단으로 된 PDF를 골라, 일반 추출기(예: 단순 pdftotext)와 OpenDataLoader의 결과를 나란히 비교해 보세요. "읽는 순서가 살아 있다"는 게 RAG 품질에 얼마나 큰 차이를 만드는지 눈으로 확인하는 게 목표입니다.
평소 접하기 어려운 접근성(a11y) 도메인을 다뤄 봅니다. 태그 없는 PDF를 스크린리더가 읽을 수 있는 Tagged PDF로 바꾸는 autotagging/은, "보이는 것"과 "기계가 읽는 구조"가 어떻게 다른지를 가르쳐 줍니다. 법적 의무가 된 분야라 실무 가치도 높습니다.
숨은 텍스트·프롬프트 인젝션을 추출 단계에서 걸러내는 설계는, "AI가 먹을 데이터"를 만드는 모든 파이프라인이 고민해야 할 주제입니다. 데이터 위생(data hygiene)을 입구에서 처리하는 사고방식을 배울 수 있습니다.
하나의 Java 엔진을 Python·Node·Java에서 똑같이 쓰게 하고, 옵션을 한 곳에서 정의해 자동 생성하는 패턴(--export-options → generate-options.mjs)은, 여러 언어로 라이브러리를 배포하는 팀에게 그대로 유용한 실전 노하우입니다.
"쉬운 건 빠른 로컬, 어려운 것만 비싼 AI"라는 하이브리드 전략은, LLM을 실제 서비스에 넣을 때 비용을 통제하는 보편적 패턴입니다. TriageProcessor가 어떤 신호로 복잡도를 판정하는지 따라가 보면, AI 시스템의 비용 설계 감각이 생깁니다.
가장 큰 장점은 로컬 모드에 GPU가 필요 없다는 점입니다. JVM만 있으면 됩니다. 한 가지 주의할 성능 포인트는, convert() 호출마다 JVM 프로세스를 새로 띄우므로 파일을 하나씩이 아니라 배치로 한 번에 넘기는 게 빠릅니다.
| 항목 | 요구사항 |
|---|---|
| 필수 | Java 11+ (JVM). Python SDK 사용 시 Python 3.10+ |
| 로컬 모드 | GPU 불필요. CPU에서 약 60+ 페이지/초(결정론적) |
| 하이브리드 모드 | 별도 백엔드 서버(FastAPI+Docling+EasyOCR) — OCR/수식/복잡 표용 |
| 호출 성능 | 호출당 JVM 기동 → 여러 파일은 배치로 한 번에 전달 권장 |
| 미지원 | Word/Excel/PPT 처리 안 함(PDF 전용) |
| 무료/유료 경계 | 추출·OCR·수식·자동 태깅까지 무료. PDF/UA export·Accessibility Studio는 엔터프라이즈 유료 |
# 로컬(빠름, 기본) — pip 한 줄
pip install -U opendataloader-pdf
# Python에서 — 여러 파일을 배치로 한 번에 import opendataloader_pdf opendataloader_pdf.convert( input_path=["a.pdf", "b.pdf", "folder/"], output_dir="output/", format="markdown,json") # 여러 포맷 동시 출력
# 하이브리드(복잡 표·OCR·수식) — 서버 + 처리 2단계 pip install -U "opendataloader-pdf[hybrid]" opendataloader-pdf-hybrid --port 5002 # 터미널1: 백엔드 서버 opendataloader-pdf --hybrid docling-fast a.pdf # 터미널2: 처리 # 접근성 태깅: opendataloader-pdf --format tagged-pdf a.pdf
주요 CLI 옵션(실제 확인): --format(json/markdown/html/text/tagged-pdf), --sanitize(안전 필터), --use-struct-tree(원본 태그 사용), --reading-order(기본 xycut/off), --pages, --image-output, --include-header-footer, --hybrid, --threads, --to-stdout, --export-options.
pip install opendataloader-pdf 후, 손에 있는 PDF 하나를 format="markdown,json"으로 변환해 보세요. Markdown은 사람이 읽기 좋게, JSON은 좌표·타입이 다 담겨 있습니다. 두 출력을 비교하며 "구조화 데이터가 뭔지" 감을 잡습니다.
JSON의 bounding box 좌표를 읽어, 특정 제목/표 위에 사각형을 그린 이미지를 만들어 보세요(예: PyMuPDF로 렌더). "이 텍스트가 원문 어디서 왔는가"를 시각화하면, RAG에서 출처 인용이 왜 강력한지 체감하게 됩니다.
--format tagged-pdf로 접근성 PDF를 만들고, 무료 스크린리더(예: NVDA)로 읽어 보세요. 변환 전/후를 비교하면 "구조 태그가 있고 없고"가 시각장애인 사용자에게 어떤 차이인지 직접 확인할 수 있습니다.
복잡한 표가 든 PDF를 골라 OpenDataLoader(로컬·하이브리드)와 markitdown·docling의 결과를 비교해 보세요. 벤치마크 수치(0.928 vs 0.273 등)가 실제로 체감되는지, 어떤 표에서 하이브리드가 필요한지 스스로 판단해 봅니다.
LangChain 통합(langchain-opendataloader-pdf)을 써서, PDF→구조화 chunk→벡터DB→질의응답까지 잇는 미니 RAG를 만들어 보세요. 답변에 경계상자 기반 출처를 함께 보여주면, "신뢰할 수 있는 문서 챗봇"의 핵심을 완성하게 됩니다.
| 주차 | 주제 | 할 일 |
|---|---|---|
| 1주차 | 문서 레이아웃 분석 | PDF 좌표 모델, 읽는 순서 문제, XY-Cut 알고리즘 개념. processors/ 구조 파악 |
| 2주차 | PDF 접근성 | Tagged PDF·PDF/UA·WCAG 기초, veraPDF 역할. autotagging/ + 스크린리더 실습 |
| 3주차 | 멀티언어 패키징 | JVM 래핑(Python/Node), 옵션 자동 생성, Maven/PyPI/npm 배포 구조 |
| 4주차 | RAG 통합·하이브리드 | chunking·임베딩·출처 인용, Triage 비용 설계, LangChain/LlamaIndex 연동 |
코어가 Java라 진입장벽이 있어 보이지만, Python SDK로 동작을 먼저 익힌 뒤 궁금한 단계만 Java 코어를 들여다보는 순서를 권합니다. 1~2주차(파싱·접근성)는 문서 AI·접근성이라는 희소하면서 수요 큰 영역이고, 3~4주차(패키징·RAG)는 어떤 백엔드/AI 엔지니어에게도 바로 쓸모가 있습니다.
| 용어 | 의미 |
|---|---|
| AI-ready data | LLM/RAG에 곧바로 넣을 수 있게 시맨틱 타입·순서가 보존된 구조화 출력 |
| 읽는 순서(Reading Order) | 시각적 배치(다단 등)와 무관하게 사람이 읽는 논리적 순서로 텍스트 재배열 |
| XY-Cut++ | 페이지를 X/Y로 재귀 분할해 다단·사이드바를 올바른 순서로 정렬하는 알고리즘 |
| 레이아웃 분석 | 좌표·폰트에서 제목·문단·표·이미지 등 구조 요소와 경계를 식별 |
| 경계상자(Bounding box) | 요소의 [좌,하,우,상] 좌표. RAG의 출처 인용·하이라이트에 필수 |
| Tagged PDF | 문서 구조(제목/표/순서)를 태그 트리로 담은 PDF. 스크린리더가 읽을 수 있게 함 |
| PDF/UA | PDF 접근성 준수 표준(ISO 14289). UA export는 이 프로젝트의 유료 애드온 |
| veraPDF | PDF/A·PDF/UA 검증 산업 표준 오픈소스. 그 wcag-algorithms가 엔진의 핵심 의존성 |
| OCR | 스캔 이미지에서 글자 추출(광학 문자 인식). 하이브리드 모드에서 80+ 언어 지원 |
| RAG | 외부 문서를 검색해 LLM 답변을 보강하는 기법. PDF 전처리 품질이 좌우 |
| 하이브리드 / Triage | 간단 페이지는 로컬, 복잡 페이지만 AI 백엔드로 자동 분기하는 비용·정확도 절충 |
| 프롬프트 인젝션 필터 | PDF에 숨긴 악성 지시(투명·0크기 폰트, 페이지 밖 텍스트)를 추출 단계에서 제거 |
| 결정론적(deterministic) | 같은 입력이면 항상 같은 출력. 테스트·재현이 쉬움(로컬 모드 특성) |
| 모노레포 | 여러 언어 패키지를 한 저장소에 담는 방식. 루트 package.json이 빌드 오케스트레이션 |