RAG 시스템의 구성요소와 성능을 좌우하는 요소

RAG 시스템을 구성하는 요소들 — Vector DB, 임베딩 모델, 청킹, 재순위, 생성 LLM — 의 역할을 정리하고, 한국어 처리에서 중요한 지점과 성능이 나쁠 때 어디를 손봐야 하는지 살펴봅니다.

앞 글에서 RAG가 결국 프롬프트 레이어에 데이터를 공급하는 장치지만, 앞단의 검색/필터링 파이프라인이 충분히 복잡해 별도 서브시스템으로 다룰 만하다고 정리했습니다. 이 글은 그 서브시스템 안을 들여다봅니다. 어떤 부품으로 구성되는지, 한국어를 다룰 때 어디가 까다로운지, 성능이 안 나올 때 어디를 의심해야 하는지를 정리합니다.

1. RAG 파이프라인의 기본 흐름

전형적인 RAG는 두 시점으로 나눠 볼 수 있습니다.

인덱싱 시점 (오프라인)

원본 문서 → 청킹 → 임베딩 → Vector DB 저장. 메타데이터(출처, 작성일, 권한 등)도 함께 저장.

질의 시점 (온라인)

사용자 질문 → 쿼리 재작성 → 임베딩 → Vector DB 검색 (+ 키워드 검색) → 재순위(rerank) → 컨텍스트 조립 → LLM에 프롬프트로 전달 → 응답 생성.

각 단계마다 별도 모델/시스템이 끼어들 수 있고, 단계마다 다른 튜닝이 필요합니다.

2. Vector DB는 어떤 역할을 하는가

Vector DB는 한마디로 "고차원 벡터의 근사 최근접 이웃(approximate nearest neighbor, ANN) 검색에 특화된 저장소" 입니다. 임베딩 모델이 만든 수백~수천 차원 벡터를 저장하고, "쿼리 벡터와 가장 가까운 K개"를 빠르게 돌려줍니다.

핵심 책임은 세 가지입니다.

  • 벡터 인덱싱: HNSW, IVF, PQ 같은 ANN 인덱스로 검색을 sub-linear 시간에 처리합니다. 정확도 vs. 속도/메모리의 트레이드오프가 있습니다.

  • 유사도 계산: 코사인 유사도, 내적, L2 거리 등. 임베딩 모델이 가정한 거리에 맞춰야 합니다.

  • 메타데이터 필터링: "2025년 이후 문서만", "권한이 있는 문서만" 같은 조건을 벡터 검색과 결합. pre-filter / post-filter 시점이 성능을 크게 좌우합니다.

흔한 오해 지점들이 있습니다.

  • Vector DB가 의미를 이해하지는 않습니다. 의미를 벡터로 인코딩하는 건 임베딩 모델이 하고, Vector DB는 벡터 간 기하학적 거리만 봅니다. 검색 품질이 나쁘면 대개 임베딩 모델 문제이지 Vector DB 문제가 아닙니다.

  • 벡터 검색이 항상 키워드 검색보다 낫지는 않습니다. 고유명사, 제품번호, 코드 같은 토큰은 BM25 같은 lexical 검색이 더 정확합니다. 그래서 실제 RAG는 벡터 + 키워드 하이브리드가 기본입니다.

  • 전용 Vector DB가 늘 필요하지도 않습니다. PostgreSQL pgvector, Elasticsearch/OpenSearch의 dense vector 필드, Redis vector search 모듈로도 상당 규모까지 커버됩니다. 운영 부담을 줄이려면 기존 DB 확장이 합리적일 때가 많습니다. Pinecone, Weaviate, Qdrant, Milvus 같은 전용 솔루션은 규모/성능 요구가 분명해질 때 고려하면 됩니다.

3. 한국어 처리에서 중요한 구성요소

한국어 RAG는 영어보다 손이 많이 갑니다. 어디서 차이 나는지 짚어봅니다.

3.1. 임베딩 모델의 한국어 이해 능력

가장 결정적인 부품입니다. OpenAI text-embedding-3, Cohere embed-multilingual, Voyage, BGE-M3 같은 다국어 임베딩이 점점 좋아지고 있지만, 한국어 전용 튜닝 모델(KURE, bge-m3-korean, 네이버 HyperCLOVA 임베딩 등)이 도메인에 따라 더 잘 맞기도 합니다. MTEB 한국어 벤치마크를 참고하되, 자기 도메인 문서로 직접 평가해보는 게 가장 정확합니다.

3.2. 토크나이저와 형태소 분석

  • 벡터 검색 쪽: 임베딩 모델 내부 토크나이저를 따라가므로 사용자가 손댈 여지가 적습니다.

  • 키워드 검색 쪽: 한국어에서 가장 손이 많이 가는 지점입니다. 영어는 공백 분리만으로도 어느 정도 작동하지만, 한국어는 조사/어미가 붙은 채로 인덱싱되면 검색이 망가집니다. "은행에서", "은행이", "은행은"이 다 다른 토큰이 되니까요.

    Elasticsearch/OpenSearch는 Nori 분석기, 그 외 Mecab-ko / Khaiii / KIWI 같은 형태소 분석기를 식별자 추출/색인에 씁니다. 사용자 사전(custom dictionary) 관리가 매우 중요합니다. 신조어, 사내 용어, 제품명을 분석기가 모르면 엉뚱한 토큰으로 쪼개집니다.

3.3. 청킹 전략

한국어 문서는 한 문장이 길고 접속사 없이 늘어지는 경우가 많아 고정 길이 청킹이 의미 단위를 자주 끊습니다.

  • 문장 분리(sentence splitting) 가 마침표 한 종류만으로는 안 됩니다. "다.", "요.", "음.", "임." 같은 어미와 줄바꿈, 표/리스트 마크업까지 함께 봐야 합니다. KSS(Korean Sentence Splitter) 같은 라이브러리가 도움이 됩니다.

  • 의미 단위 청킹(semantic chunking) — 임베딩 유사도가 급격히 변하는 지점에서 자르기 — 이 한국어에서 효과가 좋은 편입니다.

  • 계층적 청킹: 큰 청크로 검색하고 매칭된 부분의 작은 청크를 LLM에 넘기는 식. 문맥 의존성이 강한 한국어에 특히 유용합니다.

3.4. 재순위(rerank) 모델

Cross-encoder 기반 reranker는 검색 결과를 다시 정렬해 정확도를 크게 끌어올립니다. Cohere Rerank 다국어 모델, BGE-reranker, Jina multilingual reranker 등이 한국어를 다룹니다. 한국어 전용 reranker는 아직 선택지가 적어, 다국어 모델로 시작해 자체 평가셋으로 fine-tune을 검토하는 흐름이 흔합니다.

3.5. 쿼리 처리

한국어 질문은 구어체, 줄임말, 오타가 많습니다. 쿼리 재작성(query rewriting) 이나 HyDE(hypothetical document embedding) 로 LLM을 한 번 거쳐 정제하면 검색 품질이 눈에 띄게 좋아집니다. 이때 쓰는 LLM은 작아도 충분합니다.

4. RAG 안에서 쓰이는 LLM들

"RAG에 LLM 하나"라고 생각하기 쉽지만, 실제 시스템에는 서로 다른 역할의 모델 여러 개가 들어가고, 각각 요구되는 특성도 다릅니다.

위치 역할 필요한 특성

임베딩 모델

텍스트를 벡터로 변환

다국어/한국어 품질, 적절한 차원 수, 빠른 추론 속도, 긴 입력 지원

쿼리 재작성 / 분기

사용자 질문을 정제하거나 어떤 도구/인덱스를 쓸지 결정

작고 빠른 instruction-following 모델로 충분. 지연 시간이 중요

재순위 모델

검색된 후보 K개를 정밀 점수화

Cross-encoder. 일반 LLM이 아니라 전용으로 학습된 reranker가 효율적

응답 생성 LLM

검색된 문맥을 바탕으로 최종 답변 작성

긴 컨텍스트, 인용 능력, 환각 억제, 한국어 자연스러움. 대형 모델이 유리

평가 / 가드레일

응답이 컨텍스트에 충실한지, 안전한지 검증

비교적 작은 모델로 LLM-as-a-judge. 일관성과 비용이 중요

흔한 구성은 "쿼리 처리/판정은 작고 빠른 모델, 최종 생성은 큰 모델" 입니다. 전부 큰 모델로 돌리면 비용과 지연이 누적되고, 전부 작은 모델로 돌리면 답변 품질이 무너집니다. 단계별 적정 사이즈 선택이 RAG 설계의 중요한 부분입니다.

또 한 가지, 검색 결과를 잘 활용하는 능력은 모델마다 다릅니다. 같은 컨텍스트를 줘도 어떤 모델은 인용에 충실하고, 어떤 모델은 사전 지식으로 답을 만들어버립니다. RAG 평가 때는 모델의 groundedness(컨텍스트 충실도)를 따로 측정하는 게 좋습니다.

5. 성능이 안 나올 때 어디를 손볼 것인가

RAG 결과가 만족스럽지 않을 때, 무작정 더 큰 LLM으로 갈아끼우기 전에 파이프라인의 어느 단계가 망가졌는지 진단하는 게 우선입니다. 흔한 원인을 단계별로 정리합니다.

5.1. 1단계 — 검색이 잘못 가져온다 (retrieval failure)

가장 흔한 원인입니다. 답이 든 문서를 애초에 못 찾아왔다면, 뒤에 어떤 LLM을 써도 좋아질 수 없습니다.

진단: 정답이 든 청크가 top-K 안에 들어오는지 확인. 안 들어오면 검색 단계 문제입니다.

손볼 곳:

  • 임베딩 모델 교체: 한국어 품질이 더 좋은 모델로. 도메인 특화 모델 검토.

  • 하이브리드 검색 도입: 벡터 + BM25 결합. 고유명사/숫자 매칭이 안 되던 케이스가 풀립니다.

  • 청킹 재설계: 너무 크면 노이즈, 너무 작으면 문맥이 끊깁니다. 한국어는 보통 300~800 토큰에서 시작해 조정.

  • 메타데이터 필터: 검색 공간을 좁히는 가장 효과적인 수단. 날짜, 출처, 권한 등을 적극 활용.

  • 사용자 사전: 키워드 검색 쪽 도메인 용어가 잘못 토큰화되는지 점검.

5.2. 2단계 — 가져오긴 했는데 순서가 나쁘다 (ranking failure)

top-20에는 정답이 있는데 top-3에는 없는 경우. LLM에 넘기는 컨텍스트가 한정돼 있어 순서가 결과를 좌우합니다.

손볼 곳:

  • Reranker 추가: cross-encoder reranker가 가장 큰 효과를 내는 지점입니다.

  • 쿼리 재작성: 모호하거나 구어체인 질문을 검색에 유리한 형태로 변환.

  • 다중 쿼리 (multi-query): 한 질문을 여러 변형으로 던져 결과를 합칩니다.

5.3. 3단계 — 컨텍스트는 좋은데 생성이 망가진다 (generation failure)

검색은 잘 됐고 정답 청크가 top에 있는데 최종 응답이 틀리거나 환각이 섞이는 경우.

손볼 곳:

  • 프롬프트 구조: "주어진 문맥에만 근거해 답하라", "문맥에 없으면 모른다고 답하라" 같은 지시. 인용 형식 강제.

  • 컨텍스트 배치: 중요 문서를 맨 앞이나 맨 뒤에. "lost in the middle" 회피.

  • 생성 모델 업그레이드: 여기서는 큰 모델이 효과적. 단, 1~2단계가 망가져 있으면 모델만 키워도 효과가 없습니다.

  • citation / grounded generation: 답변마다 출처 청크를 명시하게 하면 환각이 줄어듭니다.

5.4. 4단계 — 평가가 없어서 어디가 망가졌는지 모른다 (evaluation gap)

실은 가장 근본적인 문제입니다. 평가셋이 없으면 변경이 개선인지 알 수 없습니다. "감으로" 튜닝하다 보면 한쪽을 고치며 다른 쪽이 망가집니다.

손볼 곳:

  • 질문-정답-출처 셋으로 된 평가 데이터셋 구축. 50~100개로도 시작 가능.

  • 단계별 지표: 검색은 recall@K, MRR. 생성은 faithfulness, answer relevance.

  • Ragas, TruLens 같은 평가 프레임워크 활용.

  • LLM-as-a-judge로 자동화하되 사람이 주기적으로 보정.

6. 어디부터 손댈지

새 RAG 시스템을 만들거나 기존 것을 개선할 때 제가 보는 우선순위는 다음과 같습니다.

  1. 평가셋부터 만든다. 없으면 개선이 개선인지 모릅니다.

  2. 하이브리드 검색을 기본으로. 벡터만 쓰지 않습니다.

  3. 한국어라면 형태소 분석기와 사용자 사전 정비. 키워드 검색의 비중이 의외로 큽니다.

  4. reranker 도입. 비용 대비 효과가 큰 편입니다.

  5. 생성 모델 업그레이드는 마지막에. 가장 비싸고, 1~3이 부실하면 효과가 작습니다.

RAG의 성능 개선은 "더 큰 모델"이 아니라 "더 정확한 검색" 에서 오는 경우가 대부분입니다. 앞 글에서 RAG를 "프롬프트 레이어로 데이터를 흘려보내는 파이프라인"이라 부른 이유도 여기 있습니다. 모델은 토큰으로 들어온 것에 답할 뿐, 들어오지 않은 것에는 답할 수 없습니다.


이 포스트는 Claude(claude.ai)와 정상혁이 함께 작성했습니다.

LLM에 정보를 전달하는 계층 분류와 회색지대