RAG 시스템을 구축하고 나서 "왜 이렇게 엉뚱한 답이 나오지?" 싶었던 적 있으신가요. 저도 처음에는 모델 탓을 했습니다. 더 비싼 LLM으로 바꾸면 나아지겠거니 했는데, 실제로 성능을 결정하는 건 모델이 아니라 문서를 얼마나 잘게, 얼마나 똑똑하게 자르느냐였습니다. 청크 분할(Chunking) 하나가 검색 정확도를 완전히 뒤흔든다는 걸, 직접 손으로 수십 번 실험하고 나서야 체감했습니다.

청크 크기가 검색 정확도를 결정하는 이유
RAG 파이프라인의 흐름은 크게 '문서 수집 → 청킹 → 임베딩 → 벡터 검색 → 답변 생성' 다섯 단계로 나뉩니다. 이 중에서 가장 먼저, 그리고 가장 많이 망가지는 지점이 바로 청킹 단계입니다. 저도 초기에는 "어차피 자동으로 나눠주는 거 아닌가" 싶어서 기본값 그대로 쓰다가, 검색 결과가 완전히 빗나가는 상황을 반복적으로 겪었습니다.
청킹이 중요한 첫 번째 이유는 임베딩 모델의 한계 때문입니다. 임베딩(Embedding)이란 텍스트를 수백 차원의 숫자 벡터로 변환하는 작업인데, text-embedding-3나 BGE 같은 고성능 모델들도 한 번에 처리할 수 있는 최대 토큰 수가 정해져 있습니다. 문서 전체를 통째로 넣으면 뒤쪽 내용이 그냥 잘려나갑니다. 중요한 내용이 문서 후반부에 몰려 있다면 처음부터 유실되는 셈입니다.
두 번째는 의미적 희석(Semantic Dilution) 문제입니다. 여기서 의미적 희석이란, 긴 문서를 하나의 벡터로 압축할 때 여러 주제가 뒤섞여 벡터 자체의 의미가 흐릿해지는 현상을 말합니다. 사용자가 "제품 유지보수 주기"를 물었는데, 검색된 청크에는 제품 소개, 가격, 설치 방법까지 함께 들어있다면 코사인 유사도(Cosine Similarity) 점수가 떨어질 수밖에 없습니다. 코사인 유사도란 두 벡터가 얼마나 같은 방향을 향하는지를 0~1 사이의 수치로 나타낸 것으로, RAG에서 "이 문서가 질문과 얼마나 관련 있는가"를 판단하는 핵심 기준입니다.
너무 작아도, 너무 커도 문제다
청크를 100~300토큰 수준으로 아주 작게 자르면 검색 정밀도는 올라갑니다. 특정 수치나 키워드와 정확히 매칭되는 경우가 많아지죠. 그런데 제가 직접 써봤는데, 이게 생각보다 위험합니다. "동 제품의 유지보수 주기는 6개월이다"라는 문장 하나만 청크로 잘려 나오면, LLM은 '동 제품'이 뭔지 모른 채 답변을 만들어냅니다. 그 결과물이 바로 할루시네이션(Hallucination), 즉 모델이 사실처럼 보이는 허위 정보를 생성하는 현상입니다.
반대로 800~1,500토큰짜리 큰 청크는 문맥은 충분하지만 벡터 공간에서 검색 상위권에 오르지 못하는 검색 실패(Retrieval Failure)가 발생합니다. 설령 검색을 통과해도 'Lost in the Middle'이라는 문제가 생깁니다. 이 현상은 LLM이 긴 프롬프트에서 중간 부분의 내용을 제대로 인식하지 못하는 경향을 말하는데, Stanford NLP 연구 결과에서도 실제로 확인된 현상입니다(출처: Stanford NLP Group). 핵심 답변이 청크 중간에 묻혀 있으면 모델이 아예 못 보고 지나치는 겁니다.
주요 청킹 전략을 정리하면 이렇습니다.
- 고정 길이 분할(Fixed-size Chunking): 512토큰 단위로 자르고 50~100토큰 중첩(Overlap)을 두는 가장 기본적인 방식. 구현이 쉽지만 문장 중간이 끊기는 단점이 있습니다.
- 재귀 문자 분할(Recursive Character Text Splitting): 줄바꿈, 문단, 문장 부호 순서로 문법 구조를 유지하며 자릅니다. 자연스러운 의미 단위가 살아있어 고정 분할보다 검색 정밀도가 확연히 높습니다.
- 문서 구조 기반 분할(Document-Specific Chunking): Markdown 헤더나 HTML 태그, 표(Table) 구조를 인식해 섹션 단위로 나눕니다. 기술 문서나 법률 문서처럼 위계가 명확한 데이터셋에서 특히 효과적입니다.
- 시맨틱 청킹(Semantic Chunking): 문장 간 임베딩 유사도를 연속으로 측정해 주제가 바뀌는 지점에서 자동으로 경계를 설정합니다. 문맥 통일성이 가장 높지만 연산 비용도 가장 큽니다.
Parent-Child RAG, 직접 써보고 달라진 것들
솔직히 이건 예상 밖이었습니다. 처음 Parent-Child Document Retrieval이라는 개념을 접했을 때, 구조가 복잡해 보여서 굳이 쓸 필요가 있을까 싶었습니다. 그냥 청크 크기 중간 어딘가에서 타협하면 되지 않을까 하는 안일한 생각이었죠. 그런데 막상 적용해 보니, 검색 단계와 생성 단계의 요구사항이 근본적으로 다르다는 걸 깨달았습니다.
작동 방식은 이렇습니다. 문서를 100~200토큰짜리 작은 '자식 청크(Child Chunk)'로 쪼개서 벡터 데이터베이스에 인덱싱합니다. 사용자가 질문을 던지면 이 작은 청크들 사이에서 정밀하게 검색합니다. 그런데 LLM 프롬프트에 실제로 넘겨주는 건 그 자식 청크가 속한 '부모 청크(Parent Chunk)', 즉 1,000~2,000토큰짜리 원본 맥락 전체입니다. 검색의 정밀함과 생성의 풍부함을 동시에 잡는 구조입니다.
제 경험상 이건 좀 다릅니다. 단순히 청크 크기를 조정하는 것과 비교해서, Parent-Child 구조를 도입한 뒤 "엉뚱한 답변"이 나오는 빈도가 체감상 절반 이하로 줄었습니다. 특히 "~의 조건은?"처럼 전제 조건이 이전 문단에 있는 질문에서 차이가 극명했습니다. LangChain이나 LlamaIndex 같은 프레임워크에서는 이 구조를 위한 ParentDocumentRetriever가 이미 공식 지원되고 있습니다(출처: LangChain 공식 문서).
물론 한계도 있습니다. 부모 청크 크기를 잘못 설정하면 결국 큰 청크의 단점이 그대로 재현됩니다. 저는 실무에서 자식 청크는 150토큰, 부모 청크는 1,200토큰으로 시작해서 데이터 특성에 따라 A/B 테스트로 좁혀나가는 방식을 씁니다. 일반적으로 오버랩(Overlap)은 전체 청크 크기의 10~20% 선에서 설정하라고 알려져 있지만, 제 경험상 문서 유형에 따라 편차가 상당해서 결국 직접 실험해보는 수밖에 없더군요.
자주 묻는 질문
Q. RAG에서 청크 크기 기본값은 얼마로 설정하면 좋나요?
A. 정답은 없지만, 일반적인 출발점은 512토큰에 오버랩 50~100토큰입니다. 다만 이건 단지 실험의 시작점일 뿐이고, 문서가 기술 매뉴얼인지, 짧은 FAQ인지, 법률 계약서인지에 따라 최적값이 완전히 달라집니다. 데이터 특성을 먼저 파악하고 A/B 테스트로 검증하는 것이 유일하게 확실한 방법입니다.
Q. 시맨틱 청킹이 가장 좋은 방법 아닌가요?
A. 이론상 문맥 일관성이 가장 높은 건 맞습니다. 문장 간 임베딩 유사도를 계속 측정해서 주제가 바뀌는 지점을 찾아내는 방식이라 직관적으로도 좋아 보입니다. 하지만 문서 수가 많을수록 연산 비용이 급격히 늘어나고, 실제 검색 정확도 개선폭이 Recursive 분할 대비 생각보다 작을 수 있습니다. 비용 대비 효과를 따져보고 선택하시길 권합니다.
Q. 할루시네이션이 자꾸 발생하는데 청킹 문제인지 어떻게 알 수 있나요?
A. 검색 단계와 생성 단계를 분리해서 진단해보면 됩니다. 벡터 검색 결과만 먼저 출력해서 질문과 실제로 관련 있는 청크가 상위에 오고 있는지 확인하세요. 검색 결과 자체가 엉뚱하다면 청킹 설계 문제이고, 검색 결과는 맞는데 최종 답변이 틀렸다면 LLM 프롬프트나 모델 문제일 가능성이 높습니다.
Q. Parent-Child RAG 구현이 어렵지 않나요?
A. LangChain의 ParentDocumentRetriever를 쓰면 생각보다 진입 장벽이 낮습니다. 자식 청크 크기와 부모 청크 크기만 파라미터로 넘겨주면 나머지는 프레임워크가 처리해줍니다. 다만 인메모리 저장소 대신 Chroma나 FAISS 같은 영구 벡터 DB를 붙이는 작업이 추가로 필요하고, 부모 청크를 별도로 저장할 스토리지 설계도 함께 고려해야 합니다.
결론
RAG 시스템이 기대만큼 동작하지 않을 때, 가장 먼저 의심해야 할 것은 모델 성능이 아니라 청킹 전략입니다. 저도 처음에는 비싼 LLM으로 교체하면 해결되겠지 싶었는데, 실제로는 청크 크기를 조정하고 Parent-Child 구조를 도입한 뒤에야 의미 있는 성능 개선이 이뤄졌습니다. 검색 증강 생성(RAG)의 품질은 결국 벡터 검색 단계에서 얼마나 의미적으로 밀도 높은 청크를 건져 올리느냐에 달려 있습니다.
보유한 문서 특성을 먼저 분석하고, 고정 길이 분할로 베이스라인을 잡은 뒤, Recursive 분할과 Parent-Child 구조를 단계적으로 실험해 보시길 권합니다. 수치로 평가하고 싶다면 Ragas 같은 RAG 평가 프레임워크로 검색 정확도와 응답 신뢰도를 정량화하는 것도 좋은 방법입니다. 청킹 하나 바꿨을 뿐인데 시스템이 달라지는 경험, 직접 해보시면 아마 저처럼 꽤 놀라실 겁니다.
참고: 출처: LangChain 공식 문서 — ParentDocumentRetriever / 출처: Stanford NLP Group