RAG 초보자 가이드 — LLM에 내 데이터를 연결하는 법
RAG(검색 증강 생성)의 핵심 개념, 동작 원리, 임베딩과 벡터 DB 기초를 정리한 입문 가이드.
ChatGPT한테 우리 회사 내부 문서에 대해 물어보면? 당연히 모른다. 학습 데이터에 없으니까. 그렇다고 모델을 파인튜닝하자니 비용도 시간도 만만치 않다. 그래서 나온 게 RAG다.
RAG가 뭔가
RAG는 Retrieval-Augmented Generation의 약자다. 한국어로는 "검색 증강 생성"이라고 하는데, 이름이 거창할 뿐 개념은 단순하다.
- 사용자가 질문을 한다
- 관련 문서를 검색해서 찾는다
- 찾은 문서를 LLM 프롬프트에 같이 넣는다
- LLM이 해당 문서를 참고해서 답변한다
오픈북 시험이라고 생각하면 된다. LLM이 알아서 답을 만들어내는 게 아니라, 참고 자료를 옆에 펼쳐놓고 답하는 방식이다.
왜 파인튜닝 대신 RAG를 쓸까
파인튜닝과 RAG는 "LLM에 새로운 지식을 주입한다"는 목적은 같지만 접근 방식이 완전 다르다.
파인튜닝은 모델의 가중치 자체를 바꾼다. 학습 데이터를 준비하고, GPU를 돌려서 모델을 재학습시킨다. 비용이 크고, 데이터가 바뀌면 다시 학습해야 한다. 모델이 "알게" 되는 거라서 할루시네이션 통제가 어렵다.
RAG는 모델을 건드리지 않는다. 검색 시스템만 붙이면 된다. 데이터가 바뀌면 문서만 업데이트하면 그만이다. 출처를 명시할 수 있어서 답변의 근거가 명확하다.
실제로는 둘 다 쓰는 경우도 있다. 하지만 대부분의 "내 데이터를 LLM에 연결하고 싶다" 유스케이스에서 RAG가 첫 선택이 되는 이유는 비용 대비 효과가 훨씬 좋기 때문이다.
핵심 구성 요소
RAG 파이프라인은 크게 세 부분으로 나뉜다.
1. 문서 전처리
원본 문서를 LLM이 소화할 수 있는 형태로 잘라야 한다. PDF, 워드, 웹페이지 등 다양한 포맷의 문서를 텍스트로 변환하고, 적절한 크기의 "청크(chunk)"로 분할한다.
청크 크기는 보통 500~1000 토큰 정도로 잡는다. 너무 크면 검색 정확도가 떨어지고, 너무 작으면 맥락이 끊긴다. 겹치는 구간(overlap)을 두기도 하는데, 문단 경계에서 정보가 잘리는 걸 방지하기 위해서다.
원본 문서 → 텍스트 추출 → 청크 분할 → 임베딩 생성 → 벡터 DB 저장
2. 임베딩과 벡터 DB
여기가 RAG의 핵심이다. 텍스트를 숫자 벡터로 변환하는 걸 "임베딩"이라고 한다.
"고양이"와 "강아지"는 텍스트로 보면 전혀 다르지만, 임베딩 공간에서는 가까운 위치에 놓인다. 의미가 비슷하니까. "자동차"는 둘 다와 거리가 멀다. 이런 식으로 의미적 유사도를 수치로 계산할 수 있게 만드는 게 임베딩의 역할이다.
임베딩 모델은 OpenAI의 text-embedding-3-small이나 오픈소스인 bge-m3 같은 걸 쓴다. 모든 청크를 임베딩으로 변환해서 벡터 DB에 저장한다.
벡터 DB는 이 벡터들을 효율적으로 검색하는 데 특화된 데이터베이스다. 주요 옵션들:
- Pinecone — 관리형 서비스, 설정이 간편함
- Weaviate — 하이브리드 검색(키워드+벡터) 지원
- Chroma — 로컬 개발에 좋은 경량 옵션
- pgvector — PostgreSQL 확장, 기존 DB 인프라 활용 가능
3. 검색과 생성
사용자 질문이 들어오면:
- 질문도 같은 임베딩 모델로 벡터로 변환한다
- 벡터 DB에서 가장 유사한 청크 k개를 가져온다
- 가져온 청크들을 프롬프트에 "컨텍스트"로 넣는다
- LLM이 컨텍스트를 참고해서 답변을 생성한다
[시스템 프롬프트]
아래 컨텍스트를 기반으로 질문에 답하세요.
컨텍스트에 없는 내용은 "모르겠습니다"라고 답하세요.
[컨텍스트]
{검색된 청크 1}
{검색된 청크 2}
{검색된 청크 3}
[질문]
{사용자 질문}
이게 RAG의 전체 흐름이다.
실제로 만들면 신경 쓸 것들
개념은 간단한데 잘 동작하게 만드는 건 또 다른 문제다.
청크 전략이 답변 품질을 좌우한다. 같은 문서라도 어떻게 자르느냐에 따라 검색 결과가 크게 달라진다. 단순히 글자 수로 자르는 것보다 문단이나 섹션 단위로 자르는 게 낫고, 제목이나 메타데이터를 청크에 포함시키면 검색 정확도가 올라간다.
하이브리드 검색을 고려하자. 벡터 검색(의미 기반)만으로는 정확한 키워드나 고유명사를 찾는 데 약하다. BM25 같은 키워드 검색을 병행하면 보완된다. 실무에서는 둘을 함께 쓰고 결과를 합치는 방식(Reciprocal Rank Fusion 등)이 흔하다.
리랭킹(re-ranking)으로 정밀도를 높인다. 1차로 넉넉하게 후보를 뽑고(예: 20개), 리랭킹 모델로 다시 정렬해서 상위 몇 개만 컨텍스트에 넣는 방식. Cohere Rerank나 bge-reranker 같은 모델이 여기에 쓰인다.
평가가 어렵다. "답변이 잘 나오는가"를 체계적으로 측정하기 까다롭다. RAGAS 같은 프레임워크가 있긴 한데, 결국 도메인 전문가가 직접 확인하는 과정이 필요하다.
시작하려면
가장 빠르게 RAG를 경험해보려면 LangChain + Chroma 조합이 좋다. Python 기준으로 파일 몇 개면 동작하는 프로토타입을 만들 수 있다.
# 개념적인 흐름 (의사코드)
documents = load_documents("./docs")
chunks = split_into_chunks(documents, chunk_size=500)
embeddings = embed(chunks, model="text-embedding-3-small")
vector_store = store_in_chroma(embeddings)
# 질문 시
query = "분기별 매출 추이는?"
relevant_chunks = vector_store.search(query, k=3)
answer = llm.generate(context=relevant_chunks, question=query)
프로덕션 수준으로 가려면 청크 전략 실험, 임베딩 모델 비교, 리랭킹 도입, 평가 파이프라인 구축까지 해야 한다. 하지만 일단 돌아가는 프로토타입을 만들어보는 게 가장 중요하다. 그래야 어디서 병목이 생기는지, 어떤 부분을 개선해야 하는지 감이 잡힌다.
RAG는 LLM 애플리케이션의 기본 패턴이 됐다. 사내 검색 시스템, 고객 지원 챗봇, 문서 기반 QA 등 어디든 쓰인다. 기본 개념을 확실히 잡아두면 응용은 자연스럽게 따라온다.