RAG - 텍스트 분할(Text Splitter)

김소은·2025년 8월 14일

https://wikidocs.net/233776

📚 텍스트 분할(Text Splitter)

RAG 파이프라인의 2단계인 핵심 전처리: 크고 복잡한 문서를 LLM이 처리 가능한 청크로 쪼개 정확하고 경제적인 검색·답변을 노린다.


1) 왜 문서 분할이 중요한가

  • 정확성: 질문과 연관된 부분만 찾게 해 품질↑
  • 효율성: 토큰·비용↓, 불필요한 맥락 제거로 할루시네이션↓

2) Splitter 비교표 + 실무 추천값

기본 가이드:

  • 텍스트형 문서: 300–800 토큰 / 10–20% overlap
  • 고구조 문서(JSON/코드/Markdown/HTML): 구조 기준 분할 + 소형 재분할(200–500 토큰, 10–15%)
  • 긴 문단/서사형: 의미 기반(SemanticChunker) + 250–700 토큰, 10–15%
  • “문자 수” 기준 분할 시: 토큰≈문자/3(한-영 혼용은 가변)로 대략 환산
Splitter기준특징장점단점추천 chunk_size추천 overlap적합한 상황
CharacterTextSplitter문자(\n\n 기본)단순, 빠름구현 쉬움의미 단위 손실1200–2400자10–15%구조 적은 텍스트, 로그
RecursiveCharacterTextSplitter["\n\n","\n"," ",""] 재귀단락→문장→단어맥락 유지약간 느림300–800토큰10–20%일반 문서/기사/리포트
TokenTextSplitter토큰LLM 한계 정확 대응의미 단위 무시 가능300–800토큰10–15%모델 입력 최적화 필수일 때
from_tiktoken / HF tokenizer토크나이저모델별 토큰 정확구현 복잡300–800토큰10–15%OpenAI/HF 모델 정밀 제어
SentenceTransformersTokenTextSplitter토큰 + 의미 유지sBERT류에 최적화의존성↑200–500토큰0–10%임베딩 검색 정확도↑
spaCyTextSplitter문장유럽 언어 강점언어 모델 필요300–700토큰10–15%영어 문장 위주 문서
NLTKTextSplitter문장가볍고 빠름품질이 다소 낮을 수300–700토큰10–15%학습 자료, 문서 초벌
KoNLPy(Kkma) Splitter한국어 문장·형태소한국어 강점속도↓250–600토큰10–15%한국어 긴 문장 처리
SemanticChunker임베딩 유사도의미 유지 최고비용·속도 부담250–700토큰10–15%정확도 최우선 RAG
CodeTextSplitter언어별 구분자함수/클래스 단위언어 세팅 필요80–200토큰0–5%코드 검색·설명
MarkdownHeaderTextSplitter헤더구조 인식, 메타 유지헤더 없으면 X(헤더별) 300–700토큰로 재분할10%기술문서/README
HTMLHeaderTextSplitterh1~h4웹 문서 구조 반영HTML 구조 편차(섹션별) 300–700토큰로 재분할10%웹페이지, 뉴스
RecursiveJsonSplitterJSON 값중첩 보존, DFS리스트 기본 비분할(객체 청크화 후) 200–500토큰로 재분할5–10%API 응답/스키마

모델 컨텍스트 창이 크더라도, 청크는 너무 크게 잡지 않는 것이 검색 품질과 재랭킹 효율에 유리(Top-k가 의미 있게 작동).


3) 파라미터 튜닝 체크리스트

  1. 문서 구조 먼저 파악

    • Markdown/HTML/JSON/코드 → 전용 splitter로 1차 분할 → RecursiveCharacterTextSplitter로 2차 소형화
  2. chunk_size

    • 일반 문서: 300–800 토큰
    • 코드/JSON: 80–200 / 200–500 토큰
    • 긴 서사/학술: 500–900 토큰(질문 길다면 300–600으로 보수적)
  3. overlap

    • 기본 10–15% (문장 경계가 잘리는 데이터는 15–20%)
    • 코드/표/JSON 키-값 등 원자 요소 위주면 0–10%
  4. 쿼리-청크 매칭 실패 시

    • overlap ↑, 또는 의미 기반(SemanticChunker)로 전환
    • 헤더/제목/섹션 메타데이터를 문서 메타로 저장해 재랭킹에 활용
  5. 성능-비용 트레이드오프

    • SemanticChunker는 정확도↑ 대신 비용·레이트↑ → 중요 문서에만 적용하거나 오프라인 전처리로 캐싱

4) 실무 베스트 프랙티스(레시피)

A. 일반 PDF/리포트/블로그

  1. RecursiveCharacterTextSplitter(300–800, 10–20%)
  2. 메타데이터: 제목, 섹션, 페이지 보존
  3. 벡터화 → Top-k=4~8 → Rerank(선택)

B. 기술문서(README/가이드)

  1. MarkdownHeaderTextSplitter(strip_headers=False)
  2. 섹션별 RecursiveCharacterTextSplitter(300–700, 10%)
  3. 헤더 계층 메타 유지

C. 웹 페이지

  1. HTMLHeaderTextSplitter(h1~h4)
  2. 섹션별 RecursiveCharacterTextSplitter(300–700, 10%)
  3. DOM 구조 불규칙하면 문단 기준 재분할

D. JSON / OpenAPI / 설정파일

  1. RecursiveJsonSplitter(max_chunk_size≈2–4KB)
  2. 필요 시 convert_lists=True
  3. 결과 문자열을 RecursiveCharacterTextSplitter(200–500, 5–10%)로 2차 분할

E. 코드 저장소

  1. CodeTextSplitter(Language=언어)로 함수/클래스 단위
  2. 추가로 80–200 토큰으로 소형화, overlap 0–5%
  3. 파일 경로·심볼명 메타 저장 → 검색 가이드

F. 한국어 긴 문장(기사/리포트)

  1. KoNLPy(Kkma)로 문장 분할
  2. RecursiveCharacterTextSplitter(250–600, 10–15%)
  3. 문장 경계가 자주 잘리면 overlap을 15–20%

G. 정확도 최우선(FAQ/지식베이스)

  1. SemanticChunker(Percentile 60–75)
  2. 청크 250–700, overlap 10–15%
  3. 임베딩·재랭킹 조합(ColBERT류/Hybrid)

5) 미니 코드 스니펫

# 1) 일반 문서
from langchain_text_splitters import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(chunk_size=600, chunk_overlap=80)
docs = splitter.create_documents([text])

# 2) Markdown → 구조 유지 후 재분할
from langchain_text_splitters import MarkdownHeaderTextSplitter
md = MarkdownHeaderTextSplitter(headers_to_split_on=[("#","H1"),("##","H2")], strip_headers=False)
sections = md.split_text(markdown_text)
fine = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50).split_documents(sections)

# 3) 의미 기반
from langchain_experimental.text_splitter import SemanticChunker
from langchain_openai.embeddings import OpenAIEmbeddings
sem = SemanticChunker(OpenAIEmbeddings(), breakpoint_threshold_type="percentile", breakpoint_threshold_amount=70)
sem_chunks = sem.create_documents([text])

# 4) JSON
from langchain_text_splitters import RecursiveJsonSplitter
js = RecursiveJsonSplitter(max_chunk_size=3000)
json_docs = js.create_documents([json_obj])  # 이후 200–500 토큰으로 재분할 권장

6) 자주 나는 실수 & 디버깅 포인트

  • 청크가 너무 큼 → Top-k가 넓게 커버 못 함, 재랭킹 성능↓
  • overlap=0 → 경계에 걸친 개념 유실 → 10–15% 권장
  • 형식 미스매치(JSON/HTML/코드에 일반 splitter) → 전용 splitter로 1차 구조화
  • 한국어 문장 경계 깨짐 → KoNLPy → overlap 15–20%로 보강

7) 결론

  • 좋은 RAG는 좋은 청크 설계에서 시작.
  • 문서 형식에 맞는 전용 splitter + 보편 재귀 분할 조합이 가장 안정적.
  • chunk_size/overlap은 “검색 성능 ↔ 비용” 사이의 현장 튜닝 변수다.

✨ 추천 기본값(안전빵): 600 토큰 / 80 토큰 overlap(≈13%)
문서·질문 특성에 따라 ±200 토큰 범위에서 조정해보자.

profile
개발자

0개의 댓글