잘게 썰수록 놓치는 문맥, 크게 썰수록 잡히는 노이즈
RAG 품질은 모델이나 임베딩보다 청크 분할에서 갈리는 경우가 많다. 문장 하나를 통째로 잘라내도 답이 될 단락까지 쪼개는 실수, 고정 토큰 수 분할이 의미 단위를 무너뜨리는 문제, 임베딩 모델의 문맥 창을 무시한 크기 설정까지. 이 글은 흔한 분할 실수들을 문맥 유실과 노이즈 유입이라는 두 축으로 비교하고, 평가 시 실제 쿼리 적중 여부와 판단 척도를 따져 청크 설계를 바로잡는 방법을 정리한다.
DevInsight 편집팀 발행
AI 보조 초안과 편집 검수를 거쳐 발행했습니다.
RAG 파이프라인을 처음 만들 때 대부분 임베딩 모델 선택에 시간을 쓴다. 그런데 실제 검색 품질을 갈라놓는 변수는 청크 분할인 경우가 훨씬 많다. 모델을 바꿔도 재현율이 오르지 않으면 분할 방식을 의심해야 한다.
청크 분할 실수는 크게 두 축으로 나뉜다. 문맥 유실과 노이즈 유입. 이 둘은 반대 방향에서 검색을 망가뜨린다.
문장 하나를 통째로 자르면 답이 될 단락이 증발한다
문맥 유실의 전형은 청크 경계가 문장 중간에 떨어지는 경우다. 예를 들어 "기본값은 30초이고, 네트워크 상황에 따라 60초까지 늘어난다"는 하나의 청크에 들어가야 하는 문장인데, 분할 기준이 문장 단위라면 앞부분과 뒷부분이 서로 다른 청크에 실린다. "타임아웃 최대치는?"이라는 질문에 임베딩은 청크 하나씩만 보므로 30초와 60초는 각각 다른 벡터가 된다. 어느 쪽도 질문에 온전히 답하지 못한다.
고정 토큰 수 기준 chunking으로 자르면 이 문제가 더 악화된다. 토큰이 문장 한가운데를 갈랐을 때 앞부분엔 숫자만 남고 뒷부분엔 단위가 없는 숫자가 남는다. 벡터 검색은 정규식으로 이런 파편을 찾지 않는다. 유사도 기준으로 보면 반쪽짜리 문장은 '타임아웃'이라는 질문과 거리가 멀어지는 경우가 많다.
이를 피하려면 문장 단위나 단락 단위로 분할하되, 경계를 넘어가는 문장은 잘라내지 말고 뒤쪽 청크에 통째로 넘겨야 한다. 한국어 문서라면 문장 부호를 기준으로 문장을 분리하고, 앞뒤 문장이 겹치도록 오버랩을 주는 방식이 통한다. 오버랩 토큰 수는 전체 청크의 10~15% 수준이면 대부분 충분하다. 더 넣어도 검색 점수는 크게 변하지 않는 대신 저장 용량만 늘어난다.
크게 썰수록 답은 오는데 정답이 묻힌다
반대 축의 실수는 청크를 너무 크게 잡는 것이다. 페이지 전체를 하나의 청크로 넣으면 검색은 항상 '맞는' 청크를 반환한다. 문제는 그 안에 답이 희석되어 있다는 점이다. top-k로 5개를 뽑아 LLM에 넣어도, 답과 무관한 3,000토큰이 질문보다 앞선다. 재생성 단계에서 모델이 그 노이즈에 휘둘리면 정답을 인용하지 않고 둥근 요약에 그친다.
문맥 유실은 재현율 문제이고, 노이즈 유입은 정밀도 문제다. 같은 파이프라인에서 둘 다 발생할 수 있다. 문서 섹션이 길수록 이 둘은 트레이드오프를 만든다. 섹션 단위로 자르면 문맥은 살아나지만 무관한 부분까지 끌려 들어온다. 그래서 섹션이 길 때는 섹션 내부를 문단 단위로 다시 자르고, 각 문단 앞에 섹션 제목을 메타데이터로 붙이는 편이 낫다.
임베딩 모델의 문맥 창을 무시한 크기 설정
여기서 한 가지 더. 청크 크기를 정할 때는 임베딩 모델의 최대 입력 길이를 먼저 확인해야 한다. 모델마다 문맥 창이 제각각이고, 입력 시퀀스가 한도를 넘으면 뒤쪽을 잘라내거나 토큰 평균을 내서 벡터를 만드는 모델도 있다. 그런 모델에 1,000토큰짜리 청크를 넣으면 앞쪽 문맥만 남아 분할한 의미가 반쯤 무너진다.
한도보다 작다고 안심할 수도 없다. 1,000토큰 모델에 800토큰 청크를 넣어도, 앞부분에 가중치를 두도록 학습된 모델이라면 뒷부분 정보가 벡터에 거의 반영되지 않는다. 이 경우는 청크 크기를 줄이는 것보다 긴 문맥용 임베딩 모델로 교체하는 게 나을 수 있다. 어느 쪽이 맞는지는 상황에 따라 다르다. 정해진 테스트 쿼리 셋으로 두 구성을 비교해보면 판단이 끝난다.
분할을 평가할 때는 점수가 아니라 적중 여부를 본다
분할이 잘 됐는지 판단하는 흔한 실수는 유사도 점수만 보는 것이다. 유사도 0.85가 0.80보다 낫다는 보장은 없다. 모델마다 점수 분포가 다르고, 같은 모델이라도 문서 유형에 따라 다르다. 대신 실제로 들어오는 쿼리와 그 정답이 담긴 청크를 짝지은 쿼리 셋을 만들어, 벡터 검색이 그 청크를 top-k 안에 넣는지로 판단하는 게 낫다. 검색 로그에 쌓인 실제 쿼리로 구성하면 더 현실적이다.
재생성 결과로 판단하면 안 되나? 그건 파이프라인 전체 성능이라 분할 문제만 분리하기 어렵다. 검색(Retrieval) 단계만 단독으로 평가하려면, 청크마다 출처 id를 붙이고 검색 결과의 출처가 정답과 일치하는지로 recall@k를 계산하면 된다. k는 운영 환경의 top-k 값으로 고정한다. 테스트용으로 k를 10으로 올려놓고 좋아하는 설정은 실제 프롬프트에 들어가는 개수와 다르다.
좋은 청크 설계는 한 번에 나오지 않는다. 분할 기준, 크기, 오버랩, 평가 쿼리 셋. 이 네 변수를 한 번에 하나씩 바꿔가며 재현율과 정밀도를 표에 기록해보라. 답이 묻히는 쿼리와 답이 증발하는 쿼리가 한 표에서 교차되는 지점이 보일 때가 설계를 고칠 타이밍이다.
댓글
댓글을 읽어오는 중입니다.
같이 읽으면 좋은 글
방금 읽은 주제와 이어지는 글을 골랐습니다.
LLM이 JSON만 뱉기로 약속했을 때 실제로 일어나는 일
OpenAI의 json_object 모드는 구문 유효성만 보장할 뿐 스키마를 강제하지 않는다. Zod/Pydantic 검증, json_repair로 복구하고 validation error를 피드백해 재시도하는 단계별 방어선과 서킷 브레이커, 멀티 프로바이더 폴백까지 실무 패턴을 파헤친다.
AI 에이전트는 서버에서 태어났지만 브라우저에서 산다
대부분의 AI 에이전트가 여전히 서버에서 오케스트레이션을 돌리고 있지만, 브라우저가 가진 런타임 맥락과 WebGPU·WebLLM의 발전이 이 판도를 바꾸고 있다. 서버 중심과 브라우저 네이티브 아키텍처의 지연 시간, 개인정보 보호, 비용, 확장성을 비교하며 왜 지금 이 전환이 중요한지 분석한다.
LLM 서빙의 숨은 병목, KVCache를 무기로 바꾸다
Moonshot AI의 Kimi 서비스를 지탱하는 Mooncake 플랫폼의 KVCache 중심 분리형 아키텍처를 분석한다. Prefill과 Decode를 분리하고 유휴 CPU·DRAM·SSD 자원을 캐시 풀로 전환해 처리량을 75% 이상 끌어올린 설계 철학과 vLLM·SGLang 생태계 확장을 추적한다.
이전 글
번들이 실은 비밀 키, 프런트에서 남는 흔적들
DevInsight Digest
새 글이 쌓이면, 피드에서 바로 이어 읽으세요.
과장된 알림 대신 발행한 글 전체를 RSS로 제공합니다.