RAG 개발 1편 - 문서 수집과 데이터 정제 파이프라인 설계
RAG 품질의 출발점은 모델이 아니라 데이터입니다. 어떤 문서를 수집해야 하는지, HTML/PDF/위키 데이터를 어떻게 정제하고 메타데이터를 붙여야 하는지, 실무용 수집 파이프라인 관점에서 설명합니다.
이 시리즈에서 다루는 것
이 글은 RAG 개발 시리즈의 1편입니다.
- 1편: 문서 수집과 데이터 정제
- 2편: 청킹과 임베딩 전략
- 3편: 검색, Hybrid Search, Reranking
- 4편: 답변 생성, 프롬프트, 출처 설계
- 5편: 평가, 관측성, 운영 안정화
RAG를 잘 만들고 싶다면 검색 모델보다 먼저 데이터 파이프라인부터 정리해야 합니다.
왜 데이터 파이프라인이 먼저인가
많은 팀이 RAG를 시작할 때 벡터 DB와 임베딩 모델부터 고릅니다. 하지만 실제로 품질을 가장 많이 흔드는 건 문서의 상태입니다.
대표적인 실패 패턴:
- 오래된 문서와 최신 문서가 섞여 있음
- 메뉴, 푸터, 광고 문구까지 같이 임베딩함
- 제목과 본문 관계가 끊긴 채 저장됨
- 같은 내용이 여러 버전으로 중복 저장됨
- 문서 출처와 업데이트 시점이 없음
이 상태에서 어떤 검색 모델을 붙여도 답변은 불안정해집니다.
먼저 정해야 할 문서 범위
RAG가 대상으로 삼을 문서는 무조건 많이 넣는다고 좋은 게 아닙니다.
초기에는 아래처럼 범위를 나누는 편이 좋습니다.
1. 공식 문서
- 제품 가이드
- API 문서
- 운영 매뉴얼
- 자주 묻는 질문
2. 내부 운영 지식
- 장애 대응 문서
- 온콜 런북
- 배포 체크리스트
- 아키텍처 결정 기록
3. 비정형 자료
- 회의록
- 이메일 요약
- 슬라이드
RAG 초기 버전에서는 공식 문서부터 넣는 편이 가장 안전합니다. 비정형 자료는 노이즈가 많고 정제 비용도 큽니다.
문서 소스별 수집 전략
문서 소스마다 수집 방식이 다릅니다.
웹 문서
정적 사이트나 문서 포털은 HTML 파싱으로 수집할 수 있습니다.
중요한 것은 본문만 뽑아내는 것입니다.
from bs4 import BeautifulSoup
def extract_main_content(html: str) -> str:
soup = BeautifulSoup(html, "html.parser")
for tag in soup.select("nav, footer, header, script, style, aside"):
tag.decompose()
main = soup.select_one("main, article, .content, .docs-content")
return main.get_text("\n", strip=True) if main else soup.get_text("\n", strip=True)
그대로 임베딩하면 메뉴와 버튼 문구까지 벡터에 들어가는 경우가 많습니다.
위키/노션/컨플루언스
이런 문서는 보통 API를 통해 가져오는 편이 좋습니다.
수집 시 함께 저장할 값:
- 문서 ID
- 원본 URL
- 작성자
- 최종 수정 시각
- 스페이스/카테고리
이 메타데이터가 없으면 이후 필터링과 출처 링크 생성이 어렵습니다.
PDF는 특히 조심해야 합니다.
문제점:
- 줄바꿈이 비정상적으로 들어감
- 표가 깨짐
- 머리글/바닥글이 반복 추출됨
- 페이지 번호가 본문에 섞임
PDF는 텍스트 추출 후 바로 임베딩하지 말고, 정제 단계를 반드시 거쳐야 합니다.
정제 단계에서 꼭 하는 작업
1. 불필요한 요소 제거
다음 요소는 대부분 노이즈입니다.
- 글로벌 네비게이션
- 푸터 문구
- 저작권 문구
- 반복 경고 배너
- 페이지 번호
2. 구조 정보 보존
제목, 소제목, 리스트, 표, 코드 블록 구조는 가능한 한 유지해야 합니다.
RAG에서 중요한 건 단순 텍스트 양이 아니라, 문맥 단위가 보존되는 것입니다.
예:
### 인증 실패 대응
- access token 만료 여부 확인
- refresh token 만료 여부 확인
- 토큰 재발급 API 호출
이런 구조는 하나의 의미 단위로 유지되어야 검색 결과가 자연스럽습니다.
3. 중복 제거
문서 사이트에서는 같은 내용이 여러 URL로 노출되는 경우가 많습니다.
예:
- 버전별 복제
- 프린트 페이지
- 다국어 문서 일부 중복
- 쿼리 파라미터만 다른 URL
같은 내용이 반복 임베딩되면 검색 결과가 한쪽으로 치우칩니다.
메타데이터 설계가 생각보다 중요하다
RAG에서는 본문 못지않게 메타데이터가 중요합니다.
추천 메타데이터:
{
"doc_id": "api-auth-001",
"source_url": "https://docs.example.com/api/auth",
"title": "인증 API 가이드",
"category": "api",
"product": "console",
"language": "ko",
"updated_at": "2026-04-17T12:00:00Z",
"visibility": "internal",
"owner": "platform-team"
}
이 정보가 있어야 가능한 것:
- 카테고리별 검색
- 최신 문서 우선 노출
- 권한 기반 필터링
- 답변 출처 링크 생성
- 운영 중 문서 품질 분석
어떤 문서를 넣지 말아야 할까
아래 문서는 초기에 제외하는 편이 좋습니다.
- 너무 짧아서 문맥이 거의 없는 문서
- 오래돼서 현재 정책과 맞지 않는 문서
- 작성자가 불분명한 임시 메모
- 중복이 심한 FAQ 복제본
- 권한 제어가 필요한 민감 문서
RAG는 “많이 넣기”보다 “신뢰 가능한 자료만 넣기”가 더 중요합니다.
문서 버전 관리 전략
문서가 바뀌면 RAG도 바뀌어야 합니다.
그래서 수집 파이프라인에는 변경 감지가 있어야 합니다.
대표 전략:
updated_at기반 변경 감지- 문서 해시 비교
- 증분 인덱싱
- 삭제 문서 tombstone 처리
예시:
import hashlib
def content_hash(text: str) -> str:
return hashlib.sha256(text.encode("utf-8")).hexdigest()
이 해시를 저장해두면 문서 변경 여부를 빠르게 판단할 수 있습니다.
추천 파이프라인 구조
실무에서는 보통 아래처럼 나눕니다.
Source Fetcher
-> Raw Document Store
-> Cleaning / Normalization
-> Metadata Enrichment
-> Deduplication
-> Chunking Queue
중요한 점은 원본과 정제본을 분리해 보관하는 것입니다. 그래야 정제 로직을 바꿨을 때 다시 재처리하기 쉽습니다.
운영 중 자주 생기는 문제
문서가 바뀌었는데 재색인이 늦음
이 경우 사용자는 최신 문서를 보고 있는데 RAG는 예전 내용을 답합니다.
권한 있는 문서가 잘못 유입됨
내부 전용 문서가 전체 검색 대상이 되면 보안 사고로 이어질 수 있습니다.
HTML 구조 변경으로 본문 추출이 깨짐
문서 사이트 레이아웃이 바뀌면 수집기가 갑자기 메뉴만 긁어오는 경우도 있습니다.
체크리스트
- 어떤 문서를 대상으로 삼을지 정의했는가
- 원본과 정제본을 분리 저장하는가
- 메타데이터 스키마가 있는가
- 중복 제거 기준이 있는가
- 문서 변경 감지와 재색인 기준이 있는가
- 민감 문서 필터링이 있는가
마무리
RAG 개발의 첫 단추는 검색이 아니라 문서 파이프라인입니다.
데이터 수집과 정제가 잘 되어 있으면 이후 청킹, 임베딩, 검색 품질 개선도 훨씬 쉬워집니다. 반대로 이 단계가 약하면 뒤의 모든 최적화가 흔들립니다.
다음 글에서는 이렇게 정제된 문서를 어떤 단위로 나눌지, 그리고 임베딩 전략은 어떻게 잡아야 하는지 다루겠습니다.