목록으로 돌아가기

작게 쪼개고 넓게 읽는다: 마크다운 계층 구조를 활용한 Parent-Document Retriever(PDR) 구현법

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

※ 본 내용은 이해를 돕기 위한 아키텍처 예시이며, 실제 구축 환경 및 데이터 특성에 따라 상이할 수 있습니다.

RAG(검색 증강 생성) 시스템을 구축할 때 모든 엔지니어가 겪는 근본적인 딜레마가 있습니다. 바로 청크(Chunk) 크기의 트레이드오프입니다.

  • 청크를 작게 만들면(예: 100~200자): 사용자 질문과의 임베딩 코사인 유사도가 높아져 원하는 문장을 정확하게 찾아내지만, LLM이 문맥을 이해하기에는 주변 정보가 부족합니다.
  • 청크를 크게 만들면(예: 1,500자 이상): LLM이 답변할 충분한 배경 문맥이 확보되지만, 청크 내에 여러 주제가 섞여 임베딩 벡터가 희석되고 검색 정확도가 크게 떨어집니다.

이 딜레마를 해결하는 대표적인 고급 검색 아키텍처가 바로 Small-to-Big Retrieval, 즉 Parent-Document Retriever(PDR) 패턴입니다.

1. 부모-자식 문서(Parent-Child) 구조 설계 원리

PDR의 핵심 아이디어는 "검색용 청크"와 "답변 생성용 청크"를 분리하는 것입니다.

  1. 자식 청크 (Child Chunk): 100~300자 내외의 작은 단위로 쪼개어 벡터 DB에 임베딩합니다. 검색 엔진은 이 자식 청크를 대상으로 고밀도 유사도 검색을 수행합니다.
  2. 부모 청크 (Parent Chunk): 자식 청크를 감싸고 있는 1,000~2,000자 규모의 상위 섹션 전체 문서입니다. 문서 저장소(DocStore)에 별도 보관됩니다.
  3. 검색 및 교체 워크플로우: 사용자 질문으로 가장 유사한 자식 청크를 찾은 뒤, LLM 프롬프트에 주입할 때는 자식 청크의 부모 ID를 참조하여 부모 문서 전체를 컨텍스트로 교체하여 전달합니다.

2. 마크다운 헤더 계층 구조가 부모 문서 범위를 명확하게 정의하는 이유

일반 텍스트 파일에서는 단락과 단락 사이의 의미론적 경계를 기계적으로 파악하기 어렵기 때문에 부모 청크의 범위를 임의의 글자 수로 잘라야 했습니다.

하지만 문서를 **구조화된 마크다운(Markdown)**으로 전처리해 두면 헤더 계층을 통해 자연스럽고 완결성 높은 부모 문서 경계를 구성할 수 있습니다.

  • 부모 단위: ## (H2) 또는 ### (H3) 단위의 대단락 전체. (제목, 하위 본문, 관련 표가 모두 포함된 완전한 문맥)
  • 자식 단위: 해당 섹션 내부의 개별 문장이나 1~2개 행 단위의 세부 단락.

이 구조 덕분에 표나 리스트가 중간에 잘리지 않고, LLM은 항상 표의 헤더와 배경 설명이 포함된 완전한 문맥을 수신할 수 있습니다.

3. LangChain 표준 ParentDocumentRetriever 연동 예제

LangChain의 표준 모듈을 활용하여 마크다운 문서에 PDR을 적용하는 워크플로우 코드 예시입니다.

from langchain_community.vectorstores import Chroma from langchain_openai import OpenAIEmbeddings from langchain_text_splitters import MarkdownHeaderTextSplitter, RecursiveCharacterTextSplitter from langchain.storage import InMemoryByteStore from langchain.retrievers import ParentDocumentRetriever from langchain_core.documents import Document # 1. 마크다운으로 1차 변환된 사내 문서 markdown_text = """ # 사내 정보보안 가이드라인 ## 1. 비밀번호 설정 및 관리 규정 임직원은 8자리 이상의 영문, 숫자, 특수문자 조합으로 비밀번호를 설정해야 합니다. 비밀번호는 분기별 1회 의무적으로 변경해야 하며 이전 3개 비밀번호는 재사용할 수 없습니다. ## 2. 외부 저장매체 통제 지침 USB, 외장하드 등 휴대용 저장장치는 보안 관리자의 승인 후 등록된 기기만 사용할 수 있습니다. 미인가 저장매체 연결 시 보안 관제 센터로 자동 경보가 발송됩니다. """ # 2. 부모 문서 분할기 (H2 기준) 및 자식 문서 분할기 (작은 문자열 기준) headers_to_split_on = [("##", "Section")] parent_splitter = MarkdownHeaderTextSplitter(headers_to_split_on=headers_to_split_on) child_splitter = RecursiveCharacterTextSplitter(chunk_size=150, chunk_overlap=20) # 3. 저장소 초기화 (벡터 DB 및 부모 문서 저장소) vectorstore = Chroma( collection_name="pdr_markdown_sample", embedding_function=OpenAIEmbeddings() ) docstore = InMemoryByteStore() # 4. ParentDocumentRetriever 구성 retriever = ParentDocumentRetriever( vectorstore=vectorstore, docstore=docstore, child_splitter=child_splitter, parent_splitter=parent_splitter, ) # 5. 마크다운 문서 적재 retriever.add_documents([Document(page_content=markdown_text)]) # 6. 정밀 검색 질의 query = "비밀번호 변경 주기는 어떻게 되나요?" retrieved_docs = retriever.invoke(query) print(f"검색된 부모 문서 개수: {len(retrieved_docs)}") print("=== LLM에 전달될 전체 부모 컨텍스트 ===") print(retrieved_docs[0].page_content)

retriever.invoke()를 호출하면 "분기별 1회"라는 세부 자식 문장으로 날카롭게 검색되지만, LLM에는 ## 1. 비밀번호 설정 및 관리 규정 전체 섹션이 온전히 전달되어 정밀한 문맥 기반 답변이 생성됩니다.

결론: 문맥 절단 없는 정밀한 RAG를 위한 구조화 전처리의 가치

Parent-Document Retriever는 검색 정확도와 답변 품질을 동시에 확보할 수 있는 검증된 패턴입니다.

이 아키텍처가 실무에서 진가를 발휘하려면, 문서가 헤더와 표의 위계를 잃지 않고 표준 마크다운 형식으로 정밀하게 구조화되어 있어야 합니다.

HWP, DOCX, PPTX 등 사내 복잡한 문서를 PDR 친화적인 마크다운으로 전환하고 싶다면 MDChange 변환 파이프라인을 활용해 보세요.

지금 바로 무료로 변환해보세요

HWP/HWPX 공공 및 기술 문서를 3초 만에 깨끗한 마크다운으로 추출합니다.

무료로 문서 변환하기