ADR 0051: HWP born-digital 텍스트 추출
배경
한국 법률 사무소 문서의 사실상 표준은 HWP 다. 그런데 사무소 기억 (legacyDocuments) 학습 파이프라인은 Cloud Vision OCR 단일 경로였고, Vision 은 HWP 를 읽지 못한다. "대부분 HWP" 인 사무소의 현실 (2026-06-10 진단):
| 경로 | HWP 동작 |
|---|---|
| 사건 서류 업로드 | 허용 — 보관만 되고 학습 0 |
| 사무소 기억 스캔 업로드 | accept 자체가 image/*,.pdf — 정문 없음 |
| 사건 종결 자동 유입 | contentType 필터 없이 Vision OCR 에 투입 → 전건 실패 |
자동 유입 실패는 단순 누락이 아니라 한도 잠식이다 — failed legacyDoc
도 legacyDocuments count() 기반 한도 슬롯을 차지한다. HWP 사무소는
종결할 때마다 실패 문서만 쌓이고, 한 줄 본질 ("자기 데이터로 자기 AI
학습") 의 입력 파이프가 통째로 끊긴다.
핵심 통찰: HWP 는 스캔 이미지가 아니라 born-digital 텍스트다. OCR 이 필요 없고, 직접 추출하는 쪽이 더 정확하며 (오인식 0) Vision 호출 비용도 0 이다. 스캔본보다 오히려 고품질 학습 입력이다.
결정
1. 순수 추출 모듈 — functions/src/born-digital-extract.ts
진입점 extractBornDigitalText(buf, fileName) 하나. 바이트 시그니처
우선 판별 (확장자는 TXT 만 보조):
| 포맷 | 판별 | 추출 |
|---|---|---|
| HWP 5.x | CFB 시그니처 + FileHeader 스트림 "HWP Document File" 서명 | BodyText/Section* raw deflate 해제 → HWPTAG_PARA_TEXT 레코드의 UTF-16LE 디코딩 (제어문자 스펙 처리 — 인라인/확장 컨트롤 8-WCHAR skip, 탭/하이픈/빈칸 치환) |
| HWPX | ZIP + mimetype=application/hwp+zip 또는 Contents/content.hpf | Contents/section*.xml 의 <hp:t> 문단 단위 수집 + 엔티티 복원 |
| DOCX | ZIP + word/document.xml | <w:t> 동일 방식 |
| TXT | .txt 확장자 (알려진 바이너리 매직 차단) | UTF-8 / UTF-16 BOM / EUC-KR(CP949) 순서 시도 |
근거 스펙: 한컴 공개 "한글문서파일형식 5.0", KS X 6101 (OWPML).
의존성은 cfb 1개 (순수 JS, CFB·ZIP 양쪽 파싱 — SheetJS 계열) +
Node 내장 zlib/TextDecoder (Node 24 full-ICU 로 EUC-KR 지원).
2. 반환 계약 — null 폴백 vs fail-loud throw
- born-digital 미식별 (이미지·PDF·일반 zip·.doc 등 비 HWP OLE) →
null반환. 호출부가 기존 Vision OCR 경로로 폴백 — 현행 동작 보존. - 포맷은 식별됐으나 추출 불가 →
BornDigitalExtractErrorthrow (ADR 0029 fail-loud). 사유별 한국어 메시지가ocrError로 그대로 노출:hwp5-encrypted— 암호 설정 문서 (암호 해제 사본 재업로드 안내)hwp5-distribution— 배포용(보기 전용) 문서hwp5-malformed— 레코드/압축 손상txt-unknown-encoding— UTF-8/EUC-KR 외 + 제어문자 감지 (바이너리 위장)empty-text— 본문 0자 (이미지 전용 문서)
silent 부분 추출 (PrvText 미리보기 폴백 등) 은 채택하지 않는다 — 잘린 텍스트가 전문인 양 학습되는 쪽이 더 위험하다.
3. 보안 — 파싱만, 실행 없음
HWP 는 국내 APT 공격의 단골 벡터 (OLE/EPS 개체 삽입) 다. 본 모듈은 렌더링·매크로·OLE 개체 해석 없이 텍스트 레코드만 파싱하고, ICU 디코더가 fatal 모드에서도 통과시키는 C1 제어문자 (실측: 미매칭 EUC-KR lead byte → U+0080~U+009F) 까지 가드해 바이너리 위장 입력을 차단한다.
4. 검증 — 합성 픽스처 + 실파일 이중
- 단위 테스트 31종: 스펙대로 CFB 컨테이너·레코드·zip 을 직접 조립한 합성 픽스처 (외부 파일 의존 0 — 목 데이터 원칙)
- 실파일 smoke: 한컴 산출 HWP 5.x 3종 (pyhwp OSS 픽스처 — aligns · charshape · footnote-endnote) 본문/각주/미주 추출 확인 (2026-06-10)
구현 (3 PR)
- PR-1 (본 ADR): 추출 모듈 + 테스트 + cfb 의존성 — 행동 변화 없음
- PR-2:
processFileForLegacy분기 wiring — born-digital 이면 Vision 우회 (confidence=1.0,extractionMethod기록), 자동 유입 fileEntries 확장자 필터 (.doc 등 미지원 → skip + 알림 가시화, 한도 잠식 차단) - PR-3: 스캔 업로드 모달 accept 확장 (
.hwp,.hwpx,.docx,.txt) + 카피
비범위 (후속)
- 출력측 HWP 네이티브 생성 — ADR 0012 S2 백로그 유지 (법원 전자소송은 PDF 변환 제출이라 docx+PDF 로 운영 가능)
- 포털 의뢰인 업로드 HWP 허용 — 비신뢰 입력 경로라 별도 보안 검토 필요
- 수신 서류 분류 파이프라인 (incoming-doc-pipeline) 의 HWP 연결
- incoming-content-gate 의 hwp 바이트 검증 (현재 hwp 는 스니퍼 미지원 통과 — magic-bytes.ts 확장 후속)
.doc(구형 Word 바이너리) 추출 — 수요 확인 후
영향
- HWP 사무소의 기존 서면이 사무소 기억 학습에 진입 가능해짐 — 도입 온보딩 시나리오 ("진행 중 사건 + 기존 서류 반입") 의 선행 조건 해소
- Vision 호출 절감 (born-digital 은 추출이 OCR 보다 싸고 정확)
- 실패 legacyDoc 의 한도 슬롯 잠식 차단 (PR-2 필터)