회사 챗봇에게 “올해 휴가 규정이 어떻게 돼?”라고 물었다고 해볼게요. 챗봇은 꽤 자연스럽게 답했지만, 알고 보니 작년 규정이었어요. LLM은 문장을 잘 만들지만 회사의 최신 문서까지 저절로 알고 있지는 않기 때문이에요.
RAG(Retrieval-Augmented Generation, 검색 증강 생성)는 이 문제를 먼저 자료를 찾고, 그 자료를 보고 답하게 하는 방식으로 해결해요. LLM이 기억에만 의존하는 대신 질문과 관련된 문서를 검색한 뒤, 찾은 내용을 근거로 답하는 거예요.
복잡한 이름과 달리 발상은 단순해요. 이 글에서는 RAG가 어떻게 움직이고, 무엇을 잘 챙겨야 쓸 만한 답을 얻을 수 있는지 살펴봐요.
오픈북 시험과 비슷해요
일반적인 LLM 사용이 기억에 의존하는 시험이라면 RAG는 오픈북 시험에 가까워요. 질문을 받으면 관련 페이지를 펼쳐 보고 답을 작성해요.
예를 들어 사내 문서 챗봇은 다음 순서로 답할 수 있어요.
- 사용자가 “경조 휴가는 며칠이야?”라고 질문해요.
- 검색 시스템이 최신 인사 규정에서 관련 문단을 찾아요.
- LLM이 그 문단을 읽고 답변과 출처를 만들어요.

문서가 바뀌면 LLM을 다시 학습할 필요도 없어요. 검색할 문서만 갱신하면 다음 질문부터 새로운 내용을 사용할 수 있어요.
RAG는 이런 상황에 특히 잘 맞아요.
- 사내 규정이나 제품 설명서처럼 LLM이 알 수 없는 자료를 사용할 때
- 정책, 가격, 일정처럼 내용이 자주 바뀔 때
- 답변과 함께 원문 출처를 보여 줘야 할 때
반대로 외부 자료가 필요 없는 글쓰기나 번역까지 무조건 RAG로 만들 필요는 없어요. 자료가 몇 페이지뿐이라 prompt에 그대로 넣을 수 있다면 더 단순한 구성이 나을 수도 있어요.
질문이 답변이 되기까지
RAG는 크게 문서를 준비하는 과정과 질문에 답하는 과정으로 나뉘어요.
| 과정 | 하는 일 |
|---|---|
| 문서 준비 | 문서를 작은 단위로 나누고 검색할 수 있게 저장해요. |
| 검색 | 질문과 관련 있는 문서 조각을 찾아요. |
| 선별 | 검색 결과 중 실제로 도움이 되는 내용만 추려요. |
| 답변 | 질문과 찾은 자료를 LLM에 함께 전달해요. |

코드로 아주 단순하게 표현하면 다음과 같은 흐름이에요. 실제 서비스에서는 각 단계에 vector database나 LLM SDK가 연결돼요.
async function answerWithRag(question: string) {
const candidates = await searchDocuments(question);
const context = await rerank(question, candidates);
return generateAnswer({ question, context });
}여기서 중요한 점은 검색 결과를 전부 LLM에 넣지 않는다는 거예요. 먼저 넓게 찾고, 질문과 가까운 자료만 골라 전달해야 답변이 엉뚱한 방향으로 흐르지 않아요.
문서를 어떻게 나눌까
책 한 권을 통째로 건네며 정답을 찾으라고 하면 필요한 문장을 발견하기 어려워요. 그렇다고 모든 문장을 낱장으로 찢으면 제목이나 앞뒤 조건을 잃어버려요. RAG도 마찬가지예요.
문서를 검색하기 좋은 크기로 나눈 조각을 chunk라고 해요. 좋은 chunk를 만드는 가장 쉬운 출발점은 문서 구조를 그대로 활용하는 거예요.
- 제목과 section을 기준으로 나눠요.
- 표, code block, 질문과 답처럼 함께 읽어야 하는 내용은 붙여 둬요.
- 너무 긴 section만 길이를 기준으로 한 번 더 나눠요.
- 문서 제목, 작성일, version, 접근 권한을 metadata로 함께 저장해요.

FAQ는 질문과 답 하나가 자연스러운 chunk예요. 반면 계약서나 장애 보고서는 앞뒤 조건이 중요해서 조금 더 길게 보존해야 해요. 모든 문서에 통하는 하나의 정답 크기는 없으므로, 실제 사용자의 질문으로 검색 결과를 확인하며 조정하는 편이 좋아요.
검색은 한 가지 방식만 고집하지 않아요
RAG를 이야기할 때 흔히 vector search부터 떠올려요. 문장을 embedding이라는 숫자 배열로 바꾸면 표현이 달라도 의미가 비슷한 문서를 찾을 수 있어요.
예를 들어 “결제 취소”를 검색해도 “환불 방법”이 포함된 문서를 찾을 수 있어요. 단어가 정확히 일치하지 않아도 의미가 가깝기 때문이에요.
하지만 주문 번호, 오류 코드, class 이름처럼 정확한 문자열은 keyword search가 더 잘 찾아요. 그래서 실제 서비스에서는 두 결과를 합치는 hybrid search가 좋은 출발점이 돼요.
검색할 때 접근 권한도 함께 확인해야 해요. 권한 없는 문서를 찾은 다음 화면에서 숨기는 방식은 안전하지 않아요. 검색 단계부터 사용자가 볼 수 있는 문서만 대상으로 삼아야 해요.
많이 찾는 것보다 잘 고르는 게 중요해요
검색 결과를 많이 넣으면 LLM이 더 똑똑해질 것처럼 보이지만 꼭 그렇지는 않아요. 관련 없는 자료가 섞일수록 어떤 내용을 믿어야 할지 판단하기 어려워지고, 처리 시간과 비용도 늘어나요.
reranking은 처음 찾은 후보를 질문과 다시 비교해 순서를 정하는 단계예요. 검색으로 20개를 찾았다면 reranking을 거쳐 가장 관련 있는 5개만 LLM에 전달하는 식이에요.

최종 context에는 원문뿐 아니라 문서 제목과 source ID도 함께 넣는 편이 좋아요. 그래야 답변 아래에 출처를 연결하고, 문제가 생겼을 때 어떤 문서를 사용했는지 확인할 수 있어요.
Fine-tuning과 무엇이 다를까
RAG와 fine-tuning은 해결하려는 문제가 달라요.
| 필요한 것 | 알맞은 방법 |
|---|---|
| 최신 문서의 내용을 찾아 답하기 | RAG |
| 답변에 원문 출처 표시하기 | RAG |
| 항상 정해진 JSON 형식으로 답하기 | Fine-tuning |
| 특정 분류 방식이나 문체 익히기 | Fine-tuning |
쉽게 말해 RAG는 무엇을 참고할지 정하고, fine-tuning은 model이 어떻게 행동할지 조정하는 데 가까워요. 필요하다면 두 방법을 함께 사용할 수도 있어요.
틀린 답은 어디서 시작됐을까
RAG의 답변이 틀렸다고 바로 LLM을 바꾸면 원인을 놓치기 쉬워요. 먼저 어느 단계에서 문제가 생겼는지 확인해야 해요.
필요한 문서를 찾지 못했어요
Chunk가 너무 크거나 작지 않은지 확인해요. 오류 코드처럼 정확한 단어가 중요한 질문이라면 keyword search를 함께 사용해요.
문서는 찾았지만 답이 틀렸어요
관련 없는 자료가 너무 많이 섞였는지 살펴봐요. Reranking 결과와 LLM에 실제로 전달한 문서를 기록해 두면 원인을 찾기 쉬워요.
예전 내용으로 답했어요
원본 문서가 수정되거나 삭제될 때 검색 index도 함께 갱신되는지 확인해요. 새 문서를 추가하는 기능만 만들고 수정과 삭제를 놓치는 경우가 의외로 많아요.
평가도 어렵게 시작할 필요는 없어요. 실제 질문과 정답 문서를 몇 개 준비한 뒤 아래 세 가지부터 확인하면 돼요.
- 검색 결과에 정답 문서가 들어 있는가
- 답변이 찾은 문서의 내용과 일치하는가
- 근거가 없을 때 모른다고 답하는가
작게 시작하기
처음부터 여러 검색 전략과 복잡한 agent를 붙이지 않아도 돼요. 작은 RAG를 먼저 만들고 실패를 관찰하는 편이 빠르게 개선하기 좋아요.
- 실제 사용자가 물어볼 질문과 정답 문서를 준비해요.
- 문서 구조에 맞게 chunk를 만들어요.
- vector search로 관련 chunk를 찾아요.
- 찾은 근거와 함께 답변과 출처를 만들어요.
- 검색이 부족할 때 keyword search와 reranking을 추가해요.
RAG의 핵심은 LLM에 가능한 한 많은 자료를 넣는 데 있지 않아요. 질문에 필요한 자료를 잘 찾고, 답변에 쓸 근거만 건네는 데 있어요. 좋은 model을 고르는 일만큼 좋은 검색 결과를 만드는 일이 중요한 이유예요.
