ChatGPT한테 "우리 회사 작년 사내 규정 알려줘" 하면 절대 모른다. 학습한 적이 없으니까. 그런데 요즘 "사내 문서 물어보면 답해주는 챗봇"은 어떻게 만드는 걸까? 그 비밀이 바로 RAG다.
오늘은 RAG가 뭔지, 그리고 RAG를 이해하려면 꼭 알아야 하는 임베딩과 벡터DB까지 한 번에 정리해본다. 이 세 개는 따로 노는 개념이 아니라 하나의 흐름으로 엮여 있어서, 같이 보면 이해가 훨씬 빠르다.
먼저 문제부터: LLM은 모르는 게 많다
GPT 같은 LLM은 똑똑하지만 두 가지 한계가 있다.
- 학습 시점 이후의 일을 모른다. 작년 데이터까지만 배웠으면 올해 일은 모른다.
- 내 개인/회사 데이터를 모른다. 우리 회사 위키, 내 노션, 사내 규정 같은 건 학습한 적이 없다.
그래서 LLM한테 모르는 걸 물으면 그냥 모른다고 하거나, 더 나쁘게는 그럴듯하게 지어낸다(환각, hallucination).
이걸 해결하는 가장 직관적인 방법은 뭘까? "모르면 자료를 찾아서 보여주고, 그 자료를 보고 답하게 하면 되잖아?" 맞다. 그게 바로 RAG의 핵심 아이디어다.
RAG = 검색해서(Retrieval) + 보강해서(Augmented) + 생성하기(Generation)
RAG는 Retrieval-Augmented Generation의 약자다. 이름을 그대로 풀면 흐름이 보인다.
- Retrieval(검색): 사용자가 질문하면, 관련 있는 문서를 먼저 찾아온다.
- Augmented(보강): 찾아온 문서를 질문이랑 같이 LLM한테 넘긴다.
- Generation(생성): LLM은 그 문서를 참고해서 답을 만든다.
비유하자면 이렇다. 시험을 보는데 LLM 혼자 기억으로 푸는 게 아니라, 오픈북 시험처럼 관련 페이지를 먼저 펼쳐주고 그걸 보고 답하게 하는 거다. 당연히 정확도가 올라가고, 지어내는 것도 줄어든다.
[사용자 질문]
│
▼
[관련 문서 검색] ← 여기가 핵심! 어떻게 "관련 있는" 문서를 찾지?
│
▼
[질문 + 문서] → [LLM] → [답변]그런데 여기서 진짜 질문이 생긴다. "관련 있는 문서"를 도대체 어떻게 찾지? 그냥 키워드 검색하면 되는 거 아닌가? 여기서 임베딩이 등장한다.
임베딩(Embedding): 의미를 숫자로 바꾸기
키워드 검색에는 한계가 있다. 사용자가 "퇴사하면 연차 수당 받나요?"라고 물었는데, 문서에는 "근로 종료 시 미사용 휴가 보상"이라고 적혀 있다면? 단어가 하나도 안 겹쳐서 키워드 검색으로는 못 찾는다. 하지만 둘은 같은 의미다.
그래서 필요한 게 임베딩이다. 임베딩은 글자(텍스트)를 의미를 담은 숫자 배열(벡터)로 바꾸는 것이다.
"퇴사하면 연차 수당 받나요?" → [0.21, -0.88, 0.45, ... ]
"근로 종료 시 미사용 휴가 보상" → [0.19, -0.85, 0.48, ... ]
"오늘 점심 뭐 먹지" → [-0.72, 0.33, -0.10, ... ]핵심은 이거다. 의미가 비슷한 문장은 숫자(벡터)도 비슷하게 나온다. 위에서 첫 번째랑 두 번째는 숫자가 비슷하고, 세 번째(점심)는 완전히 다르다.
이렇게 숫자로 바꿔두면, 두 문장이 얼마나 비슷한지를 수학적으로 계산할 수 있다. 벡터 사이의 거리(또는 각도)를 재면 "이 두 문장이 의미적으로 가깝다/멀다"를 판단할 수 있는 거다. 보통 코사인 유사도(cosine similarity)라는 걸 쓴다.
정리하면, 임베딩 덕분에 "단어가 겹치는 문서"가 아니라 "의미가 비슷한 문서"를 찾을 수 있게 된다.
벡터DB: 임베딩을 저장하고 빠르게 검색하기
이제 문제가 하나 더 생긴다. 회사 문서가 10만 개라고 하자. 사용자가 질문할 때마다 10만 개 벡터랑 일일이 거리를 계산하면 너무 느리다.
그래서 벡터DB(Vector Database)가 필요하다. 벡터DB는 이름 그대로 벡터(임베딩)를 저장하고, 그중에서 가장 비슷한 것들을 빠르게 찾아주는 데 특화된 데이터베이스다.
일반 DB(MySQL 등)는 "id가 3인 행 찾아줘" 같은 정확한 일치 검색에 강하다. 반면 벡터DB는 "이 벡터랑 가장 비슷한 상위 5개 찾아줘" 같은 유사도 검색에 특화돼 있다. 내부적으로는 ANN(근사 최근접 이웃) 같은 알고리즘을 써서, 10만 개를 다 비교하지 않고도 비슷한 걸 빠르게 추려낸다.
대표적인 벡터DB로는 Pinecone, Weaviate, Qdrant, Chroma, 그리고 PostgreSQL 확장인 pgvector 등이 있다. 입문 단계에서는 설치가 간단한 Chroma나, 기존 Postgres에 얹는 pgvector부터 써보는 걸 추천한다.
전체 그림 다시 보기
이제 세 개념이 어떻게 한 흐름으로 엮이는지 다시 보자. RAG는 크게 준비 단계와 질문 단계로 나뉜다.
준비 단계 (미리 한 번):
회사 문서들 → [임베딩 모델] → 벡터로 변환 → [벡터DB에 저장]문서를 적당한 크기로 쪼개고(청킹), 각 조각을 임베딩해서 벡터DB에 차곡차곡 넣어둔다.
질문 단계 (사용자가 물을 때마다):
1. 사용자 질문 → [임베딩 모델] → 질문도 벡터로 변환
2. 그 벡터로 [벡터DB] 검색 → 가장 비슷한 문서 조각 N개 가져옴
3. [질문 + 가져온 문서] → [LLM] → 최종 답변질문도 똑같이 벡터로 바꾼 다음, 벡터DB에서 의미가 가까운 문서를 찾고, 그걸 LLM한테 같이 넘겨서 답하게 하는 거다.
아주 간단한 의사코드로 보기
말로만 보면 추상적이니까, 파이썬 의사코드로 흐름만 잡아보자.
# === 준비 단계 ===
docs = load_company_documents() # 회사 문서 불러오기
chunks = split_into_chunks(docs) # 적당히 쪼개기
for chunk in chunks:
vector = embedding_model.embed(chunk) # 임베딩
vector_db.save(vector, text=chunk) # 벡터DB에 저장
# === 질문 단계 ===
question = "퇴사하면 연차 수당 받나요?"
q_vector = embedding_model.embed(question) # 질문 임베딩
related = vector_db.search(q_vector, top_k=3) # 비슷한 문서 3개 검색
prompt = f"""
아래 문서를 참고해서 질문에 답해줘.
문서:
{related}
질문: {question}
"""
answer = llm.generate(prompt) # LLM이 문서 보고 답변
print(answer)
핵심은 vector_db.search()로 관련 문서를 먼저 찾고, 그걸 프롬프트에 넣어서 LLM이 그 문서를 근거로 답하게 한다는 점이다. 이게 RAG의 전부다.
정리
| 개념 | 한 줄 요약 | 역할 |
|---|---|---|
| RAG | 검색해서 찾은 문서를 보고 답하기 | 전체 흐름 |
| 임베딩 | 텍스트를 의미가 담긴 벡터로 변환 | 의미 비교 가능하게 |
| 벡터DB | 벡터를 저장하고 비슷한 것 빠르게 검색 | 빠른 유사도 검색 |
세 줄로 압축하면 이렇다.
- LLM은 내 데이터를 모르니까, 관련 문서를 찾아서 같이 넘겨주자 → 이게 RAG
- "관련 문서"를 의미 기준으로 찾으려면 텍스트를 숫자로 바꿔야 한다 → 이게 임베딩
- 그 숫자(벡터)를 빠르게 검색하려면 전용 DB가 필요하다 → 이게 벡터DB
RAG는 지금 LLM 애플리케이션에서 가장 많이 쓰이는 패턴 중 하나다. 챗봇, 사내 문서 검색, 고객 지원 자동화 같은 게 거의 다 이 구조로 돌아간다. 오늘은 큰 그림만 잡았으니, 다음엔 직접 Chroma나 pgvector로 간단한 RAG를 만들어보는 실습 글로 이어가보자.
'프로그래밍 > AI·LLM' 카테고리의 다른 글
| [AI/LLM] RAG 검색 정확도 올리기 — 청킹 전략과 검색 품질 (1) | 2026.07.06 |
|---|---|
| [AI/LLM] 직접 만들어보는 RAG — Chroma와 pgvector 실습편 (0) | 2026.07.03 |