← 목록으로
AI2026.10.12 02:09

RAG 청킹 전략 — Fixed·Semantic·Recursive 비교

구조화된 문서라면 Recursive 청킹부터 시작하세요. Fixed·Recursive·Semantic 청킹의 원리와 비용, 문서 유형과 정확도 요구에 따라 고르는 기준을 비교했습니다.

목차

  1. 개요
  2. 청킹이 RAG 품질을 결정하는 이유
  3. Fixed-Size 청킹 — 단순함의 힘과 한계
  4. Recursive 청킹 — 문서 구조를 따르는 분할
  5. Semantic 청킹 — 의미 경계로 쪼개기
  6. 전략별 비교와 선택 기준
  7. 운영 환경 적용 시 고려사항
  8. 맺음말

개요

RAG(Retrieval-Augmented Generation)는 LLM이 외부 문서를 참조해 답변을 생성하는 아키텍처입니다. 이 시스템에서 검색 품질을 결정하는 가장 핵심적인 요소 중 하나가 청킹(chunking) — 원본 문서를 어떤 기준으로, 어느 크기로 쪼개느냐입니다. 잘못된 청킹 전략은 아무리 뛰어난 임베딩 모델이나 LLM을 적용하더라도 검색 정확도를 구조적으로 낮춥니다. 특히 계약서, 기술 문서, 논문처럼 문맥 연결이 중요한 문서를 다룰 때 청킹 방식의 선택은 전체 RAG 시스템의 신뢰도를 좌우합니다.

문제 배경

초기 RAG 구현에서는 512 토큰, 또는 500자로 잘라내는 Fixed-Size 방식이 표준처럼 자리 잡았습니다. 빠르고 예측 가능하다는 장점이 분명하지만, 문장 중간을 잘라내거나 하나의 논리적 개념을 여러 청크에 분산시키는 문제가 빈번하게 발생합니다. 이로 인해 벡터 검색 시 관련 청크를 놓치거나, 부분적인 정보만 LLM에 전달되어 답변 품질이 떨어지는 상황이 반복됩니다. 청킹은 단순한 전처리가 아니라 검색 가능성 자체를 설계하는 작업이라는 인식이 확산되면서 Recursive 청킹과 Semantic 청킹이 주목받기 시작했습니다.

기존 방식의 한계

Fixed-Size의 핵심 문제는 "텍스트의 의미 구조"를 전혀 고려하지 않는다는 점입니다. "G1GC는 힙을 Region으로 나눠 일시정지 시간을 예측 가능하게 합니다. 이를 위해 각 Region은—" 처럼 문장이 청크 경계에서 잘리면, 두 청크 모두 불완전한 정보를 담게 됩니다. 어느 청크도 "G1GC Region 분할의 목적과 방식"을 완전히 설명하지 못하므로, 관련 쿼리가 들어와도 적절한 청크가 검색되지 않습니다. Recursive와 Semantic은 각각 다른 방식으로 이 한계를 극복하려 시도하며, 문서의 특성과 운영 제약에 따라 효과가 달라집니다.


청킹이 RAG 품질을 결정하는 이유

벡터 검색의 작동 원리

RAG 시스템에서 문서는 청크 단위로 임베딩 벡터로 변환되어 벡터 DB에 저장됩니다. 검색 시 쿼리도 동일한 임베딩 모델로 벡터화되고, 코사인 유사도나 내적을 이용해 가장 가까운 청크를 찾습니다. 이 과정에서 청크 하나가 "하나의 의미 단위"를 온전히 담고 있어야 쿼리 벡터와의 유사도가 높게 계산됩니다.

임베딩 모델은 입력 텍스트를 고정 차원의 벡터 공간에 매핑합니다. "Java의 GC 알고리즘"을 주제로 한 3페이지 문서를 하나의 청크로 만들면, 해당 벡터는 "Java GC"라는 넓은 의미 영역 어딘가에 위치합니다. 반면 "G1GC의 Region 분할 방식"만 담은 청크는 그 주제에 훨씬 밀집된 벡터가 생성되어, 동일 주제의 쿼리와 더 높은 유사도를 기록합니다. 청킹은 검색 정밀도를 직접적으로 결정합니다.

RAG 파이프라인 — 청킹 · 임베딩 · 유사도 검색 사용자 쿼리와 원본 문서가 각각 임베딩 벡터로 변환된 뒤, 유사도 검색을 거쳐 상위 K개 청크가 검색 결과로 반환되는 RAG 데이터 흐름을 보여줍니다. 쿼리 경로 문서 경로 임베딩 청킹 청킹 청킹 임베딩 임베딩 임베딩 유사도 검색 상위 K개 INPUT 사용자 쿼리 INPUT 원본 문서 청크 1 청크 2 청크 3 VEC 쿼리 벡터 벡터 1 VEC 벡터 2 벡터 3 RESULT 검색 결과 Top-K chunks LEGEND 핵심 경로 비선택 청크/벡터 입력 유사도 검색

청크가 의미 단위를 온전히 담을수록 쿼리 벡터와의 유사도 계산이 정확해져 검색 품질이 높아집니다.

청크 크기와 검색 정확도의 관계

청크 크기는 검색 정확도와 비선형적인 관계를 보입니다. 128~512 토큰 범위에서 많은 문서 유형이 최적 성능을 보인다는 경험치가 있지만, 이것은 평균적인 지침일 뿐입니다. 법률 문서처럼 하나의 조항이 수백 토큰에 달하는 경우, 256 토큰 청크는 조항 중간을 잘라 핵심 맥락을 파괴합니다. 반면 FAQ 문서처럼 질문-답변 쌍이 50토큰 이내인 경우, 512 토큰 청크는 여러 QA 쌍을 섞어 임베딩 벡터를 흐리게 만듭니다.

또한 청크 크기는 LLM 컨텍스트 활용 효율에도 영향을 미칩니다. RAG 시스템은 검색된 상위 K개 청크를 LLM 프롬프트에 삽입합니다. 청크가 너무 크면 K를 줄여야 하고, 그만큼 다양한 각도의 정보를 제공할 기회가 감소합니다. 반대로 청크가 너무 작으면 K를 늘려야 하는데, LLM 컨텍스트 창의 한계로 실질적인 정보량이 오히려 줄어들 수 있습니다.

청크 크기 검색 정밀도 맥락 충분성 적합한 문서 유형
64~128 토큰 높음 낮음 FAQ, 단문 Q&A
256~512 토큰 중간 중간 기술 문서, 블로그
512~1024 토큰 낮음 높음 논문, 법률 조항

의미 손실과 잡음의 트레이드오프

청킹에서 피해야 할 두 가지 문제는 의미 손실과 잡음 삽입입니다. 의미 손실은 하나의 논리적 개념이 여러 청크에 나뉘어 어느 청크에도 완전히 담기지 않을 때 발생합니다. 잡음은 관련 없는 내용이 동일 청크에 포함되어 임베딩 벡터를 오염시킬 때 생깁니다. 두 문제는 서로 상충합니다 — 청크를 크게 만들면 의미 손실은 줄지만 잡음이 늘고, 청크를 작게 만들면 잡음은 줄지만 의미 손실 위험이 커집니다.

이 트레이드오프를 해결하기 위한 접근법이 Overlap, Recursive 분할, Semantic 분할입니다. 각각 다른 방식으로 의미 경계를 찾으려 시도하며, 문서의 특성과 운영 비용 제약에 따라 효과가 달라집니다. "Semantic이 무조건 좋다"는 단순한 결론보다, 문서 유형과 비용을 함께 고려하는 실용적 접근이 장기적으로 더 안정적인 결과를 만들어냅니다.


Fixed-Size 청킹 — 단순함의 힘과 한계

작동 원리와 구현

Fixed-Size 청킹은 텍스트를 고정된 길이(토큰 수 또는 문자 수)로 순차적으로 분할합니다. 가장 직관적인 방식으로, 별도의 NLP 처리나 임베딩 API 호출 없이 즉시 동작합니다. Python의 LangChain에서는 CharacterTextSplitter가 이 역할을 담당하며, chunk_size(청크 최대 길이)와 chunk_overlap(인접 청크 간 겹치는 길이)을 입력받습니다.

Fixed-Size의 핵심 약점은 문장이나 단락의 경계를 고려하지 않는다는 점입니다. "Java의 GC는 메모리를 자동으로 관리합니다. 이를 통해 개발자는—" 처럼 문장이 중간에 잘리면, 두 청크 모두 불완전한 정보를 담게 됩니다. 이를 완화하기 위해 문자 단위 대신 토큰 단위로 자르거나, 특정 구분자(줄바꿈, 마침표)를 우선 분할 기준으로 삼는 변형이 활용됩니다.

python
from langchain.text_splitter import CharacterTextSplitter

# chunk_size=500, chunk_overlap=50으로 고정 크기 분할
splitter = CharacterTextSplitter(
    separator="\n\n",       # 단락 경계를 우선 구분자로 사용
    chunk_size=500,
    chunk_overlap=50,
    length_function=len,
)

text = """
Java의 가비지 컬렉터(GC)는 더 이상 참조되지 않는 객체를 자동으로 회수합니다.
G1GC는 힙을 고정 크기의 Region으로 나눠 GC 일시정지 시간을 예측 가능하게 만듭니다.

ZGC는 1TB 이상의 힙에서도 1ms 미만의 일시정지를 목표로 설계되었습니다.
로드 배리어를 사용해 GC 작업을 애플리케이션 스레드와 동시에 수행합니다.
"""

chunks = splitter.split_text(text)
# 결과: 2개의 청크 — 단락 경계("\n\n")에서 분리됨
# chunks[0]: "Java의 가비지 컬렉터 ... 예측 가능하게 만듭니다."
# chunks[1]: "ZGC는 1TB 이상 ... 동시에 수행합니다."

separator="\n\n"를 지정하면 단락 경계를 우선 분할 지점으로 사용하기 때문에 순수한 Fixed-Size보다 의미 손실 위험이 낮아집니다. 구분자로 나눈 후에도 목표 크기를 초과하면 문자 단위로 강제 분할됩니다.

중첩(Overlap) 기법

chunk_overlap은 Fixed-Size 청킹의 핵심 보완 장치입니다. 청크 경계에서 잘릴 수 있는 문장이나 개념을 다음 청크의 앞부분에 반복 포함시킴으로써, 맥락 단절로 인한 검색 실패를 줄입니다. chunk_size=500, overlap=50이면 청크 A의 마지막 50자가 청크 B의 앞 50자와 동일합니다.

Fixed-size 청킹 — 겹침 슬라이딩 윈도우 원본 텍스트가 고정 크기 청크 A·B·C로 분할되고, 인접 청크 사이에 50자 겹침 구간이 존재하는 슬라이딩 윈도우 방식을 보여준다. 50자 겹침 50자 겹침 INPUT 원본 텍스트 CHUNK 청크 A 0 – 500자 CHUNK 청크 B 450 – 950자 CHUNK 청크 C 900 – 1400자 겹침 겹침 LEGEND 원본 입력 청크 (fixed-size) 겹침 구간 (50자) 분할

중첩은 경계 부근의 맥락 손실을 완화하지만 저장 공간과 임베딩 비용이 증가합니다. overlap=50이면 전체 텍스트의 약 10%가 중복 저장되므로 10~15% 수준이 일반적인 권장 범위입니다.

언제 Fixed-Size를 선택하나

Fixed-Size는 다음 조건에서 여전히 유효한 선택입니다. 첫째, 문서가 구조적으로 균질할 때입니다. 제품 설명 카탈로그처럼 각 항목이 비슷한 길이와 형식을 가지면, 고정 크기 분할이 의미 경계와 자연스럽게 맞아떨어질 가능성이 높습니다. 둘째, 인덱싱 속도가 중요할 때입니다. Semantic 청킹은 문장마다 임베딩 API를 호출해야 해서 수십 배 느립니다. 대용량 문서를 실시간으로 인덱싱해야 한다면 Fixed-Size가 현실적인 선택입니다. 셋째, 프로토타입 단계에서 빠르게 검증해야 할 때입니다. 청킹 전략은 나중에 바꿀 수 있으므로, 초기에는 단순한 Fixed-Size로 시스템을 구축하고 성능 병목이 드러난 뒤 전략을 고도화하는 접근이 리스크를 줄입니다.


Recursive 청킹 — 문서 구조를 따르는 분할

재귀적 분할의 메커니즘

Recursive 청킹은 구분자(separator) 목록을 우선순위 순서로 정의하고, 청크가 목표 크기를 초과할 때만 다음 구분자로 내려가며 재귀적으로 분할합니다. LangChain의 RecursiveCharacterTextSplitter는 기본적으로 ["\n\n", "\n", " ", ""] 순서로 구분자를 시도합니다. 먼저 이중 줄바꿈(단락 경계)으로 분할을 시도하고, 여전히 청크가 크면 단일 줄바꿈(문장 경계)으로, 그래도 크면 공백으로, 최후에는 문자 단위로 분할합니다.

이 방식의 장점은 가능한 한 큰 의미 단위를 보존하면서 목표 크기를 맞춘다는 점입니다. Fixed-Size와 달리 구분자를 문서 구조에 맞게 커스터마이즈할 수 있습니다. Markdown 문서라면 ["## ", "### ", "\n\n", "\n"]을, Python 코드라면 ["\nclass ", "\ndef ", "\n\n", "\n"]을 구분자로 사용할 수 있습니다.

Recursive Chunking 분할 흐름 RAG Recursive Chunking 전략에서 청크 크기 초과 여부를 시작으로 단락·문장·단어·문자 경계 분할을 순차적으로 시도하는 의사결정 흐름을 보여주는 순서도. 아니오 예 예 아니오 예 아니오 예 아니오 청크 크기 초과하나? 현재 청크 유지 단락 구분자 (\n\n) 있나? 단락 경계 분할 줄바꿈 문자 (\n) 있나? 문장 경계 분할 공백 문자 있나? 단어 경계 분할 문자 단위 분할 LEGEND 의사결정 우선 분할 차선 분할 최후 수단

구분자 우선순위에 따라 의미 단위를 최대한 보존하며 분할하고, 문자 단위 분할은 최후 수단으로만 사용됩니다.

LangChain의 RecursiveCharacterTextSplitter

RecursiveCharacterTextSplitter는 현재 가장 널리 사용되는 청킹 구현 중 하나입니다. 단순 Fixed-Size 대비 코드 두세 줄만 변경하면 적용할 수 있어 진입 장벽이 낮고, 대부분의 텍스트 문서에서 Fixed-Size보다 나은 결과를 보입니다. from_language() 클래스 메서드를 사용하면 프로그래밍 언어에 최적화된 구분자를 자동으로 선택합니다.

python
from langchain.text_splitter import RecursiveCharacterTextSplitter, Language

# 일반 텍스트 문서용
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=512,
    chunk_overlap=64,
    separators=["\n\n", "\n", ".", " ", ""],  # 우선순위 순서
)

# Python 코드 전용 — 클래스/함수 경계를 구분자로 자동 사용
py_splitter = RecursiveCharacterTextSplitter.from_language(
    language=Language.PYTHON,
    chunk_size=512,
    chunk_overlap=64,
)

doc = """
## G1GC 작동 방식

G1GC는 힙 전체를 동일한 크기의 Region으로 나눕니다.
각 Region은 Eden, Survivor, Old, Humongous 역할 중 하나를 맡습니다.

GC 사이클마다 일시정지 목표 시간 안에 수거할 Region을 선택합니다.
수거 우선순위는 가비지 비율이 높은 순서입니다.
"""

chunks = text_splitter.split_text(doc)
# 결과: H2 헤더+첫 단락 / 두 번째 단락으로 자연스럽게 분리됨
# 각 청크가 논리적 단위를 유지함

from_language(Language.PYTHON) 호출 시 \nclass , \ndef , \n\n, \n이 자동 구분자로 사용되어 함수나 클래스 단위로 코드를 청킹합니다. 코드 베이스에 RAG를 적용하거나 코드 검색 시스템을 구축할 때 특히 효과적입니다.

구조적 문서에서의 효과

Markdown, HTML, 소스 코드처럼 명시적인 구조를 가진 문서에서 Recursive 청킹은 두드러진 효과를 보입니다. H2/H3 헤더를 구분자로 지정하면 섹션 단위 청킹이 가능하고, 각 청크가 하나의 소주제를 온전히 담게 됩니다. HTML 문서에서는 </p>, </section> 태그를 구분자로 활용할 수 있습니다.

반면 구조가 없는 문서 — 소설, 뉴스 기사, 강연 녹취록 등 — 에서는 Recursive가 Fixed-Size 대비 의미 있는 이점을 보이지 않습니다. 이런 문서에서는 의미 경계가 줄바꿈이나 공백이 아닌 내용 자체에 있기 때문에, 임베딩 기반의 Semantic 청킹이 더 적합합니다.

문서 유형 Recursive 효과 이유
Markdown / 기술 문서 매우 높음 헤더 구분자로 섹션 단위 분리 가능
Python / Java 소스코드 높음 클래스·함수 경계 직접 활용
HTML 웹페이지 중간 태그 기반 구조 부분 활용
뉴스 기사 / 에세이 낮음 의미 경계가 구조에 없음
강연 녹취록 낮음 줄바꿈이 의미 단위와 무관

Semantic 청킹 — 의미 경계로 쪼개기

문장 임베딩 기반 경계 탐지

Semantic 청킹은 텍스트를 먼저 문장 단위로 분리한 뒤, 인접한 문장들의 임베딩 유사도를 계산해 의미가 크게 달라지는 지점을 청크 경계로 삼습니다. "문장 A와 문장 B가 의미적으로 유사하면 같은 청크, 그렇지 않으면 다른 청크"라는 직관을 알고리즘으로 구현한 것입니다.

가장 기본적인 구현은 슬라이딩 윈도우 유사도 비교입니다. 문장 i와 i+1의 임베딩 코사인 유사도를 계산하고, 유사도가 임계값(threshold) 이하로 떨어지면 그 지점을 분할 경계로 표시합니다. 이후 인접 문장들을 목표 청크 크기에 맞게 합칩니다. 더 정교한 구현에서는 단순 인접 비교 대신 문장 그룹 간 유사도를 계산하거나, 유사도 변화율이 급격히 커지는 지점을 경계로 찾기도 합니다.

Semantic 청킹 처리 흐름 문서 입력에서 시작해 문장 임베딩·유사도 계산·임계값 판단을 거쳐 의미 단위 청크를 생성하는 Semantic 청킹 알고리즘의 단계별 흐름도. INPUT 문서 입력 STEP 문장 단위 분리 EMBEDDING 각 문장 임베딩 생성 SIMILARITY 인접 문장 간 유사도 계산 유사도 < 임계값? 예 아니오 SPLIT 분할 경계 표시 KEEP 같은 청크 유지 MERGE 크기 조정 및 청크 병합 의미 단위 청크 LEGEND 핵심 단계 처리 단계 분기 결과 상태 유지 판단

의미적으로 다른 주제로 전환되는 지점에서 자동으로 경계를 만드는 것이 Semantic 청킹의 핵심 메커니즘입니다.

구현과 주요 파라미터

LangChain의 SemanticChunker는 breakpoint_threshold_type으로 경계 결정 방식을 선택합니다. "percentile"은 유사도 분포의 하위 n번째 백분위를 경계로 삼고, "standard_deviation"은 평균에서 n 표준편차 이하를 경계로, "interquartile"은 IQR 기반으로 이상치를 경계로 처리합니다.

python
from langchain_experimental.text_splitter import SemanticChunker
from langchain_openai import OpenAIEmbeddings

embeddings = OpenAIEmbeddings(model="text-embedding-3-small")

# percentile 방식: 유사도 하위 80번째 백분위 지점에서 분할
semantic_splitter = SemanticChunker(
    embeddings=embeddings,
    breakpoint_threshold_type="percentile",
    breakpoint_threshold_amount=80,
)

text = """
탄소 중립은 온실가스 배출량과 흡수량을 균형 맞추는 것입니다.
이를 위해 재생에너지 전환과 에너지 효율화가 필수입니다.
태양광과 풍력 발전은 화석연료를 대체할 핵심 기술입니다.

한편, 자바 가비지 컬렉션은 메모리 관리 자동화 기술입니다.
G1GC와 ZGC는 대용량 힙에서 일시정지를 최소화하도록 설계됐습니다.
"""

chunks = semantic_splitter.split_text(text)
# 결과: 2개의 청크 — 탄소중립 주제 / GC 주제로 분리
# Fixed-Size로는 이 경계를 찾기 어려움

breakpoint_threshold_amount=80은 유사도 하위 80번째 백분위 이하 지점에서 분할한다는 의미입니다. 이 값을 높이면 경계를 더 쉽게 만들어 청크 수가 늘고, 낮추면 경계를 더 엄격하게 설정해 청크가 줄어듭니다. 문서 유형마다 최적값이 달라지므로, 실제 쿼리와 답변 품질을 측정하며 튜닝하는 과정이 필수입니다.

성능과 비용의 트레이드오프

Semantic 청킹의 가장 큰 단점은 비용과 속도입니다. 문서 내 모든 문장을 임베딩 API로 처리해야 하므로, Fixed-Size 대비 API 호출 비용이 수십 배 증가합니다. 10만 토큰 문서를 청킹할 때 Fixed-Size는 임베딩 없이 즉시 완료되지만, Semantic 청킹은 수백 번의 임베딩 API 호출이 필요합니다. 로컬 임베딩 모델(sentence-transformers 계열)을 사용하면 API 비용은 없지만 처리 시간이 수 분으로 늘어날 수 있습니다.

또한 Semantic 청킹의 품질은 문장 분리 품질에 크게 의존합니다. 한국어처럼 문장 끝이 명확하지 않거나, 기술 문서처럼 약어나 기호가 많이 포함된 텍스트에서는 문장 분리 자체가 부정확해져 이후 유사도 계산도 어긋납니다. 임계값 튜닝도 문서 유형마다 다르게 해야 하므로 운영 복잡도가 높아집니다.

Semantic 청킹은 "최선의 청크 경계를 찾을 수 있다"는 잠재력만큼, "잘못된 임계값으로 청크를 과도하게 쪼개거나 합칠 수 있다"는 위험을 함께 인식해야 합니다.


전략별 비교와 선택 기준

성능 특성 비교

세 전략을 다섯 가지 차원에서 비교하면 각각의 강점과 약점이 명확해집니다. Fixed-Size는 속도와 예측 가능성에서 압도적이며, Recursive는 구조화된 문서에서 Fixed-Size 대비 의미 보존성이 높으면서도 비용 증가가 없습니다. Semantic은 의미 경계 정확도에서 가장 높은 잠재력을 가지지만, 비용과 운영 복잡도가 함께 올라갑니다.

RAG 청킹 전략 선택 흐름 문서 구조 여부와 의미 경계 중요도를 기준으로 Fixed-Size, Semantic, Recursive 중 적합한 RAG 청킹 전략을 선택하는 결정 흐름도. 예 아니오 예 아니오 예 아니오 문서 구조가 명확한가? Markdown·코드 인가? 의미 경계가 핵심인가? RECURSIVE Recursive 청킹 FIXED Fixed-Size 청킹 SEMANTIC Semantic 청킹 FIXED Fixed-Size 청킹 LEGEND 결정 권장 전략 기본 전략

문서 구조 여부와 의미 경계 중요도에 따라 전략을 선택하는 판단 흐름입니다.

기준 Fixed-Size Recursive Semantic
인덱싱 속도 매우 빠름 빠름 느림
API 비용 없음 없음 높음
의미 보존성 낮음 중간~높음 높음
구현 복잡도 낮음 낮음 중간~높음
파라미터 튜닝 적음 중간 많음
구조화 문서 적합성 낮음 높음 중간

대안 기술과 비교

세 전략 외에도 Parent-Child 청킹과 Proposition 청킹 같은 변형이 주목받고 있습니다. Parent-Child 청킹은 큰 청크(Parent)와 작은 청크(Child)를 동시에 생성해, 벡터 검색은 Child로 수행하고 LLM에는 Parent 전체를 전달하는 방식입니다. 검색 정밀도와 맥락 충분성을 동시에 확보할 수 있어 프로덕션 RAG 시스템에서 점점 더 많이 채택되고 있습니다.

Parent-Child 청킹 전략 — RAG 파이프라인 원본 문서가 큰 Parent 청크로 분할되고, 다시 작은 Child 청크 세 개로 나뉜다. Child 청크들은 벡터 DB에 임베딩되어 검색에 활용되며, 검색 히트 시 원래의 Parent 청크가 LLM 프롬프트로 전달된다. 벡터 검색 검색 히트 LLM 전달 DOC 원본 문서 PARENT 큰 청크 (Parent) CHILD 작은 청크 1 (Child) CHILD 작은 청크 2 (Child) CHILD 작은 청크 3 (Child) VECTOR DB 벡터 DB PROMPT LLM 프롬프트 Parent 청크 Child 청크 Vector DB (Store) 벡터 검색 검색 히트 / LLM 전달

Parent-Child 청킹은 검색 정밀도(Child)와 맥락의 풍부함(Parent)을 분리해서 최적화하는 접근입니다.

Proposition 청킹은 문서에서 독립적인 사실 명제를 추출해 각각을 하나의 청크로 만드는 방식으로, LLM 기반 전처리가 필요해 비용이 매우 높습니다. 그러나 각 청크가 완전한 의미 단위를 담으므로 검색 정확도는 가장 뛰어납니다. 검색 오답 비용이 높은 도메인 — 의료, 법률, 금융 규정 — 에서 비용을 감수하고 적용하는 경우가 늘고 있습니다.

어떤 상황에서 선택할 것인가

전략 선택은 문서 유형, 인덱싱 빈도, 정확도 요구사항의 세 축으로 판단하는 것이 실용적입니다. 문서가 Markdown이나 코드처럼 구조화되어 있다면 Recursive를 기본으로 선택합니다. 추가 비용 없이 Fixed-Size보다 높은 품질을 얻을 수 있으며, 대부분의 기업 내부 기술 문서는 여기에 해당합니다. 구조화되지 않은 문서에서 검색 정확도가 핵심 요구사항이라면 Semantic을 고려하되, 임계값 튜닝에 들어갈 시간과 임베딩 비용을 미리 산정해야 합니다. 실시간 인덱싱이나 빠른 프로토타이핑이 필요한 상황이라면 Fixed-Size가 여전히 합리적인 시작점이고, 충분한 실제 쿼리 데이터가 쌓인 뒤 다른 전략으로 마이그레이션하는 방식이 위험을 줄입니다.


운영 환경 적용 시 고려사항

흔한 실수와 함정

RAG 청킹에서 가장 자주 발생하는 실수는 청크 크기를 검증 없이 고정하는 것입니다. 256 토큰이나 512 토큰 같은 "전통적인" 값을 아무 검증 없이 적용하고 성능 문제를 다른 곳(LLM 모델, 프롬프트)에서 찾는 경우가 많습니다. 청킹 전략은 반드시 실제 문서와 실제 쿼리로 검증해야 합니다. RAGAS나 LlamaIndex의 ResponseEvaluator 같은 평가 프레임워크를 사용하면 Faithfulness, Relevancy, Context Precision 지표로 청킹 전략을 정량 비교할 수 있습니다.

두 번째 함정은 메타데이터 손실입니다. 청크를 만들 때 원본 문서의 출처(파일명, URL, 페이지 번호)와 섹션 정보를 함께 저장하지 않으면, 검색된 청크가 어디서 왔는지 추적할 수 없어 인용 기능을 구현할 수 없습니다. LangChain의 Document 객체는 metadata 딕셔너리를 제공하므로 이를 적극 활용해야 합니다.

RAG 청킹 파이프라인 원본 문서가 청킹을 거쳐 여러 청크로 분리되고, 임베딩 후 벡터 DB에 저장되어 메타데이터 포함 검색 결과로 인용 답변을 생성하는 흐름을 보여주는 순서도. 검색 결과 INPUT 원본 문서 + 메타데이터 STEP 청킹 CHUNK 청크 1 + 출처 정보 CHUNK 청크 2 + 출처 정보 MODEL 임베딩 STORE 벡터 DB OUT 메타데이터 포함 반환 검색 결과 INPUT 원본 문서 + 메타데이터 STEP 청킹 CHUNK 청크 1 + 출처 정보 CHUNK 청크 2 + 출처 정보 MODEL 임베딩 STORE 벡터 DB OUT 메타데이터 포함 반환 GEN 인용 포함 답변 생성 LEGEND 입력 주요 단계 저장소 / 처리 출력

메타데이터를 청크와 함께 저장하면 출처 추적과 인용이 가능해져 RAG 시스템의 신뢰성이 높아집니다.

세 번째 실수는 청크 경계가 코드 블록이나 표를 쪼개는 것입니다. 기술 문서에는 마크다운 코드 블록(```)이나 표가 자주 등장하는데, 이를 고려하지 않은 청킹은 코드의 절반만 담긴 청크를 만들어냅니다. 구분자 목록에 코드 블록 경계를 추가하거나, 전처리 단계에서 코드 블록을 별도로 처리하는 로직이 필요합니다. HTML 파싱 후 청킹하거나, 청크 전에 특정 패턴을 마커로 치환하는 방법이 현업 프로젝트에서 자주 활용됩니다.

모니터링과 평가 지표

청킹 전략을 프로덕션에 적용한 뒤에는 지속적인 품질 모니터링이 필요합니다. Context Precision은 검색된 청크 중 실제로 답변에 도움이 된 청크의 비율로, 이 수치가 낮으면 청크에 잡음이 많거나 검색이 관련 없는 청크를 반환하고 있다는 신호입니다. Context Recall은 정답 생성에 필요한 정보가 검색된 청크에 모두 포함되어 있는지를 측정합니다. 이 수치가 낮으면 청크 경계에서 핵심 정보가 잘려나갔을 가능성이 높습니다.

지표 낮을 때 의심 문제 조치 방향
Context Precision 청크 내 잡음 과다 청크 크기 축소 또는 Semantic 청킹 도입
Context Recall 핵심 정보 분산 청크 크기 확대 또는 Overlap 증가
Answer Relevancy 검색 자체 부정확 쿼리 확장 또는 Re-ranking 도입
Faithfulness LLM 할루시네이션 청크 맥락 충분성 검토

확장과 마이그레이션

청킹 전략을 변경하면 전체 벡터 DB를 재인덱싱해야 합니다. 프로덕션 시스템에서는 이것이 큰 작업이 될 수 있으므로, 마이그레이션 전략을 미리 설계해야 합니다. 블루-그린 재인덱싱 방식이 권장됩니다 — 기존 벡터 DB를 유지한 채 새 청킹 전략으로 새 벡터 DB를 구축하고, 트래픽을 점진적으로 전환하면서 A/B 테스트로 품질을 비교합니다. 기존 DB는 롤백 용도로 일정 기간 유지합니다.

대용량 문서 컬렉션(수백만 건)에서 Semantic 청킹의 임베딩 비용은 수천 달러에 달할 수 있습니다. 이런 경우 전체 컬렉션에 Semantic을 적용하기보다, 쿼리 빈도가 높거나 정확도가 중요한 하위 집합에만 Semantic을 적용하고 나머지는 Recursive로 처리하는 계층적 전략이 비용 대비 효과적입니다. 문서 유형별로 청킹 전략을 다르게 적용할 수 있도록 파이프라인을 설계하는 것이 장기적으로 유연한 운영을 가능하게 합니다.


맺음말

핵심 요약

RAG 시스템에서 청킹은 검색 가능성을 설계하는 핵심 단계입니다. Fixed-Size 청킹은 빠르고 예측 가능하지만 문서의 의미 경계를 고려하지 않아 관련 정보가 여러 청크에 분산되는 문제가 발생합니다. Recursive 청킹은 문서의 구조적 계층(단락, 문장, 단어)을 우선순위로 활용해 추가 비용 없이 의미 보존성을 높이며, 대부분의 구조화된 문서에 기본 선택으로 적합합니다. Semantic 청킹은 임베딩 유사도로 의미 경계를 탐지해 잠재적으로 가장 정확한 청크를 만들지만, 전처리 비용과 파라미터 튜닝 부담이 함께 따릅니다. Parent-Child나 Proposition 방식은 정확도를 더 높일 수 있지만 운영 복잡도와 비용이 한 단계 더 올라갑니다.

적용 판단 기준

청킹 전략 선택은 세 가지 질문으로 시작하는 것이 실용적입니다. "문서가 구조화되어 있는가?" — Markdown, HTML, 소스 코드라면 Recursive를 먼저 시도합니다. 추가 비용 없이 Fixed-Size보다 높은 품질을 얻을 수 있습니다. "검색 정확도가 비용보다 중요한가?" — 법률 검토, 의료 정보 조회처럼 오답 비용이 높은 도메인이라면 Semantic 청킹이나 Parent-Child 방식의 추가 비용을 정당화할 수 있습니다. "지금 프로토타입 단계인가?" — 초기에는 Recursive로 빠르게 검증하고, 실제 쿼리 데이터가 충분히 쌓인 뒤 RAGAS 같은 평가 프레임워크로 전략별 품질을 정량 비교해 마이그레이션 여부를 결정하는 방식이 리스크를 줄입니다. 청킹 전략은 LLM 모델 선택보다 RAG 품질에 더 직접적인 영향을 미치며, 한 번 적용하면 재인덱싱 비용 때문에 바꾸기 어렵습니다. 초기 설계 단계에서 충분히 검토하는 것이 장기적으로 유리합니다.