프로덕션 RAG 파이프라인 구축: 문서 처리, 청킹(Chunking) 및 메타데이터 설계
요약
프로덕션 환경에서 성공적인 RAG 시스템을 구축하기 위한 문서 인제스션(Ingestion)의 중요성을 다룹니다. 단순한 텍스트 추출을 넘어 문서의 구조 보존, 노이즈 제거, 메타데이터 설계가 검색 품질에 미치는 결정적인 역할을 설명합니다.
핵심 포인트
- 검색 품질은 사용자의 질문 시점이 아닌 문서 인제스션 단계에서 결정됨
- 단순 텍스트 추출은 의미 파편화와 노이즈를 발생시켜 검색 성능을 저하시킴
- 훌륭한 인제스션은 구조 보존, 노이즈 제거, 메타데이터 추출을 수행해야 함
- 문서의 구조(표, 헤더 등)를 유지하는 것이 기계 학습 가능한 지식 전환의 핵심
첫 번째 기사에서 우리는 왜 많은 RAG 시스템이 프로덕션 환경에서 실패하는지 탐구했으며, 검색 품질(retrieval quality)이 답변 품질을 결정한다는 핵심 원칙을 세웠습니다. 또한 프로덕션급 RAG 시스템 뒤에 숨겨진 아키텍처를 소개하고, 왜 단순한 "임베딩 (embeddings) + 벡터 데이터베이스 (vector database) + LLM" 파이프라인만으로는 충분하지 않은지 설명했습니다.
이번 두 번째 파트에서는 파이프라인의 한 단계 앞인, 문서가 시스템에 처음 들어오는 시점으로 이동하겠습니다. 우리는 단 하나의 임베딩이 생성되기 전에 문서가 어떻게 수집(ingested), 정제(cleaned), 파싱(parsed), 청킹(chunked)되어야 하며, 메타데이터(metadata)로 어떻게 풍부해져야 하는지 살펴볼 것입니다. 이러한 결정은 모든 프로덕션 RAG 시스템의 토대를 형성하며, 종종 임베딩 모델이나 LLM의 선택보다 검색 품질에 더 큰 영향을 미칩니다.
프로덕션 RAG 아키텍처 시리즈
-
✅ 왜 대부분의 RAG 시스템이 프로덕션에서 실패하는가: AI 검색 뒤에 숨겨진 아키텍처 문제
-
프로덕션 RAG 파이프라인 구축: 문서 처리, 청킹 (Chunking) 및 메타데이터 설계 (현재 위치)
-
벡터 검색을 넘어: 하이브리드 검색(Hybrid Search)과 리랭킹(Reranking)을 통한 더 나은 RAG 검색 구축
-
RAG 시스템 확장: 프로덕션 아키텍처 및 성능 최적화
-
프로덕션 RAG 시스템 평가: 지표, 모니터링 및 일반적인 실패 패턴
제4장 — 인제스션 (Ingestion): 검색이 실제로 시작되는 곳
프로덕션에서 검색은 사용자가 질문을 던질 때 시작되지 않습니다. 검색은 문서가 시스템에 들어오는 순간 시작됩니다. 만약 인제스션(ingestion)이 취약하다면, 이후의 모든 단계가 그 피해를 물려받게 되며, 아무리 영리한 프롬프팅 (prompting)을 사용하더라도 이를 완전히 복구할 수는 없습니다.
이것이 바로 인제스션이 단순한 "PDF 파싱 작업"이 아닌 이유입니다. 그것은 무질서한 인간의 문서를 구조화되고, 검색 가능하며, 신뢰할 수 있는 지식으로 전환하는 과정입니다.
인제스션이 중요한 이유
문서는 단순히 평문(plain text)인 경우가 거의 없습니다. 계약서에는 섹션, 표, 헤더(header), 푸터(footer), 페이지 번호가 있으며, 때로는 스캔된 서명이 포함되기도 합니다. 핸드북에는 제목, 코드 블록(code block), 글머리 기호 목록, 삽입된 스크린샷이 포함될 수 있습니다. 정책 문서는 사람에게는 읽기 쉬워 보일 수 있지만, 구조가 너무 일찍 평탄화(flattened)되면 기계에게는 거의 무용지물이 될 수 있습니다.
만약 단순히 모든 텍스트를 추출하여 청커(chunker)에 쏟아붓는다면, 노이즈(noise)를 생성하게 됩니다. 그러면 모델은 완전한 아이디어 대신 의미의 파편들을 보게 됩니다. “활성 송장은 업그레이드 전에 반드시 마감되어야 한다”와 같이 명확한 규칙을 가진 문서가, 관련성은 있어 보이지만 잘못된 답을 내놓는 검색 결과로 변질되는 방식이 바로 이렇습니다.
훌륭한 인제스션(Ingestion)이 하는 일
훌륭한 인제스션 파이프라인은 네 가지를 수행합니다:
- 구조를 보존합니다.
- 노이즈를 제거합니다.
- 메타데이터(metadata)를 추출합니다.
- 검색(retrieval)을 위해 문서를 준비합니다.
당연한 소리처럼 들리겠지만, 대부분의 취약한 시스템은 이 중 하나만 수행하며, 그마저도 제대로 하지 못하는 경우가 많습니다. 그들은 텍스트를 추출하고, 아마도 청크(chunk)로 나눈 뒤, 나머지는 알아서 잘 작동할 것이라고 가정합니다. 실제로 인제스션은 시스템이 검색 가능한 지식이 되느냐, 아니면 단순한 텍스트 더미로 남느냐를 결정하는 단계입니다.
간단한 멘탈 모델 (Mental Model)
인제스션을 요리하기 전 재료를 준비하는 과정이라고 생각해보세요.
채소 전체와 포장지, 라벨을 팬에 통째로 던져 넣고 식사가 알아서 완성되기를 바라지는 않습니다. 먼저 씻고, 껍질을 벗기고, 자르고, 분류하고, 라벨을 붙입니다. 마찬가지로, 문서는 인덱싱(indexing)되기 전에 정제되고, 구조화되며, 주석(annotation)이 달린 상태여야 합니다.
파이프라인의 기본적인 형태는 다음과 같습니다:
Upload
↓
Object Storage
...
각 단계가 존재하는 이유는 원본 문서(raw documents)는 무질서하고, 검색 시스템은 까다롭기 때문입니다.
파싱(Parsing)은 추출(Extraction)이 아니다
파싱 (Parsing)은 단순히 텍스트를 추출하는 것이 아니라 형식을 이해하는 과정입니다. PDF 파서 (PDF parser)는 문서가 스캔된 것인지 디지털 방식인지, 콘텐츠가 열(column)로 배치되어 있는지, 표 (table)가 시각적으로 중요한지, 그리고 파일에 반복되는 헤더 (header)나 푸터 (footer)가 포함되어 있는지를 알아야 합니다. HTML 파서 (HTML parser)는 의미론적 태그 (semantic tags)를 보존해야 합니다. 마크다운 파서 (Markdown parser)는 제목 (heading), 목록 (list), 코드 블록 (code block)을 온전하게 유지해야 합니다.
파서가 형편없다면, 그 이후의 모든 과정은 더 악화됩니다.
클리닝 (Cleaning)은 선택 사항이 아니다
파싱을 마친 후에도 문서는 대개 여전히 불필요한 정보 (junk)를 포함하고 있습니다. 다음과 같은 것들을 볼 수 있습니다:
- 페이지 번호,
- 반복되는 회사 이름,
- "기밀 (confidential)" 배너,
- 끊어진 줄 바꿈 (line wraps),
- 중복된 단락,
- OCR 오류.
이러한 요소들을 제거하지 않으면, 이들은 임베딩 (embedding)되고 저장되며, 나중에 마치 중요한 정보인 것처럼 검색 (retrieval)됩니다. 이는 검색 품질을 망치는 가장 조용한 방법 중 하나입니다.
깨끗한 인제스션 파이프라인 (ingestion pipeline)은 의미를 삭제하지 않으면서 노이즈 (noise)를 제거합니다. 이 균형이 중요합니다. 과도한 클리닝은 콘텐츠를 손상시킬 수 있고, 클리닝이 부족하면 검색 결과가 오염됩니다.
구조 보존 (Structure preservation)
구조는 문서를 사용할 수 있게 만드는 핵심입니다. 제목 (heading)은 해당 섹션이 무엇에 관한 것인지 알려줍니다. 표 (table)는 관계를 인코딩합니다. 목록 (list)은 계층 구조를 보여줍니다. 코드 블록 (code block)은 정확한 구문 (syntax)을 보존합니다. 이러한 구조를 잃는 것은 종종 몇 단어를 잃는 것보다 더 해롭습니다.
예를 들어, 다음의 두 표현은 동일하지 않습니다:
결제 조건 (Payment Terms)
송장은 30일 이내에 지급되어야 합니다.
지급 실패 시 서비스가 중단될 수 있습니다.
그리고:
송장은 30일 이내에 지급되어야 합니다.
지급 실패 시 서비스가 중단될 수 있습니다.
두 번째 것도 기술적으로는 읽을 수 있지만, 첫 번째가 훨씬 더 유용합니다. 제목이 청크 (chunk)에 명확한 의미론적 프레임 (semantic frame)을 제공하기 때문입니다. 이 프레임은 검색을 돕고, 나중에 LLM이 답변을 설명하는 데 도움을 줍니다.
메타데이터 (Metadata)는 여기서부터 시작된다
메타데이터는 마지막에 붙이는 것이 아닙니다. 인제스션 (ingestion) 과정 중에 생성되어야 합니다.
유용한 메타데이터에는 흔히 다음과 같은 것들이 포함됩니다:
-
문서 유형 (document type),
-
제목 (title),
-
섹션 (section),
-
페이지 번호 (page number),
-
언어 (language),
-
버전 (version),
-
소스 시스템 (source system),
-
부서 (department),
-
생성 시간 (creation time),
-
최종 수정 시간 (last updated time).
이러한 메타데이터는 시스템이 다음과 같은 질문에 답할 수 있게 해줍니다:
-
“최신 정책을 보여줘.”
-
“영어 문서만 검색해줘.”
-
“재무 관련 문서를 찾아줘.”
-
“더 이상 사용되지 않는 버전은 무시해줘.”
-
“12페이지에 있는 조항을 반환해줘.”
메타데이터가 없다면, 모든 것이 모호한 의미론적 수프 (semantic soup)가 되어버립니다.
실질적인 예시
다음 텍스트를 인제스션 (ingestion) 한다고 가정해 봅시다:
제목: 가격 정책 (Pricing Policy)
섹션: 플랜 업그레이드 (Upgrading Plans)
...
취약한 파이프라인은 이를 하나의 긴 덩어리 (blob)로 저장할 수 있습니다. 더 나은 파이프라인은 다음과 같이 구조화된 레코드 (structured records)를 생성합니다:
{
"text": "고객은 Professional에서 Enterprise로 업그레이드할 수 있습니다. 중요: 업그레이드 전에 활성화된 인보이스 (invoices)가 반드시 마감되어야 합니다.",
"metadata": {
...
이제 리트리버 (retriever)는 이 청크 (chunk)를 훨씬 더 지능적으로 필터링, 랭킹 (rank), 그리고 인용 (cite)할 수 있습니다.
OCR 및 스캔된 문서
스캔된 문서는 프로덕션 환경에서 인제스션 (ingestion)이 자주 실패하는 지점입니다. 텍스트가 픽셀 (pixels)로만 존재한다면, 다른 작업을 수행하기 전에 OCR (광학 문자 인식)이 반드시 필요합니다. 그리고 OCR은 단순히 예외적인 상황을 위한 보조 기능이 아닙니다. 많은 실제 조직에서 유용한 지식의 상당 부분이 스캔된 양식, 서명된 계약서, 인보이스 (invoices), 그리고 오래된 PDF 파일 형태로 존재합니다.
OCR 품질이 낮으면 시스템이 중요한 단어를 놓치거나 표의 셀 (cells)을 서로 뒤섞을 수 있습니다. 레이아웃 (layout)을 무시하면, 모델은 하나의 길고 끊어진 문장이나 읽을 수 없는 텍스트로 평탄화된 표와 같은 무의미한 결과물을 보게 될 수 있습니다. 이것이 바로 훌륭한 인제스션 (ingestion) 과정이 OCR과 레이아웃 추출 (layout extraction)을 최우선 과제로 다루어야 하는 이유입니다.
주요 실패 패턴
가장 흔한 실패 패턴은 다음과 같습니다:
-
문서가 시스템에 진입합니다.
-
텍스트가 너무 일찍 추출됩니다.
-
구조가 손실됩니다.
-
청킹 (Chunking)이 잘못된 경계에서 분할됩니다.
-
메타데이터 (Metadata)가 누락되었거나 얕습니다.
-
검색 (Retrieval) 결과가 "관련은 있지만" 불완전한 컨텍스트를 반환합니다.
-
LLM이 잘못된 해석을 바탕으로 자신 있게 답변합니다.
그러한 실패는 종종 AI의 문제처럼 보이지만, 실제로는 데이터 수집 (Ingestion)의 문제입니다.
목표로 해야 할 것
수집 (Ingestion) 파이프라인은 다음과 같은 특성을 가진 청크 (Chunk)를 생성해야 합니다:
- 그 자체로 읽기 가능해야 함,
- 알려진 섹션 또는 소스에 연결되어 있어야 함,
- 메타데이터 (Metadata)로 풍부해져 있어야 함,
- 임베딩 (Embedding)하기에 충분히 깨끗해야 함,
- 그리고 원본 문서로 추적 가능해야 함.
만약 청크 (Chunk)가 어디에서 왔는지, 왜 존재하는지를 알려줄 수 없다면, 그것은 아마도 프로덕션 (Production) 환경에 투입될 준비가 되지 않은 것입니다.
핵심 아이디어
수집 (Ingestion)은 검색 (Retrieval)을 위한 보조 작업이 아닙니다. 그것은 검색 (Retrieval)의 토대입니다.
이것이 바로 프로덕션 (Production) RAG 시스템이 임베딩 (Embedding)을 다루기 전에 파싱 (Parsing), 정제 (Cleaning), 구조 (Structure), 그리고 메타데이터 (Metadata)에 그토록 많은 노력을 기울이는 이유입니다. 일단 이 토대가 견고해지면, 그 이후의 모든 하위 단계(Downstream)는 더 쉽고, 저렴하며, 더 신뢰할 수 있게 됩니다.
제5장 — 청킹 (Chunking): 검색 품질의 핵심
청킹 (Chunking)은 대부분의 RAG 시스템이 조용히 무너지는 지점입니다. 견고한 모델, 빠른 벡터 데이터베이스 (Vector Database), 그리고 다듬어진 프롬프트 (Prompt)를 갖추고 있더라도, 청크 (Chunk)의 형태가 잘못 잡혀 있다면 검색 (Retrieval)은 여전히 핵심을 놓치게 됩니다.
이유는 간단합니다. 검색 (Retrieval)은 "진실"을 찾는 것이 아니라, 유용해 보이는 텍스트 조각을 찾는 것이기 때문입니다. 만약 그 조각들이 불완전하거나, 노이즈가 섞여 있거나, 잘못된 위치에서 잘려 있다면, 시스템 전체가 잘못된 방향으로 자신감 있고 정밀하게 답변하기 시작합니다.
청킹 (Chunking)이 중요한 이유
모든 페이지가 무작위로 조각나서 섞여 있는 매뉴얼을 읽는 상황을 생각해 보십시오. 단어 몇 개는 알아볼 수 있겠지만, 그 결과를 신뢰할 수는 없을 것입니다. 문서가 단순히 토큰 수 (Token count)에 의해서만 분할될 때 정확히 이런 일이 발생합니다.
좋은 청크 (Chunk)는 단순히 "충분히 작은 것"이 아닙니다. 다음과 같아야 합니다:
- 의미론적으로 일관되어야 하며 (semantically coherent),
- 이해할 수 있을 만큼 충분히 독립적이어야 하고 (self-contained),
- 문서 구조와 일치해야 하며 (aligned with document structure),
- 검색되었을 때 그 자체로 유용해야 합니다.
만약 하나의 청크를 이해하기 위해 다른 세 개의 청크가 추가로 필요하다면, 검색 품질 (retrieval quality)은 급격히 떨어집니다.
잘못된 청킹 방식
가장 흔한 초보적인 접근 방식은 고정 크기 분할 (fixed-size splitting)입니다.
1000 tokens
↓
아무 곳에서나 절단
...
코드상으로는 깔끔해 보일지 모르지만, 실제 적용 시에는 엉망이 됩니다. 문장이 중간에 잘릴 수 있고, 표의 행 (table row)이 헤더 (header)와 분리될 수 있습니다. 섹션 제목은 한 청크에 들어가고 내용은 다른 청크에 나타날 수도 있습니다. 그렇게 되면 검색기 (retriever)는 아이디어 대신 파편들을 보게 됩니다.
예를 들어:
원본:
"Active invoices must be closed before upgrading from Professional to Enterprise."
...
두 청크 모두 이상적이지 않습니다. 하나는 불완전하고, 다른 하나는 의미가 없습니다. 모델이 답변을 할 수는 있겠지만, 그것은 추측에 불과할 것입니다.
고정 크기 청킹 (Fixed-size chunking)
고정 크기 청킹은 구현이 쉽고 설명하기 용이하기 때문에 수많은 튜토리얼에 등장합니다. 빠른 실험에는 괜찮지만, 최종 선택지로 사용되는 경우는 드물어야 합니다.
적합한 경우:
- 가공되지 않은 일반 텍스트 (raw plain text),
- 프로토타입 (prototypes),
- 빠른 베이스라인 (quick baselines).
부적합한 경우:
- 계약서 (contracts),
- 제목/헤딩이 있는 문서 (docs with headings),
- 표 (tables),
- 코드가 많은 문서 (code-heavy documents),
- 구조가 중요한 모든 것.
문제는 단순히 정확성만이 아닙니다. 고정 크기 분할은 검색 노이즈 (retrieval noise)를 생성하기도 합니다. 청크가 임의로 잘리기 때문에 벡터 표현 (vector representation)의 집중도가 떨어집니다. 이는 검색 엔진이 전처리 과정에서 파괴된 의미를 복구하기 위해 훨씬 더 많은 노력을 기울여야 함을 의미합니다.
슬라이딩 윈도우 청킹 (Sliding window chunking)
슬라이딩 윈도우 청킹은 첫 번째 단계의 작은 개선책입니다. 한 번 자르고 끝내는 대신, 인접한 청크 사이에 중첩 (overlap)을 유지합니다.
청크 1: [0..500]
청크 2: [400..900]
청크 3: [800..1300]
이는 경계 부분에서의 문맥 (context)을 보존하는 데 도움이 됩니다. 만약 중요한 문장이 한 청크에서 잘리더라도, 다음 청크에 여전히 나타날 수 있습니다. 그 자체만으로도 검색 (retrieval) 성능을 크게 향상시킬 수 있습니다.
하지만 중첩 (overlap)이 만능은 아닙니다. 소스 구조가 좋지 않다면, 슬라이딩 윈도우 (sliding windows) 방식은 하나의 나쁜 청크 대신 약간의 중복이 있는 두 개의 나쁜 청크를 제공할 뿐입니다. 이는 피해를 줄여줄 뿐, 설계 문제를 해결해주지는 않습니다.
구조 인식 청킹 (Structure-aware chunking)
더 나은 접근 방식은 문서 자체에 따라 청킹을 수행하는 것입니다.
소스가 Markdown이라면 헤딩 (headings)을 사용하세요. HTML이라면 시맨틱 태그 (semantic tags)를 사용하세요. 계약서라면 조항 (clauses)과 절 (sections)을 사용하세요. 핸드북이라면 장 (chapters)과 단락 (paragraphs)을 사용하세요.
이렇게 하면 다음과 같은 청크를 얻을 수 있습니다:
섹션: 플랜 업그레이드
고객은 Professional에서 Enterprise로 업그레이드할 수 있습니다.
...
이는 단순한 토큰 슬라이싱 (token slice)보다 훨씬 강력한데, 청크가 자체적인 문맥을 담고 있기 때문입니다. 검색기 (retriever)가 이를 찾아냈을 때, LLM은 이미 답변 준비가 된 단위와 유사한 형태를 보게 됩니다.
시맨틱 청킹 (Semantic chunking)
시맨틱 청킹은 한 단계 더 나아갑니다. 단순히 서식에 기반하여 나누는 대신, 의미 (meaning)에 따라 나누려고 시도합니다. 시스템은 주제의 전환 (topic shifts), 아이디어의 경계 (idea boundaries), 또는 함께 속하는 문장 그룹을 찾습니다.
이는 문서의 형식이 덜 정형화되어 있을 때 특히 유용합니다:
- 제품 문서 (product documentation),
- 내부 메모 (internal notes),
- 정책 텍스트 (policy text),
- 긴 기사 (long articles),
- 혼합 형식의 지식 베이스 (mixed-format knowledge bases).
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기