05 · Extraction & Rendering
이 문서가 답하는 질문: 업로드된 PDF / docx / xlsx / 이미지에서 텍스트 · 메타 · 이미지 · 썸네일 을 어떻게 뽑아내는가? 어떤 도구를, 어떤 순서로 호출하는가? 사용처: 검색 인덱싱, 미리보기, OCR 파이프라인, 컴플라이언스 검사
한 줄 답 (Pyramid Top)
추출은 “무엇이 있는가 (메타) → 본문 텍스트 → 시각 표현 (썸네일)” 의 3단 파이프라인이다. 가벼운 도구(
pdftotext,mammoth,openpyxl)가 첫 번째 패스, Apache Tika 가 만능 fallback, 시각 표현은 LibreOffice / pdf.js / Poppler 의 헤드리스 렌더가 끝낸다.
Why — 왜 통합 파이프라인이 필요한가
업로드 1 개에서 다음을 모두 만들어야 한다:
| 목적 | 필요한 산출물 |
|---|---|
| 검색 | 본문 텍스트 + 메타 (제목·저자·날짜) |
| 미리보기 | 첫 페이지 PNG / 전체 페이지 PNG |
| 컴플라이언스 | 매크로 / 외부 링크 / GPS 검출 |
| 다운로드 변환 | docx → PDF, xlsx → CSV |
각 포맷마다 다른 도구가 필요 → 포맷 → 도구 매핑 이 파이프라인의 핵심.
또한 샌드박스 — 신뢰 못 할 입력을 서버 main process 에서 처리하면 RCE / DoS 위험.
도커 컨테이너 / Lambda / firejail 로 격리.
How — 3단 파이프라인의 구체
단계 1 — 식별 (앞 단원의 결과)
file.bin → 매직 검사 → MIME 결정 → 파이프라인 분기file --mime-type 또는 libmagic, 보안 민감 시 직접 매직 검사.
단계 2 — 메타 추출 (가벼운 패스)
| 포맷 | 도구 | 명령 |
|---|---|---|
pdfinfo (Poppler) | pdfinfo file.pdf | |
| 이미지 | exiftool | exiftool photo.jpg |
| 비디오 | ffprobe | ffprobe -v error -show_format file.mp4 |
| OOXML | unzip + docProps/core.xml | unzip -p f.docx docProps/core.xml |
| 모든 것 | Apache Tika | tika --metadata file |
Tika 의 강점: 1,400+ 포맷 지원, 자바 한 도구로 끝.
$ tika --metadata report.pdf
Author: Finance Team
Content-Type: application/pdf
Creation-Date: 2026-05-01T09:00:00Z
producer: LibreOffice 7.5
xmpTPg:NPages: 12단계 3 — 텍스트 추출 (포맷별 최적 도구)
| 포맷 | 1순위 (가벼움) | 2순위 (만능) | 3순위 (스캔 PDF) |
|---|---|---|---|
pdftotext -layout | pdfplumber (표) | pdftoppm + tesseract (OCR) | |
| docx | mammoth (HTML/MD) | python-docx | Tika |
| xlsx | openpyxl / pandas | Tika | — |
| pptx | python-pptx | Tika | — |
| HWP (한글) | pyhwp | LibreOffice → docx → mammoth | OCR |
| 이미지 | — | — | tesseract -l kor+eng |
| RTF | unrtf | LibreOffice → txt | — |
# 일반 PDF
$ pdftotext -layout report.pdf - | head -5
# 표가 있는 PDF
$ python -c "
import pdfplumber
with pdfplumber.open('finance.pdf') as p:
for table in p.pages[0].extract_tables():
for row in table: print(row)
"
# 스캔 PDF
$ pdftoppm -r 300 scan.pdf page -png
$ for f in page-*.png; do tesseract "$f" stdout -l kor+eng; done > all.txt
# docx → Markdown (mammoth)
$ npx mammoth report.docx --output-format=markdown > report.md
# Tika 만능 fallback
$ tika --text any.unknown > extracted.txt단계 4 — 렌더링 (썸네일·미리보기)
| 입력 | 도구 | 명령 |
|---|---|---|
| PDF → PNG | Poppler | pdftoppm -r 150 a.pdf out -png |
| PDF → PNG | MuPDF | mutool draw -o out-%d.png a.pdf |
| docx → PDF | LibreOffice | libreoffice --headless --convert-to pdf a.docx |
| pptx → PNG | LibreOffice | libreoffice --headless --convert-to png a.pptx |
| xlsx → PDF | LibreOffice | libreoffice --headless --convert-to pdf a.xlsx |
| HTML → PNG | Chromium headless / Puppeteer | chromium --headless --screenshot a.html |
| SVG → PNG | rsvg-convert / inkscape | rsvg-convert a.svg > a.png |
전형적 파이프라인 — Office → PDF → PNG:
# 1) docx → PDF
libreoffice --headless \
-env:UserInstallation=file:///tmp/lo-$$ \
--convert-to pdf --outdir /tmp/out report.docx
# 2) PDF → PNG (1 페이지만, 200 DPI, 너비 800)
pdftoppm -r 200 -f 1 -l 1 -scale-to-x 800 /tmp/out/report.pdf /tmp/thumb -png
# 3) thumb-1.png 가 산출물What — 도구 카탈로그 + 컨테이너 레시피
도구 카탈로그
| 도구 | 언어/런타임 | 강점 | 약점 |
|---|---|---|---|
Poppler (pdftotext, pdftoppm, pdfinfo) | C++ | PDF 표준 도구 | 표 추출 약함 |
| MuPDF | C | 빠르고 가벼움 | 라이선스 (GPL/상용) |
| pdfplumber | Python | 표·좌표 보존 | 큰 PDF 느림 |
| pdf.js | JS | 브라우저 단독 | 서버에서는 무거움 |
| mammoth | JS/Python | docx → 의미 보존 MD/HTML | 표 약함 |
| python-docx / openpyxl / python-pptx | Python | 정확한 셀/단락 | 변환 X |
| LibreOffice headless | Java/UNO | 만능 변환 | 무겁고 동시성 어려움 |
| Apache Tika | Java | 1,400 포맷 메타+텍스트 | 정확도 보통 |
| tesseract | C++ | OCR (100+ 언어) | 정확도는 입력 품질 의존 |
| exiftool | Perl | 이미지/비디오 메타 | 표는 X |
| ImageMagick / GraphicsMagick | C | 이미지 변환 | RCE 이력 다수 (정책 파일 필요) |
| ffmpeg | C | 비디오/오디오 변환·스냅샷 | CLI 복잡 |
컨테이너 레시피 (Dockerfile 일부)
FROM debian:12-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
libreoffice \
poppler-utils \
tesseract-ocr tesseract-ocr-kor tesseract-ocr-eng \
fonts-noto-cjk \
ghostscript \
imagemagick \
ffmpeg \
python3 python3-pip \
&& rm -rf /var/lib/apt/lists/*
# Tika
RUN curl -L -o /opt/tika.jar \
https://archive.apache.org/dist/tika/3.0.0/tika-app-3.0.0.jar
RUN pip3 install --break-system-packages \
pdfplumber python-docx openpyxl python-pptx mammoth pillow
# ImageMagick — 기본 정책에서 PDF 처리 막혀있음, 필요시 풀어주되 신중히
# /etc/ImageMagick-6/policy.xml 의 PDF/EPS rights 조정Lambda / Cloud Function 패턴
LibreOffice 는 무거움 (수백 MB). Lambda Layer 또는 Container Image 로 구성. 콜드 스타트 부담 → Provisioned Concurrency 또는 별도 변환 워커 권장.
[ Upload S3 ] → [ EventBridge ]
→ [ Lambda: 메타·텍스트 (가벼움, JS/Python) ]
→ [ Lambda Container: LibreOffice 변환 (무거움) ]
→ [ Lambda: pdftoppm 썸네일 ]
→ [ S3 산출물 ]미디어 변환 파이프라인의 fan-out/fan-in 패턴과 동일 (07-pipeline-theory/03-fan-out-fan-in 참조).
검색 파이프라인과의 결합
원본 → Tika (텍스트+메타) → ES bulk index
→ meta facet (author, date, page_count)
쿼리 → ES match → 점수 → 썸네일 URL (S3) → UIpg_trgm 트라이그램 / tsvector 도 한국어/영어 적당히 작동.
한국어는 형태소 분석기 필요 (Mecab, Nori).
What-if — 파이프라인이 깨지는 지점
1) LibreOffice 동시 실행 락
같은 user profile 디렉토리를 잠금. 두 번째 요청이 hang.
대응: 요청별 -env:UserInstallation=file:///tmp/lo-$$.
2) ImageMagick RCE (CVE-2016-3714 “ImageTragick”)
MVG, MSL 형식이 임의 명령 실행 가능.
대응: /etc/ImageMagick-6/policy.xml 에서 위험 코더 차단:
<policy domain="coder" rights="none" pattern="MVG" />
<policy domain="coder" rights="none" pattern="MSL" />
<policy domain="coder" rights="none" pattern="EPHEMERAL" />3) PDF 의 폰트 미임베딩
서버에서 PDF 렌더 시 한글이 □□□.
대응: 컨테이너에 fonts-noto-cjk 포함, LibreOffice 가 이걸 fallback 으로 사용.
4) 무한 페이지 PDF / 100K 슬라이드 pptx
DoS 우려. 첫 페이지만 렌더하더라도 LibreOffice 는 전체 로드 시도. 대응:
- 입력 페이지 수 사전 검사 (
pdfinfo결과 페이지 수 한계). - 시간/메모리 제한 (
timeout 30s, cgroup).
5) Tika 의 OOM
큰 첨부 PDF (수백 MB) 에서 Tika JVM 힙 폭발.
대응: 별도 프로세스, -Xmx2g 등 제한, 실패 시 가벼운 도구로 fallback.
6) OCR 의 환각
Tesseract 가 빈 페이지에서 ”…의 의 의 의” 같은 환각 텍스트 생성.
대응: 신뢰도(-c tessedit_create_hocr=1) 낮은 결과 필터링.
7) 변환 결과의 fidelity 손실
docx → PDF 변환 시 폰트 fallback 으로 줄바꿈 위치 변경 → 페이지 수 변경. 검증 필요: 사용자에게 “원본 보기” 옵션 제공.
Insight — 흥미로운 이야기
“왜 LibreOffice 가 사실상 표준 변환기인가”
Microsoft Office Online 은 클라우드에 묶여있고, 서버 라이선스는 비싸고, API 가 제한적. LibreOffice 는 LGPL — 회사 서버에 무한정 설치 가능, headless 모드 지원, OOXML 호환성이 점점 좋아지고 있음. 단점은 무거움 (200+ MB) 과 동시성 — 그래서 보통 별도 변환 워커로 격리.
“Apache Tika 의 시작은 Lucene”
2007년 Lucene 검색 인덱싱을 위해 시작. “검색하려면 모든 포맷을 텍스트로 바꿔야 한다” → 자바 생태계의 모든 파서를 한 자켓으로. 지금은 Solr/Elasticsearch 의 첨부 파이프라인의 표준.
“OCR 의 30년 — Tesseract 의 부활”
1985년 HP Labs 에서 시작 → 1995년 사장 → 2005년 Google 이 인수해 오픈소스화. 2018년 v4 가 LSTM 신경망 기반으로 전환 → 한국어 정확도 폭발. 지금은 무료로 100+ 언어를 처리하는 사실상 유일한 오픈소스 OCR.
“Office → PDF → PNG 패턴이 보편적인 이유”
직접 Office → PNG 가 불안정 (LibreOffice 의 PNG export 가 슬라이드 단위 안정성 떨어짐). 중간에 PDF 를 거치면 벡터 → 래스터 의 마지막 한 단계만 잘 만들면 됨 → Poppler / MuPDF 가 이걸 30년간 다듬음. “복잡한 변환 = PDF 경유” 가 사실상의 모범사례.
요약 + Mermaid
추출 파이프라인은 메타 → 텍스트 → 렌더 의 3단계. 포맷별로 1순위 가벼운 도구가 따로 있고, Apache Tika 가 만능 fallback, 시각 표현은 LibreOffice → PDF → Poppler/MuPDF → PNG 의 패턴이 보편적이다. 모든 단계는 샌드박스 + 자원 한계 + 보안 정책 위에서 돌려야 한다.