본문 바로가기
RAG/Retrieval

Chunking (청킹) 이란?

by AteN 2024. 11. 1.

청킹, RAG 성능이 안 나올 때 제일 먼저 봐야 하는 것

요약

  • 청킹은 문서를 임베딩과 검색의 단위인 청크로 자르는 전처리입니다. 어디서 얼마나 크게 자르느냐가 RAG 품질을 좌우하는 숨은 레버입니다.
  • 문제는 양쪽 끝에 있습니다. 문서를 통째로 벡터 하나에 밀어넣으면 내용이 뭉개져 검색이 흐려지고, 너무 잘게 자르면 맥락이 사라져 청크 혼자서는 뜻이 안 통합니다.
  • 핵심 트레이드오프는 이겁니다. 작게 자르면 검색이 정밀해지고 맥락을 잃고, 크게 자르면 맥락은 살지만 검색이 뭉툭해집니다. 오버랩이 경계 손실을 메웁니다.
  • 전략은 사다리처럼 정교해집니다. 고정 크기, 문장과 문단, 재귀 분할, 시맨틱, 문서 구조 기반, late chunking 순입니다.
  • "정답 청크 크기"는 없습니다. chunk_size 1024, overlap 128은 보편 최적이 아니라 튜닝 출발점입니다.

왜 자르는가

RAG는 질문이 들어오면 관련 문서를 찾아 LLM에게 함께 건네는 구조입니다. 그런데 "관련 문서를 찾는다"는 게 말처럼 간단하지 않습니다.

검색은 보통 임베딩 유사도로 이뤄지는데, 임베딩 모델은 아무리 긴 글이라도 결국 고정 크기 벡터 하나로 압축합니다. 여기서 두 가지 문제가 생깁니다.

통째로 넣으면 내용이 뭉개집니다. 열 페이지짜리 문서를 벡터 하나로 만들면 온갖 주제가 평균돼버려 특정 질문과의 유사도가 흐려집니다. 게다가 대부분의 임베딩 모델은 입력 길이 상한이 있어 긴 문서는 애초에 다 넣지도 못합니다.

찾은 걸 LLM에 넣을 때도 한도가 있습니다. 문서 전체를 프롬프트에 밀어넣으면 컨텍스트 윈도우를 잡아먹고, 정작 답과 무관한 부분이 노이즈로 작용합니다.

그래서 색인 전에 문서를 적당한 조각으로 나눕니다. 각 조각이 임베딩되어 벡터 DB에 들어가고, 검색과 LLM 주입의 최소 단위가 됩니다.

청킹은 화려하지 않습니다. 하지만 같은 임베딩 모델, 같은 코퍼스인데 청킹만 바꿔서 검색 품질이 눈에 띄게 갈립니다. 실무의 숨은 핵심 레버입니다.

만지는 손잡이는 셋

손잡이 무엇
크기 (chunk size) 한 청크에 담을 분량. 토큰이나 문자 수
경계 (boundary) 어디서 자를지. 아무 데서나 vs 문장, 문단, 섹션 같은 자연스러운 경계
오버랩 (overlap) 인접한 청크끼리 얼마나 겹치게 할지

이 셋을 어떻게 조합하느냐가 곧 청킹 전략입니다.

핵심 긴장

청킹의 거의 모든 고민은 한 문장으로 압축됩니다. 작게 자를수록 검색은 날카로워지지만 맥락을 잃고, 크게 자를수록 맥락은 살지만 검색이 뭉툭해집니다.

작은 청크는 정밀하지만 맥락이 부족합니다. 청크가 하나의 좁은 아이디어만 담으면 그 벡터는 특정 질문과 선명하게 매칭됩니다. 하지만 대가가 있습니다. 답이 여러 청크에 걸쳐 있으면 한 청크만으로는 답이 안 나오고, "그것은", "이 방법은" 같은 대명사가 앞 청크를 가리키면 청크 혼자서는 무슨 말인지 모릅니다.

큰 청크는 맥락이 풍부하지만 검색이 흐려집니다. 청크가 여러 주제를 한꺼번에 담으면 임베딩이 그것들을 평균 내버려 벡터가 뭉툭해집니다. 이걸 의미 과압축이라고 부릅니다. 정보가 많을수록 하나의 벡터에 눌러 담기며 특정 질의와의 매칭이 약해집니다.

도서관 색인 카드로 비유해봅니다. 카드 한 장에 문장 하나만 적으면 원하는 걸 정확히 찾지만 앞뒤 맥락을 모릅니다. 카드 한 장에 챕터 전체를 적으면 맥락은 다 있지만 "이 카드가 내 질문과 맞나"를 판단하기 어렵습니다. 청크 크기를 정하는 건 이 카드의 크기를 정하는 일입니다.

오버랩은 경계 손실을 메우는 완충재입니다. 고정 길이로 자르면 문장이 경계에서 두 동강 날 수 있습니다. 인접 청크끼리 앞뒤 몇 토큰을 겹치게 하면 경계에 걸친 문장이 최소 한 청크 안에는 온전히 들어갑니다. 대신 같은 내용이 중복 저장되어 색인 크기와 비용이 늡니다.

같은 문서, 두 가지 청킹

짧은 마크다운 문서 하나를 두 방식으로 잘라보겠습니다.

원문

# 카페인의 효과와 부작용

카페인은 중추신경계를 자극해 각성 효과와 피로 감소를 준다.
커피 한 잔에는 약 95mg이 들어 있다.

과다 섭취 시 불면증과 심박수 증가 같은 부작용이 있다.
하루 400mg 이하가 권장된다.

방식 A. 고정 60자, 경계 무시, 오버랩 없음

청크 내용
A-1 # 카페인의 효과와 부작용 / 카페인은 중추신경계를 자극해 각성 효과와 피로 감소를 준다. 커피 한 잔에는
A-2 약 95mg이 들어 있다. / 과다 섭취 시 불면증과 심박수 증가 같은 부작용이 있다. 하루 400mg
A-3 이하가 권장된다.

방식 B. 문단 경계 + 헤더 부착

청크 내용
B-1 # 카페인의 효과와 부작용 / 카페인은 중추신경계를 자극해 각성 효과와 피로 감소를 준다. 커피 한 잔에는 약 95mg이 들어 있다.
B-2 # 카페인의 효과와 부작용 / 과다 섭취 시 불면증과 심박수 증가 같은 부작용이 있다. 하루 400mg 이하가 권장된다.

이제 "카페인 하루 권장량은?" 으로 검색한다고 해봅시다.

방식 A에서는 답인 "하루 400mg 이하가 권장된다"가 A-2와 A-3에 두 동강 나 있습니다. 하루 400mg은 A-2 끝에, 이하가 권장된다는 A-3에 홀로 남아 어느 청크도 완결된 답을 담지 못합니다. 검색이 흔들립니다.

방식 B에서는 B-2가 해당 문장을 온전히, 게다가 헤더까지 얹어 담고 있어 질의와 정확히 매칭됩니다.

같은 문서, 같은 임베딩 모델인데 청킹만 바꿔 검색 성패가 갈립니다. 방식 A에 오버랩을 줬다면 경계 문장이 한 청크에 온전히 들어가 약점을 일부 메울 수 있었다는 점도 함께 읽힙니다.

이 예시는 설명용으로 단순화한 것입니다. 실제 자르는 길이와 토큰 경계는 구현체 설정을 따릅니다.

여섯 가지 전략

단순하고 저렴한 것부터 정교하고 비싼 것 순으로 늘어놓을 수 있습니다.

1. 고정 크기와 오버랩. 일정 길이로 기계적으로 자르고 경계에 오버랩을 줍니다. 가장 단순하고 빠르고 예측 가능합니다. 문장을 한복판에서 끊을 수 있어 오버랩으로 완화합니다. "일단 뭐라도 돌려보자"의 기본값입니다.

2. 문장과 문단 기반. 마침표나 줄바꿈 같은 자연스러운 경계에서 자릅니다. 의미 단위가 덜 깨집니다. 다만 문단 길이가 제각각이라 어떤 청크는 너무 크고 어떤 건 너무 작아질 수 있습니다.

3. 재귀 분할. 산문의 기본 추천값입니다. 구분자에 우선순위를 두고 재귀적으로 나눕니다. LangChain의 RecursiveCharacterTextSplitter 기본 구분자는 ["\n\n", "\n", " ", ""] 입니다. 먼저 문단으로 자르고, 청크가 목표 크기보다 크면 문장, 그다음 단어 순으로 더 쪼갭니다.

"가능한 한 큰 자연 경계를 지키되 목표 크기는 넘기지 않는다"는 절충이라, 일반 산문에서 처음 잡는 값으로 자주 권장됩니다.

4. 시맨틱 청킹. 인접한 문장들을 임베딩해서 의미 유사도가 뚝 떨어지는 지점, 그러니까 주제가 바뀌는 곳에서 자릅니다. 길이가 아니라 의미로 경계를 잡으니 한 청크의 주제 응집도가 높아집니다. 대신 문장마다 임베딩을 계산해야 해서 색인 비용이 오릅니다.

5. 문서 구조 기반. 마크다운 헤더, HTML 태그, 코드의 함수나 클래스 같은 문서 자체의 구조를 경계로 삼습니다. 섹션이 통째로 보존되고, 헤더를 청크 앞에 붙여 맥락을 실어줄 수도 있습니다.

여기서 파생된 패턴이 sentence-window와 parent-document입니다. 작은 청크로 검색해 정밀도를 얻되, LLM에는 그 주변 문장이나 부모 청크를 건네 맥락을 채웁니다. 검색 단위와 생성 단위를 분리하는 영리한 절충입니다.

6. late chunking. 순서를 뒤집습니다. 보통은 자르고 나서 각 조각을 따로 임베딩하는데, late chunking은 롱컨텍스트 임베딩 모델로 문서 전체를 먼저 토큰 단위로 임베딩한 뒤, 청크별로 토큰 벡터를 평균냅니다.

그 결과 각 청크 임베딩이 문서 전체 맥락에 조건화되어, 청크를 따로 잘랐을 때 잃어버리던 대명사 연결과 주제 연속성이 보존됩니다. 별도 학습 없이 적용되지만 8192 토큰급 롱컨텍스트 임베딩 모델이 필요합니다.

전략 비교

전략 경계 기준 장점 약점 언제
고정 크기 + 오버랩 길이 단순, 빠름, 예측 가능 문장 절단 프로토타이핑
문장, 문단 구두점, 줄바꿈 의미 단위 보존 청크 크기 들쭉날쭉 문장 경계가 뚜렷한 산문
재귀 분할 구분자 우선순위 자연 경계와 크기 절충 순수 의미 경계는 아님 일반 산문의 기본 출발점
시맨틱 임베딩 유사도 낙차 주제 응집도 높음 문장별 임베딩 비용 주제 경계가 중요한 코퍼스
문서 구조 헤더, 태그, AST 섹션 보존, 맥락 부착 구조 없는 원문엔 무력 마크다운, 코드
late chunking 전체 임베딩 후 풀링 청크가 전체 맥락 보유 롱컨텍스트 임베딩 필요 청크가 맥락 잃어 검색이 흔들릴 때

만질 수 있는 파라미터

옵션 설명과 조정 기준
chunk_size 작게는 정밀하고 맥락이 줄고, 크게는 맥락이 늘고 노이즈가 늡니다. 임베딩 모델의 입력 상한을 넘지 않게. 출발점은 1024 토큰
chunk_overlap 경계 절단을 완화. 출발점은 chunk_size의 10~20퍼센트. 크면 중복과 색인 비용이 늡니다
separators 재귀 분할의 구분자 우선순위. 마크다운과 코드는 전용 구분자 사용
breakpoint 임계값 시맨틱 청킹에서 유사도가 얼마나 떨어지면 자를지. 낮추면 잘게, 높이면 크게
검색과 생성 단위 분리 sentence-window나 parent로 검색은 작게, LLM 주입은 크게 나눌지

파이프라인에서의 위치

청킹은 RAG 색인 파이프라인의 맨 앞, 임베딩 직전에 놓입니다.

원문 문서
   │
   ▼
청킹  (전략, 크기, 오버랩)
   │
   ▼
청크별 임베딩
   │
   ▼
벡터 DB 색인
   ▲
   │  (색인은 미리, 검색은 질의 때)
   │
질의
   │
   ▼
질의 임베딩
   │
   ▼
유사도 검색
   │
   ▼
상위 청크를 LLM에 주입

여기서 중요한 사실이 하나 있습니다. 청킹으로 만든 청크가 이후 하이브리드 검색과 재정렬의 입력이 됩니다. 청크가 나쁘면 뒷단이 아무리 좋아도 한계가 있습니다.

RAG 성능이 안 나올 때 리랭커나 임베딩 모델을 먼저 손대기 쉽습니다. 다만 앞단인 청킹을 함께 확인하지 않으면 원인을 못 찾을 수 있습니다.

실무에서는

표준 구현체는 LangChain의 RecursiveCharacterTextSplitter와 LlamaIndex의 node parser들입니다. 직접 만들 때도 이들의 구분자 우선순위를 출발점으로 삼는 게 낫습니다.

평가로 튜닝합니다. "정답 청크 크기"가 없으므로 청킹 전략은 RAG 평가로 A/B 하며 코퍼스에 맞춥니다. RAGAS의 context precision과 recall 같은 지표를 씁니다. 감이 아니라 지표로 고르는 게 핵심입니다.

마치며

핵심 세 가지로 정리합니다.

  1. 청킹은 문서를 검색과 임베딩 단위로 자르는 전처리로, RAG 품질의 숨은 레버입니다.
  2. 핵심 긴장은 작게 자르면 정밀하고 맥락을 잃고, 크게 자르면 맥락이 살고 노이즈가 는다는 것입니다. 오버랩이 경계 손실을 메웁니다.
  3. 전략은 고정에서 문장, 재귀, 시맨틱, 문서 구조, late chunking으로 정교해지며, 정답 크기가 없으니 평가로 튜닝합니다.

용어 정리

용어 한 줄 뜻
청크 (chunk) 문서를 나눈 조각. 임베딩과 검색, LLM 주입의 최소 단위
오버랩 (overlap) 인접 청크끼리 겹치는 부분. 경계에서 잘린 문장을 완화
재귀 분할 구분자 우선순위로 재귀적으로 자르는 방식
시맨틱 청킹 인접 문장 임베딩 유사도가 떨어지는 지점에서 자르는 방식
sentence-window / parent-document 작게 검색하고 LLM엔 더 넓은 청크를 주는 패턴
late chunking 전체를 먼저 임베딩한 뒤 청크별로 풀링해 전체 맥락을 담는 방식
의미 과압축 한 벡터에 너무 많은 내용을 담아 매칭이 뭉툭해지는 현상
coreference "그것", "이 방법" 등이 앞 청크를 가리켜 청크 단독으로는 뜻이 안 통하는 경우

참고자료

'RAG > Retrieval' 카테고리의 다른 글

Reranking  (0) 2025.11.12
HNSW  (0) 2025.01.06
Hybrid search  (0) 2024.09.25

댓글