RAG를 처음 이해할 때는 “외부 문서를 검색해서 답변하는 구조” 정도로 이해해도 충분합니다.
하지만 실제로 RAG가 어떻게 동작하는지 보려면 조금 더 안쪽 구조를 살펴볼 필요가 있습니다.
RAG는 원본 문서를 그대로 LLM에게 넣지 않습니다. 문서를 읽고, 작은 단위로 나누고, 검색 가능한 숫자 형태로 바꾼 뒤, 질문과 관련된 부분만 찾아 LLM에게 전달합니다.
이번 글에서는 RAG를 구성하는 핵심 요소인 Document, Chunk, Embedding, Vector, Vector DB, Retriever, Context를 쉽게 정리해보겠습니다.
RAG의 전체 흐름
RAG의 기본 흐름은 다음과 같습니다.
원본 문서
→ Document
→ Chunk
→ Embedding
→ Vector
→ Vector DB
→ Retriever
→ Context
→ Prompt
→ LLM 답변
처음 보면 용어가 많아 보이지만, 핵심은 단순합니다.
긴 문서를 검색하기 좋은 작은 단위로 바꾸고, 질문과 비슷한 문서 조각만 찾아 LLM에게 전달하는 구조입니다.

원본 문서란?
RAG에서 가장 먼저 필요한 것은 LLM이 참고할 수 있는 원본 자료입니다.
예를 들면 다음과 같은 자료가 원본 문서가 될 수 있습니다.
- PDF 문서
- DOCX 파일
- TXT 파일
- HTML 페이지
- 웹페이지
- 회사 업무 매뉴얼
- 정책 문서
- 블로그 작성 가이드
- 개인 공부 노트
예를 들어 회사 근태 매뉴얼 PDF가 있다고 해보겠습니다.
회사_근태_매뉴얼.pdf
이 문서 안에는 연차 신청, 반차 신청, 지각 처리, 휴가 승인 절차 같은 내용이 들어 있을 수 있습니다.
하지만 LLM이 이 PDF 파일을 자동으로 알고 있는 것은 아닙니다. RAG 시스템은 먼저 이 문서를 읽고, 텍스트로 변환하고, 검색 가능한 형태로 바꾸는 과정을 거쳐야 합니다.
Document란?
RAG에서 말하는 Document는 단순히 파일 하나를 뜻하는 것이 아닙니다.
개발자 관점에서 Document는 보통 문서 내용과 부가 정보를 함께 가진 객체로 다뤄집니다.
Document = page_content + metadata
예를 들어 다음과 같습니다.
page_content:
연차 신청은 HR 시스템 > 근태관리 > 휴가신청 메뉴에서 진행한다.
metadata:
source: 회사_근태_매뉴얼.pdf
page: 12
category: 근태
즉, Document는 문서 내용과 출처 정보를 함께 가진 데이터 객체라고 볼 수 있습니다.
여기서 metadata는 매우 중요합니다. 나중에 답변할 때 어떤 문서를 근거로 답했는지 표시하거나, 특정 문서만 검색하도록 제한할 수 있기 때문입니다.
예를 들어 metadata는 다음과 같은 방식으로 활용할 수 있습니다.
- 회사_근태_매뉴얼.pdf에서만 검색
- 2026년 자료만 검색
- category가 근태인 문서만 검색
- 답변에 출처 페이지 표시
왜 문서를 Chunk로 나눌까?
RAG에서 가장 중요한 작업 중 하나가 문서를 작은 단위로 나누는 것입니다.
이 작업을 보통 Chunking이라고 부르고, 나눠진 작은 문서 조각을 Chunk라고 합니다.
문서를 Chunk로 나누는 이유는 간단합니다. 긴 문서를 통째로 LLM에게 넣는 것은 비효율적이기 때문입니다.
예를 들어 100페이지짜리 회사 매뉴얼이 있다고 해보겠습니다.
사용자 질문은 다음과 같습니다.
연차 신청은 어디서 해?
이 질문에 답하기 위해 100페이지 전체가 필요한 것은 아닙니다. 필요한 것은 아마 다음과 같은 문장 하나일 수 있습니다.
연차 신청은 HR 시스템 > 근태관리 > 휴가신청 메뉴에서 진행한다.
문서 전체를 넣으면 다음과 같은 문제가 생길 수 있습니다.
- LLM 입력 길이 제한에 걸릴 수 있습니다.
- 질문과 관련 없는 내용이 너무 많이 포함됩니다.
- 검색 정확도가 떨어질 수 있습니다.
- 답변 생성 속도가 느려질 수 있습니다.
- 비용이 늘어날 수 있습니다.
그래서 RAG는 문서를 작은 Chunk로 나눈 뒤, 질문과 관련 있는 Chunk만 찾아서 LLM에게 전달합니다.
Chunk란?
Chunk는 문서를 검색하기 좋게 나눈 작은 조각입니다.
예를 들어 원본 문서에 다음 내용이 있다고 해보겠습니다.
우리 회사의 근태 규정은 다음과 같다.
연차 신청은 HR 시스템 > 근태관리 > 휴가신청 메뉴에서 진행한다.
반차는 오전 반차와 오후 반차로 구분된다.
지각은 월 3회 이상 발생하면 관리자 면담 대상이 된다.
이 문서를 Chunk로 나누면 다음과 같이 나눌 수 있습니다.
Chunk 1:
우리 회사의 근태 규정은 다음과 같다.
연차 신청은 HR 시스템 > 근태관리 > 휴가신청 메뉴에서 진행한다.
Chunk 2:
반차는 오전 반차와 오후 반차로 구분된다.
지각은 월 3회 이상 발생하면 관리자 면담 대상이 된다.
사용자가 “연차 신청은 어디서 해?”라고 물으면 RAG 시스템은 Chunk 1을 찾아올 가능성이 높습니다.
Chunk 크기는 왜 중요할까?
Chunk는 너무 커도 문제고, 너무 작아도 문제입니다.
Chunk가 너무 크면 하나의 Chunk 안에 여러 주제가 섞일 수 있습니다.
연차, 반차, 지각, 출장, 재택근무, 퇴직 규정이 하나의 Chunk에 들어감
이 경우 질문과 관련 없는 내용까지 함께 검색될 수 있습니다. 그러면 LLM이 불필요한 정보를 함께 참고하게 됩니다.
반대로 Chunk가 너무 작으면 문맥이 사라질 수 있습니다.
연차 신청은
HR 시스템
근태관리
휴가신청
이렇게 너무 잘게 나누면 각 조각만 봐서는 의미를 파악하기 어렵습니다.
따라서 Chunk는 질문에 답할 수 있을 만큼의 문맥은 유지하면서, 검색하기에는 부담이 없을 정도의 크기로 나누는 것이 중요합니다.
Chunk Overlap이란?
문서를 Chunk로 나눌 때, 조각 사이에 일부 내용을 겹치게 할 수 있습니다. 이것을 Chunk Overlap이라고 합니다.
예를 들어 원문이 다음과 같다고 해보겠습니다.
연차 신청은 HR 시스템에서 진행한다.
신청 경로는 근태관리 > 휴가신청 메뉴다.
승인은 팀장 확인 후 완료된다.
겹침 없이 나누면 다음과 같습니다.
Chunk 1:
연차 신청은 HR 시스템에서 진행한다.
Chunk 2:
신청 경로는 근태관리 > 휴가신청 메뉴다.
승인은 팀장 확인 후 완료된다.
이 경우 Chunk 2만 검색되면 “무슨 신청 경로인지”가 애매할 수 있습니다.
Overlap을 적용하면 다음처럼 나눌 수 있습니다.
Chunk 1:
연차 신청은 HR 시스템에서 진행한다.
신청 경로는 근태관리 > 휴가신청 메뉴다.
Chunk 2:
신청 경로는 근태관리 > 휴가신청 메뉴다.
승인은 팀장 확인 후 완료된다.
이렇게 일부 내용을 겹치게 하면 Chunk 사이에서 문맥이 끊기는 문제를 줄일 수 있습니다.
Embedding이란?
문서를 Chunk로 나눈 뒤에는 검색 가능한 형태로 바꿔야 합니다.
여기서 나오는 개념이 Embedding입니다.
Embedding은 텍스트를 의미 기반의 숫자 표현으로 바꾸는 작업입니다.
예를 들어 다음과 같은 문장이 있다고 해보겠습니다.
연차 신청은 어디서 하나요?
Embedding 모델은 이 문장을 다음과 같은 숫자 배열로 바꿉니다.
[0.91, 0.82, 0.13, ...]
이 숫자 배열을 Vector라고 부릅니다.
중요한 점은 이 숫자가 가나다순이나 알파벳순으로 매겨지는 것이 아니라는 점입니다.
Embedding 모델은 문장의 의미를 학습한 뒤, 의미가 비슷한 문장끼리 비슷한 숫자 배열을 가지도록 변환합니다.
예를 들어 다음 문장들은 단어는 조금 다르지만 의미는 비슷합니다.
연차 신청은 어디서 하나요?
휴가 신청 방법 알려줘
휴가신청 메뉴가 어디야?
Embedding을 사용하면 이런 문장들이 비슷한 Vector로 표현될 수 있습니다.
연차 신청은 어디서 하나요?
→ [0.91, 0.82, 0.13]
휴가 신청 방법 알려줘
→ [0.89, 0.80, 0.15]
휴가신청 메뉴가 어디야?
→ [0.90, 0.81, 0.12]
즉, Embedding은 문장을 단순히 글자 기준으로 비교하는 것이 아니라, 의미가 비슷한지 비교할 수 있도록 숫자로 바꾸는 과정입니다.
Vector란?
Vector는 Embedding의 결과로 나온 숫자 배열입니다.
정리하면 다음과 같습니다.
Embedding = 텍스트를 숫자 Vector로 바꾸는 작업 또는 모델
Vector = Embedding 결과로 나온 숫자 배열
예를 들어 다음과 같이 볼 수 있습니다.
문장:
연차 신청은 HR 시스템에서 한다.
Embedding 결과:
[0.15, -0.21, 0.03, 0.88, ...]
이 Vector는 사람이 직접 해석하기 위한 숫자라기보다는, 컴퓨터가 문장의 의미적 유사도를 계산하기 위해 사용하는 표현입니다.
Vector DB란?
Vector DB는 Embedding을 통해 만들어진 Vector를 저장하고 검색하는 저장소입니다.
여기서 주의할 점이 있습니다.
Vector DB가 문서를 Vector로 바꾸는 것이 아닙니다. 문서를 Vector로 바꾸는 것은 Embedding 모델이고, Vector DB는 그 결과를 저장하고 검색합니다.
예를 들어 하나의 Chunk가 Vector DB에 저장될 때는 다음과 같은 형태가 될 수 있습니다.
id: 1
content: 연차 신청은 HR 시스템 > 근태관리 > 휴가신청 메뉴에서 진행한다.
vector: [0.15, -0.21, 0.03, 0.88, ...]
metadata: {source: "회사_근태_매뉴얼.pdf", page: 12}
사용자가 질문하면 질문도 Embedding 모델을 통해 Vector로 변환됩니다.
질문:
연차는 어디서 신청해?
질문 Vector:
[0.14, -0.19, 0.05, 0.82, ...]
그다음 Vector DB는 질문 Vector와 가까운 문서 Vector를 찾습니다.
질문 Vector와 비슷한 Chunk 검색
→ 관련 문서 반환
즉, Vector DB의 역할은 Vector를 저장하고, 질문과 의미적으로 가까운 문서 조각을 찾아주는 것입니다.
Retriever란?
Retriever는 사용자 질문과 관련된 문서를 찾아오는 검색 인터페이스입니다.
Vector DB가 저장소라면, Retriever는 그 저장소에서 필요한 자료를 찾아오는 담당자에 가깝습니다.
사용자 질문
→ Retriever
→ 관련 Chunk 반환
예를 들어 사용자가 이렇게 질문했다고 해보겠습니다.
연차 신청은 어디서 해?
Retriever는 관련 Chunk를 찾아옵니다.
검색 결과:
1. 연차 신청은 HR 시스템 > 근태관리 > 휴가신청 메뉴에서 진행한다.
2. 휴가 신청은 팀장 승인 후 완료된다.
3. 반차 신청도 동일한 메뉴에서 가능하다.
여기서 중요한 점은 Retriever가 반드시 Vector DB만 사용하는 것은 아니라는 점입니다.
Retriever는 내부적으로 다양한 검색 방식을 사용할 수 있습니다.
- Vector DB 검색
- 키워드 검색
- SQL 조회
- Elasticsearch 검색
- API 호출
- 하이브리드 검색
따라서 Retriever는 Vector DB보다 더 넓은 개념입니다.
| 구분 | 역할 |
|---|---|
| Vector DB | Vector와 문서 Chunk를 저장하고 유사도 검색을 수행하는 저장소 |
| Retriever | 사용자 질문에 맞는 문서를 찾아오는 검색 인터페이스 |
| LLM | 검색된 내용을 바탕으로 최종 답변을 생성하는 모델 |
Context란?
Context는 Retriever가 찾아온 관련 문서 내용을 LLM이 참고할 수 있도록 Prompt에 넣어주는 참고 자료입니다.
예를 들어 Retriever가 다음 내용을 찾아왔다고 해보겠습니다.
연차 신청은 HR 시스템 > 근태관리 > 휴가신청 메뉴에서 진행한다.
휴가 신청은 팀장 승인 후 완료된다.
이 내용은 LLM에게 다음과 같은 형태로 전달될 수 있습니다.
아래 Context를 참고해서 Question에 답변하세요.
Context:
연차 신청은 HR 시스템 > 근태관리 > 휴가신청 메뉴에서 진행한다.
휴가 신청은 팀장 승인 후 완료된다.
Question:
연차 신청은 어디서 해?
그러면 LLM은 Context를 참고해서 답변합니다.
연차 신청은 HR 시스템의 근태관리 > 휴가신청 메뉴에서 진행할 수 있습니다.
즉, Context는 LLM이 답변을 만들 때 참고하는 검색된 근거 자료라고 보면 됩니다.
RAG 준비 단계와 질문 단계
RAG는 크게 두 단계로 나눌 수 있습니다.
1. 준비 단계: Indexing
질문이 들어오기 전에 문서를 미리 처리하고 저장하는 단계입니다.
원본 문서
→ Document로 로드
→ Chunk로 분리
→ Embedding 모델로 Vector 생성
→ Vector DB에 저장
이 단계에서 LLM이 문서를 저장하는 것은 아닙니다.
문서를 읽고, Chunk로 나누고, Embedding 모델을 호출하고, Vector DB에 저장하는 것은 별도의 애플리케이션 로직이 담당합니다.
2. 질문 단계: Retrieval & Generation
사용자가 질문했을 때 관련 문서를 검색하고 답변을 생성하는 단계입니다.
사용자 질문
→ 질문을 Embedding 모델로 Vector 변환
→ Vector DB에서 비슷한 Chunk 검색
→ Retriever가 관련 Chunk 반환
→ Chunk를 Context로 Prompt에 넣음
→ LLM이 답변 생성
여기서 LLM은 주로 마지막 단계에서 사용됩니다. 검색된 Context를 읽고 사용자에게 자연스러운 답변을 생성하는 역할을 합니다.
전체 예시로 이해하기
회사 근태 매뉴얼을 RAG에 넣는다고 가정해보겠습니다.
문서 준비
회사_근태_매뉴얼.pdf
→ Document로 로드
→ 연차, 반차, 지각 관련 내용으로 Chunk 분리
→ 각 Chunk를 Embedding
→ Vector DB에 저장
사용자 질문
연차 신청은 어디서 해?
검색 과정
질문을 Vector로 변환
→ Vector DB에서 비슷한 Chunk 검색
→ "연차 신청은 HR 시스템 > 근태관리 > 휴가신청 메뉴에서 진행한다" Chunk 반환
LLM 답변
연차 신청은 HR 시스템의 근태관리 > 휴가신청 메뉴에서 진행할 수 있습니다.
이렇게 RAG는 전체 문서를 그대로 읽는 것이 아니라, 질문과 관련된 문서 조각을 찾아 답변에 활용합니다.
핵심 개념 정리
| 개념 | 의미 |
|---|---|
| 원본 문서 | PDF, TXT, DOCX, HTML 같은 실제 자료 |
| Document | 원본 문서를 읽어 만든 내용과 메타데이터 객체 |
| Chunk | Document를 검색하기 좋게 나눈 작은 조각 |
| Embedding | 텍스트를 의미 기반 숫자 표현으로 바꾸는 작업 또는 모델 |
| Vector | Embedding 결과로 나온 숫자 배열 |
| Vector DB | Vector를 저장하고 비슷한 Vector를 찾는 저장소 |
| Retriever | 질문과 관련된 문서를 찾아오는 검색 인터페이스 |
| Context | LLM에게 참고 자료로 전달되는 검색 결과 |
| Prompt | 질문과 Context를 포함해 LLM에게 보내는 입력 |
| LLM | 검색된 내용을 바탕으로 답변을 생성하는 모델 |
정리
이번 글에서는 RAG의 기본 구조를 구성하는 주요 개념을 살펴봤습니다.
- RAG는 긴 문서를 통째로 LLM에게 넣는 구조가 아닙니다.
- 원본 문서를 Document로 로드하고, 검색하기 좋은 Chunk로 나눕니다.
- Embedding은 텍스트를 의미 기반 숫자 표현으로 바꾸는 작업입니다.
- Vector는 Embedding 결과로 나온 숫자 배열입니다.
- Vector DB는 Vector를 저장하고, 질문과 비슷한 문서 Chunk를 찾는 저장소입니다.
- Retriever는 질문에 맞는 문서를 찾아오는 검색 인터페이스입니다.
- Context는 LLM이 답변할 때 참고하도록 Prompt에 포함되는 검색 결과입니다.
한 문장으로 정리하면 다음과 같습니다.
RAG는 문서를 작은 Chunk로 나누고, Embedding을 통해 Vector로 변환한 뒤, 질문과 관련된 Chunk를 찾아 LLM에게 전달하는 구조입니다.