Text Splitter — 문서를 잘 자르는 청킹 전략

📌 3줄 요약
· Text Splitter는 불러온 긴 문서를 검색·모델 입력에 알맞은 작은 조각(chunk)으로 나눠 주는 단계입니다.
· 무작정 글자 수로 자르면 문맥이 끊기므로, 문단·문장 경계를 존중하며 나누고 겹침(overlap)을 조금 두는 것이 핵심입니다.
· 조각 크기와 겹침을 잘 잡아 두면 이후 임베딩 → 검색(RAG) 품질이 눈에 띄게 좋아집니다.
긴 Document → Text Splitter → 작은 chunk 여러 개 → 임베딩 → 검색·답변

지난 글에서 Document Loader로 자료를 불러왔다면, 이번엔 그 문서를 알맞은 크기로 나누는 이야기입니다. 보고서 한 편, 웹페이지 한 장은 그대로 쓰기엔 너무 깁니다. 모델이 한 번에 읽을 수 있는 양에는 한계가 있고, 검색도 문서 전체보다 필요한 부분만 골라낼 때 정확합니다. 이 나누는 작업을 맡는 것이 바로 Text Splitter(텍스트 분할기)입니다. 이번 글에서는 왜 잘라야 하는지, 어떤 기준으로 나누는지, 그리고 실제 코드까지 정리해 보겠습니다.


✂️ 왜 문서를 '잘라야' 할까

이유는 크게 두 가지입니다. 첫째, 모델과 임베딩에는 한 번에 처리할 수 있는 길이의 한계가 있습니다. 수십 페이지짜리 문서를 통째로 밀어 넣을 수는 없습니다. 둘째, RAG에서 검색은 질문과 가장 관련 있는 조각만 찾아 오는 방식으로 동작합니다. 문서가 하나의 큰 덩어리라면 "이 문서 안 어딘가에 답이 있다" 수준에서 멈추지만, 잘게 나눠 두면 답이 있는 바로 그 대목을 집어낼 수 있습니다.

핵심은 이렇습니다. 검색이 잘 되도록, 그리고 문맥이 끊기지 않도록 나눈다.

다만 무작정 잘게 쪼갠다고 좋은 것은 아닙니다. 너무 작으면 문맥이 사라지고, 너무 크면 검색이 뭉툭해집니다. 그래서 적당한 크기를 찾는 것이 이 단계의 관건입니다.


📏 chunk_size와 chunk_overlap

분할기를 다룰 때 가장 먼저 만나는 두 값입니다. 이 둘만 이해해도 절반은 온 셈입니다.

chunk_size (조각 크기)
한 조각에 담을 최대 길이입니다. 보통 글자 수나 토큰 수로 지정합니다. 크면 문맥이 풍부하지만 검색이 무뎌지고, 작으면 그 반대입니다.
chunk_overlap (겹침)
이웃한 조각끼리 겹쳐 담는 양입니다. 경계에서 문장이 잘려 문맥이 끊기는 것을 막아 줍니다.

겹침이 왜 필요한지는 예로 보면 쉽습니다. 어떤 설명이 조각의 끝과 다음 조각의 시작에 걸쳐 있다면, 겹침이 없을 때 두 조각 모두 반쪽짜리 정보만 갖게 됩니다. 조금 겹쳐 두면 양쪽 조각 모두 문맥을 온전히 담을 확률이 높아집니다.


🧩 대표적인 분할 방식

LangChain에는 여러 분할기가 있지만, 성격별로 나눠 보면 아래처럼 정리됩니다.

방식 기준 잘 맞는 자료
문자 기준지정한 구분자 하나로 자름단순한 텍스트
재귀적 문자문단→문장→단어 순으로 시도일반 문서 (권장)
토큰 기준모델 토큰 수로 맞춤입력 한계 관리
구조 기준마크다운·코드 등 구조를 인식문서·소스코드

가장 널리 쓰이는 것은 재귀적 문자 분할(RecursiveCharacterTextSplitter)입니다. 먼저 문단 단위로 나눠 보고, 그래도 조각이 너무 크면 문장, 그다음 단어 순으로 점점 더 잘게 시도합니다. 덕분에 되도록 의미 단위를 살린 채 크기를 맞출 수 있어, 특별한 이유가 없다면 이 방식이 무난한 출발점입니다.


💻 코드로 잘라 보기

사용 흐름은 로더와 비슷합니다. 분할기를 만들고 → split 함수에 문서를 넘기면 → 조각 목록이 돌아옵니다.

# 재귀적 문자 분할기 사용하기
from langchain_text_splitters import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,        # 한 조각의 최대 길이
    chunk_overlap=50,      # 이웃 조각과 겹칠 양
)

# 1) 순수 문자열을 나누기
chunks = splitter.split_text(long_text)
print(len(chunks))         # 조각이 몇 개로 나뉘었는지

# 2) 로더가 만든 Document 목록을 나누기
#    (page_content는 잘리고 metadata는 각 조각에 그대로 복사된다)
split_docs = splitter.split_documents(docs)
print(split_docs[0].page_content)
print(split_docs[0].metadata)   # 출처·페이지 정보가 유지된다

눈여겨볼 점은 split_documents가 메타데이터를 각 조각에 그대로 이어 준다는 것입니다. 덕분에 문서를 잘게 나눠도 "이 조각이 원래 어느 파일, 몇 페이지에서 왔는지"를 잃지 않고, 나중에 출처 표기를 그대로 이어 갈 수 있습니다.


⚠️ 자주 부딪히는 문제

분할은 값 몇 개만 바꾸는 단순한 작업 같지만, 아래 지점에서 자주 품질이 흔들립니다.

문맥이 끊긴 조각
겹침을 두지 않으면 경계에서 문장이 반토막 납니다. overlap을 조금 두어 앞뒤 맥락을 살리세요.
글자 수 ≠ 토큰 수
글자 기준으로 잘라도 모델은 토큰으로 셉니다. 입력 한계가 걱정되면 토큰 기준 분할기를 쓰는 편이 안전합니다.
구조 무시
표·코드·마크다운을 그냥 글자로 자르면 형태가 깨집니다. 구조를 아는 분할기를 골라야 합니다.

🔧 실무 팁

  • 재귀적 분할부터 시작 — 특별한 사정이 없으면 RecursiveCharacterTextSplitter가 무난한 기본값입니다.
  • 겹침은 조각 크기의 10~20% 정도를 흔히 시작점으로 씁니다. 자료와 검색 결과를 보며 조정하세요.
  • 정답은 자료마다 다릅니다 — 코드·표가 많은 문서와 줄글 위주 문서는 알맞은 크기가 다릅니다. 몇 번 바꿔 가며 검색 품질을 비교해 보세요.
  • 메타데이터를 지키세요 — split_documents를 쓰면 출처 정보가 조각마다 유지되어, 나중에 근거 표기가 쉬워집니다.

❓ 자주 묻는 질문 (FAQ)

Q. 조각 크기는 무조건 작을수록 좋나요?
A. 아니요. 너무 작으면 앞뒤 문맥이 사라져 조각 하나만으로는 뜻이 통하지 않게 됩니다. 반대로 너무 크면 검색이 뭉툭해집니다. 자료에 맞는 적당한 크기를 찾는 것이 핵심입니다.

Q. 겹침(overlap)은 꼭 필요한가요?
A. 필수는 아니지만 대개 도움이 됩니다. 경계에 걸친 문장이 양쪽 조각 모두에서 온전히 읽히도록 해 주기 때문입니다. 다만 겹침이 커지면 중복 저장이 늘어나므로 적당히 둡니다.

Q. 나눈 조각은 바로 검색에 쓰나요?
A. 그 사이에 한 단계가 더 있습니다. 조각을 숫자 벡터로 바꾸는 임베딩을 거친 뒤, 벡터로 검색하게 됩니다. 다음 편에서 이 임베딩 이야기로 이어 가겠습니다.


마무리

정리하면, Text Splitter는 긴 문서를 검색과 모델 입력에 알맞은 크기의 조각으로 나눠 주는 단계입니다. 핵심 감각은 두 가지, chunk_size로 크기를 잡고 chunk_overlap으로 문맥이 끊기지 않게 하는 것입니다. 대부분의 경우 재귀적 문자 분할에서 시작해 자료에 맞춰 값을 조정하면 충분합니다. 이렇게 잘 나눈 조각들은 다음 단계에서 숫자 벡터로 바뀌는데, 다음 편에서는 그 임베딩(Embedding) 이야기로 이어 가겠습니다.

댓글

이 블로그의 인기 게시물

지하철 노선도로 이해하는 LangGraph (분기·반복 설계)

LangGraph State란 무엇인가? 상태 관리 개념 쉽게 이해하기

LangGraph Node와 Edge 개념 쉽게 이해하기 (초보자 완전 정리)