logo
aillmrag

RAG 제대로 이해하기 - LLM에 '내 지식'을 붙이는 법

LLM은 똑똑하지만 두 가지 약점이 있습니다. 모르는 걸 아는 척(할루시네이션) 하고, 학습 시점 이후의 최신 정보나 우리 회사 내부 문서를 모른다는 점입니다. 이 둘을 가장 현실적으로 해결하는 방법이 RAG(Retrieval-Augmented Generation, 검색 증강 생성) 입니다.

이 글에서는 RAG가 무엇이고, 어떤 흐름으로 동작하며, 실무에서 어디를 조심해야 하는지를 정리합니다.


1. RAG는 한 문장으로

"질문과 관련 있는 문서를 먼저 찾아서, 그 내용을 프롬프트에 같이 넣어주고 답하게 하는 것."

모델을 다시 학습시키지 않습니다. 대신 답변 직전에 필요한 지식을 컨텍스트로 주입합니다. 비유하자면, 시험을 앞두고 머리에 다 외우게 하는 게 아니라 오픈북 시험처럼 관련 페이지를 펼쳐서 보여주는 것입니다.


2. 왜 파인튜닝이 아니라 RAG인가

"내 데이터를 넣고 싶으면 파인튜닝하면 되지 않나요?" 라는 질문을 많이 합니다. 셋을 비교하면 선택이 쉬워집니다.

방법하는 일강점약점
프롬프트요청할 때 예시·지침을 직접 넣음가장 쉬움, 즉시 반영넣을 수 있는 양 한계
RAG관련 문서를 찾아 컨텍스트로 주입최신성·출처 추적·비용 저렴검색 품질에 좌우됨
파인튜닝모델 가중치를 재학습말투·형식·도메인 체화비용·시간↑, 지식 갱신 어려움

핵심 감각:

  • "무엇을 아는가(지식)" 를 바꾸고 싶다 → RAG
  • "어떻게 말하는가(스타일·형식)" 를 바꾸고 싶다 → 파인튜닝
  • 지식은 계속 바뀌는데 파인튜닝으로 넣으면, 문서 하나 바뀔 때마다 재학습해야 합니다. 그래서 변하는 지식은 RAG가 정답입니다.

3. RAG 파이프라인 전체 흐름

RAG는 크게 ① 색인(Indexing)② 질의(Query) 두 단계로 나뉩니다.

[색인 - 미리 준비] 문서 → 청킹(Chunking) → 임베딩(Embedding) → 벡터 DB 저장 [질의 - 사용자가 물어볼 때] 질문 → 임베딩 → 벡터 DB에서 유사 청크 검색 → 프롬프트에 주입 → LLM 답변

하나씩 뜯어봅니다.

3-1. 청킹(Chunking) — 문서를 잘게 나누기

문서를 통째로 넣을 수 없으니 의미 단위로 자릅니다.

  • 너무 크게 자르면: 관련 없는 내용까지 딸려와 노이즈가 됩니다.
  • 너무 작게 자르면: 문맥이 끊겨 검색해도 의미가 안 통합니다.
  • 보통 300~800 토큰 + 약간의 오버랩(겹침) 으로 시작해 튜닝합니다.
// 아주 단순화한 청킹 예시 function chunk(text: string, size = 500, overlap = 50): string[] { const chunks: string[] = []; for (let i = 0; i < text.length; i += size - overlap) { chunks.push(text.slice(i, i + size)); } return chunks; }

실무에선 문단·헤딩 경계를 존중하는 "구조 기반 청킹"이 성능이 훨씬 좋습니다.

3-2. 임베딩(Embedding) — 의미를 벡터로

임베딩은 텍스트를 의미가 담긴 숫자 벡터로 바꾸는 것입니다. 의미가 비슷한 문장은 벡터 공간에서 가까이 위치합니다. 그래서 "결제 취소하는 법"과 "환불 요청 방법"이 단어는 달라도 가깝게 잡힙니다.

// 문장 → 벡터 (예: 1536차원) const vector = await embed("결제를 취소하고 싶어요"); // => [0.021, -0.135, 0.008, ... ]

3-3. 벡터 DB & 유사도 검색

청크 벡터들을 벡터 DB(pgvector, Pinecone, Qdrant 등)에 저장해두고, 질문 벡터와 코사인 유사도가 높은 상위 K개를 꺼냅니다.

-- pgvector 예시: 질문 임베딩과 가장 가까운 청크 5개 SELECT content, 1 - (embedding <=> :query_embedding) AS score FROM doc_chunks ORDER BY embedding <=> :query_embedding LIMIT 5;

3-4. 프롬프트 주입 & 생성

찾은 청크를 프롬프트에 붙여 "이 내용을 근거로만 답하라"고 지시합니다.

아래 문서를 참고해서 질문에 답하세요. 문서에 근거가 없으면 "모르겠습니다"라고 답하세요. [문서] {검색된 청크들} [질문] {사용자 질문}

이 "문서에 없으면 모른다고 하라"는 지침 하나가 할루시네이션을 크게 줄이는 실전 팁입니다.


4. 검색 품질을 끌어올리는 실전 기법

RAG의 성능은 결국 "관련 문서를 얼마나 잘 찾느냐" 에 달려 있습니다. 답이 이상하면 대부분 생성이 아니라 검색이 문제입니다.

  • 하이브리드 검색: 의미 검색(벡터) + 키워드 검색(BM25)을 합치면, 고유명사·코드·에러메시지 같은 정확 매칭에 강해집니다.
  • 리랭킹(Re-ranking): 후보를 넉넉히(예: 20개) 뽑은 뒤, 리랭커 모델로 다시 정렬해 상위 3~5개만 넣습니다.
  • 메타데이터 필터: 최신 문서만, 특정 제품군만 등 조건으로 검색 범위를 좁힙니다.
  • 쿼리 재작성: 사용자의 짧은 질문을 검색용으로 풍부하게 다시 씁니다.

5. "롱 컨텍스트가 나오면 RAG는 필요 없다?"

요즘 모델은 수십만 토큰까지 넣을 수 있어서 "문서 다 넣으면 되지 않냐"는 얘기가 나옵니다. 하지만 여전히 RAG가 유리한 이유:

  • 비용·속도: 매 요청마다 수십만 토큰을 넣으면 느리고 비쌉니다. 필요한 부분만 넣는 게 효율적입니다.
  • 정확도: 컨텍스트가 길수록 중간 내용을 놓치는 lost-in-the-middle 현상이 있습니다.
  • 출처 추적: RAG는 "이 답의 근거는 이 문서"라고 인용을 붙일 수 있습니다.

즉 롱 컨텍스트는 RAG를 대체한다기보다, RAG가 더 넉넉하게 근거를 넣을 수 있게 보완합니다.


6. 실무에서 자주 밟는 지뢰

  • 청크가 나쁘면 다 나쁩니다. 검색이 안 맞으면 청킹부터 다시 보세요.
  • 평가를 안 한다. "질문–정답–근거" 세트를 만들어 검색 적중률(recall)과 답변 품질을 수치로 관리해야 개선이 됩니다.
  • 임베딩 모델 교체 = 전체 재색인. 임베딩 모델을 바꾸면 저장된 벡터도 전부 다시 만들어야 합니다.
  • 권한 처리. 사내 RAG는 "이 사용자가 볼 수 있는 문서"만 검색되도록 필터가 필수입니다.

마무리

RAG는 거창한 기술이 아니라 "관련 문서를 잘 찾아서 프롬프트에 넣는다" 는 단순한 아이디어입니다. 하지만 실제 품질은 청킹 → 임베딩 → 검색 → 리랭킹 → 주입 각 단계의 디테일에서 갈립니다.

처음 시작한다면 이 순서를 추천합니다.

  1. 문서를 문단 단위로 청킹하고 pgvector에 넣어 가장 단순한 RAG를 만든다.
  2. 실제 질문 세트로 검색 적중률부터 측정한다.
  3. 하이브리드 검색 → 리랭킹 순으로 하나씩 붙이며 수치를 확인한다.

"모델을 더 크게" 가 아니라 "검색을 더 정확하게" 가 RAG를 잘하는 길입니다.

me

강경원

Backend Developer

성능과 안정성으로 신뢰받는 백엔드 개발자 · 요즘은 AI/LLM을 서비스에 접목합니다

이력서 보기