목록으로 돌아가기

LangChain & LlamaIndex에서 HWP 문서를 다루는 가장 깔끔한 파이프라인

AI & RAG2026-08-23· 읽기 시간 5분

Python으로 RAG(Retrieval-Augmented Generation) 시스템을 구축하다 보면 반드시 마주치는 골칫거리가 있습니다. 바로 한국 공공기관과 기업들이 수십 년간 생산해온 HWP 문서입니다.

LangChain의 공식 Document Loader 생태계는 PDF, DOCX, HTML 등 범용 포맷에 최적화되어 있고, HWP 바이너리 포맷을 네이티브하게 처리하는 표준 로더는 제공되지 않습니다. 그렇다고 오픈소스 hwp5txt를 사용하면 표 데이터가 통째로 <표>라는 단일 텍스트로 증발해 버려, 정작 중요한 평가 기준표나 원가 내역서의 데이터가 청크에서 사라지는 치명적인 문제가 발생합니다.

왜 HWP 직접 파싱이 RAG 파이프라인에서 위험한가

HWP 5.x 포맷은 OLE/CFB 바이너리 구조로 설계된 독점 포맷입니다. 표(Table) 데이터는 단순 텍스트 스트림이 아닌 계층형 컨트롤(Control) 객체로 저장되기 때문에, 일반 텍스트 추출 도구로는 셀 단위 데이터와 행/열 관계를 보존하는 것이 사실상 불가능합니다.

그 결과 청킹(Chunking) 단계에서 다음과 같은 문제가 발생합니다.

  • 메타데이터 유실: 헤더·캡션 정보가 사라져 청크의 출처와 맥락을 파악하기 어려워집니다.
  • 표 구조 붕괴: 셀 데이터가 1차원 텍스트 나열로 변환되어 행과 열의 관계가 파괴됩니다.
  • 시맨틱 청킹 불가: 문서 헤더 계층 구조가 소실되어 MarkdownHeaderTextSplitter 같은 구조 기반 분할기를 활용할 수 없습니다.

실무 2단계 파이프라인: HWP → Markdown → LangChain

가장 안정적인 접근법은 HWP를 구조화된 마크다운으로 1차 변환한 뒤, LangChain의 표준 마크다운 분할기에 입력하는 것입니다.

1단계: MDChange API로 HWP → 마크다운 변환

MDChange의 변환 API를 사용하면 표 셀, 헤더 계층, 병합 셀(Rowspan/Colspan)이 보존된 마크다운 텍스트를 얻을 수 있습니다.

import requests def convert_hwp_to_markdown(hwp_file_path: str, api_url: str) -> str: """MDChange API를 통해 HWP 파일을 마크다운으로 변환합니다.""" with open(hwp_file_path, 'rb') as f: response = requests.post( f"{api_url}/api/upload", files={"file": (hwp_file_path, f, "application/octet-stream")}, headers={"Authorization": "Bearer YOUR_API_TOKEN"} ) response.raise_for_status() return response.json()["markdown"]

2단계: MarkdownHeaderTextSplitter로 시맨틱 청킹

변환된 마크다운을 LangChain의 MarkdownHeaderTextSplitter에 입력하면, 문서의 제목 계층(#, ##, ###)을 기준으로 의미론적으로 올바른 청크를 자동 생성합니다.

from langchain.text_splitter import MarkdownHeaderTextSplitter from langchain_openai import OpenAIEmbeddings from langchain_community.vectorstores import FAISS # 마크다운 청킹: 헤더 계층 기반 시맨틱 분할 headers_to_split_on = [ ("#", "h1"), ("##", "h2"), ("###", "h3"), ] splitter = MarkdownHeaderTextSplitter( headers_to_split_on=headers_to_split_on, strip_whitespace=True ) # HWP → 마크다운 변환 markdown_text = convert_hwp_to_markdown("과업지시서.hwp", "https://api.getmdchange.com") # 청킹 실행 chunks = splitter.split_text(markdown_text) # 벡터 DB 삽입 embeddings = OpenAIEmbeddings() vectorstore = FAISS.from_documents(chunks, embeddings) print(f"총 {len(chunks)}개의 시맨틱 청크 생성 완료") for chunk in chunks[:3]: print(f"\n[청크] 헤더: {chunk.metadata}") print(chunk.page_content[:200])

LlamaIndex 사용 시에도 동일 전략 적용 가능

LlamaIndex를 사용하는 경우에도 마크다운 변환 후 MarkdownNodeParser를 통해 동일한 구조 기반 청킹을 적용할 수 있습니다.

from llama_index.core import Document from llama_index.core.node_parser import MarkdownNodeParser markdown_text = convert_hwp_to_markdown("보고서.hwp", "https://api.getmdchange.com") document = Document(text=markdown_text) parser = MarkdownNodeParser() nodes = parser.get_nodes_from_documents([document]) print(f"총 {len(nodes)}개 노드 생성")

정리

이 2단계 파이프라인의 핵심은 도구의 역할을 명확히 분리하는 것입니다. HWP의 복잡한 바이너리 구조 파싱은 전문 파서에게, 그리고 파싱된 구조화 텍스트의 시맨틱 분할과 벡터 검색은 LangChain/LlamaIndex의 강점에 맡기는 방식으로, 각 도구의 장점을 최대화할 수 있습니다.

기업 내 HWP 문서를 RAG 시스템에 안정적으로 통합하고 싶다면, MDChange B2B API 도입을 검토해 보세요.

완벽한 표 파싱 및 대량 문서 자동화가 필요하신가요?

MDChange의 B2B 엔터프라이즈 전용 API를 도입하여 사내 HWP/HWPX 문서의 RAG 데이터 생성을 자동화하세요.