TestForge Blog
← 전체 포스트

RAG 개발 1편 - 문서 수집과 데이터 정제 파이프라인 설계

RAG 품질의 출발점은 모델이 아니라 데이터입니다. 어떤 문서를 수집해야 하는지, HTML/PDF/위키 데이터를 어떻게 정제하고 메타데이터를 붙여야 하는지, 실무용 수집 파이프라인 관점에서 설명합니다.

TestForge Team ·

이 시리즈에서 다루는 것

이 글은 RAG 개발 시리즈의 1편입니다.

  • 1편: 문서 수집과 데이터 정제
  • 2편: 청킹과 임베딩 전략
  • 3편: 검색, Hybrid Search, Reranking
  • 4편: 답변 생성, 프롬프트, 출처 설계
  • 5편: 평가, 관측성, 운영 안정화

RAG를 잘 만들고 싶다면 검색 모델보다 먼저 데이터 파이프라인부터 정리해야 합니다.

왜 데이터 파이프라인이 먼저인가

많은 팀이 RAG를 시작할 때 벡터 DB와 임베딩 모델부터 고릅니다. 하지만 실제로 품질을 가장 많이 흔드는 건 문서의 상태입니다.

대표적인 실패 패턴:

  • 오래된 문서와 최신 문서가 섞여 있음
  • 메뉴, 푸터, 광고 문구까지 같이 임베딩함
  • 제목과 본문 관계가 끊긴 채 저장됨
  • 같은 내용이 여러 버전으로 중복 저장됨
  • 문서 출처와 업데이트 시점이 없음

이 상태에서 어떤 검색 모델을 붙여도 답변은 불안정해집니다.

먼저 정해야 할 문서 범위

RAG가 대상으로 삼을 문서는 무조건 많이 넣는다고 좋은 게 아닙니다.

초기에는 아래처럼 범위를 나누는 편이 좋습니다.

1. 공식 문서

  • 제품 가이드
  • API 문서
  • 운영 매뉴얼
  • 자주 묻는 질문

2. 내부 운영 지식

  • 장애 대응 문서
  • 온콜 런북
  • 배포 체크리스트
  • 아키텍처 결정 기록

3. 비정형 자료

  • PDF
  • 회의록
  • 이메일 요약
  • 슬라이드

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는 특히 조심해야 합니다.

문제점:

  • 줄바꿈이 비정상적으로 들어감
  • 표가 깨짐
  • 머리글/바닥글이 반복 추출됨
  • 페이지 번호가 본문에 섞임

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 개발의 첫 단추는 검색이 아니라 문서 파이프라인입니다.

데이터 수집과 정제가 잘 되어 있으면 이후 청킹, 임베딩, 검색 품질 개선도 훨씬 쉬워집니다. 반대로 이 단계가 약하면 뒤의 모든 최적화가 흔들립니다.

다음 글에서는 이렇게 정제된 문서를 어떤 단위로 나눌지, 그리고 임베딩 전략은 어떻게 잡아야 하는지 다루겠습니다.