1. 도입부 (Introduction)
챗GPT(ChatGPT)를 시작으로 대형 언어 모델(LLM)의 시대가 열렸지만, 현업에서 이를 활용해 비즈니스 애플리케이션을 만들다 보면 치명적인 한계에 부딪히게 된다. 대표적인 것이 바로 모델이 학습하지 않은 최신 정보나 기업 내부 기밀 데이터에 대해 묻는 경우다. LLM은 모르는 내용에 대해서도 그럴싸한 거짓말을 지어내 답변하곤 하는데, 이를 '환각 현상(Hallucination)'이라고 부른다.
이 문제를 해결하기 위해 고안된 기술이 바로 RAG(Retrieval-Augmented Generation, 검색 증강 생성)다. RAG는 LLM의 두뇌를 새로 학습시키는(Fine-tuning) 대신, 질문과 관련된 외부 문서(데이터베이스)를 먼저 찾아서 이를 질문과 함께 LLM에 참고 자료로 넘겨주는 방식이다.
이러한 RAG 기반의 LLM 애플리케이션을 더 쉽고 모듈화된 구조로 빠르게 개발할 수 있도록 도와주는 오픈소스 프레임워크가 바로 랭체인(LangChain)이다. 이번 글에서는 RAG의 기본 개념과 랭체인의 핵심 컴포넌트를 살펴보고, 실전 예제 코드를 바탕으로 동작 가능한 Simple RAG 파이프라인과 대화형 웹 인터페이스까지 완벽하게 파헤쳐 본다.
2. RAG의 기본 개념과 랭체인 아키텍처 (Main Features & Logic)

RAG의 핵심은 "검색(Retrieval)하고, 강화(Augmentation)하고, 생성(Generation)한다"는 3단계 프로세스에 있다. 사용자가 질문을 던지면 시스템은 지식베이스(문서 묶음)에서 질문과 가장 관련이 높은 문서를 먼저 검색한다. 이후 원래 질문에 검색된 문서 내용을 합쳐(강화) LLM의 입력값(프롬프트)으로 제공하며, LLM은 이 완벽한 가이드를 바탕으로 정확한 답변을 완성(생성)한다.
랭체인(LangChain) 프레임워크가 해결해 주는 문제
RAG 시스템을 직접 밑바닥부터 구현하려면 다양한 포맷의 문서 파일(PDF, TXT, HTML 등)을 파싱하고, 문장을 적절한 크기로 쪼개고, 이를 벡터(Vector) 형태로 변환하여 데이터베이스에 저장한 뒤, 유사도 검색 알고리즘을 구현하는 등 매우 번거로운 단계를 거쳐야 한다.
랭체인은 이러한 일련의 과정을 표준화된 모듈로 엮어서 제공한다. 랭체인을 사용하면 다음과 같은 시스템적 이점을 얻을 수 있다.
- 뛰어난 모듈성: 데이터 로더, 텍스트 분할기, 임베딩 모델, 벡터 DB, LLM 등 각 컴포넌트를 손쉽게 교체할 수 있다.
- 다양한 연동성: OpenAI 외에도 Anthropic, HuggingFace, 로컬 LLM(Ollama) 등 수많은 모델 아키텍처와 통합이 가능하다.
- 효율적인 체이닝(Chaining): 여러 단계를 거쳐 흐르는 데이터를 유기적으로 엮어 하나의 일관된 파이프라인(Chain)으로 제어할 수 있다.
3. 랭체인 핵심 컴포넌트 심층 분석 (Detailed Components)
RAG 파이프라인의 실전 코드를 살펴보기 전에, 데이터가 흘러가는 통로를 구성하는 랭체인의 6가지 핵심 컴포넌트를 정확하게 정리해야 한다.
| 컴포넌트 명칭 | 주요 기능 및 역할 | 실무에서의 핵심 포인트 |
| Document Loaders | PDF, Web Page, Notion 등 다양한 소스로부터 원시 데이터를 읽어 들여 랭체인 표준 문서 객체로 변환한다. | 파일 포맷에 최적화된 로더 선택이 중요하며, 텍스트뿐만 아니라 메타데이터(파일명, 페이지 번호 등)도 함께 보존해야 한다. |
| Text Splitters | 너무 긴 문서를 LLM의 컨텍스트 윈도우 한계에 맞춰 적절한 크기(Chunk)로 분할한다. | 문맥이 끊어지는 것을 방지하기 위해 각 Chunk 사이에 일정 부분 겹치는 영역(Overlap)을 지정하는 것이 일반적이다. |
| Embeddings | 분할된 텍스트 청크를 컴퓨터가 이해할 수 있는 고차원 수학적 벡터(실수 배열)로 변환한다. | 텍스트의 의미론적 유사성을 파악하는 기법으로, 질문과 문서 사이의 거리를 계산하는 수학적 토대가 된다. |
| Vector Stores | 생성된 임베딩 벡터들을 효율적으로 영구 저장하고, 빠르게 검색하기 위한 특화된 데이터베이스다. | Chroma, FAISS, Pinecone 등이 주로 쓰이며 대규모 데이터를 빠르게 탐색할 수 있도록 고안되어 있다. |
| Retrievers | 사용자의 질문(Query)을 벡터화하여 Vector Store에서 가장 유사도가 높은 관련 문서 청크들을 뽑아낸다. | 단순히 벡터 거리를 계산하는 것 외에도 하이브리드 검색, 재정렬(Reranking) 기술을 통해 검색 성능을 높일 수 있다. |
| LLMs / Chat Models | 최종적으로 취합된 컨텍스트와 사용자의 질문을 결합하여 가독성 높고 정교한 자연어로 응답을 작성한다. | 온도를 뜻하는 temperature 설정을 통해 답변의 창의성(높음) 또는 사실 지향성(0에 가까움)을 제어한다. |
4. Simple RAG 파이프라인 구현 (Step-by-step Guide with Code)
이제 랭체인 공식 라이브러리와 OpenAI API를 활용하여 실제 작동하는 간단한 RAG 파이프라인을 구축해 본다. 전체적인 실습 환경 설정과 패키지 임포트 단계부터 차근차근 전개해 나간다.

STEP 1. 환경 설정 및 원본 문서 준비
가장 먼저 .env 파일 등을 활용하여 OpenAI API 키를 로드하고 필요한 패키지들을 불러온다.
import os
from dotenv import load_dotenv
# API 키가 담겨있는 .env 환경변수를 로드한다.
load_dotenv()
STEP 2. 문서 로드 및 텍스트 분할 (Chunking)
가상의 혹은 실제 PDF 문서 파일(section1.pdf)을 읽어 들이고, 효율적인 검색을 위해 1,000자 단위로 텍스트를 쪼갠다. 이대 각 청크 사이의 흐름이 끊기지 않도록 200자 정도를 겹치도록(overlap) 설정한다.
from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
# 1. PDF 문서를 로드한다.
loader = PyPDFLoader("section1.pdf")
raw_documents = loader.load()
# 2. 문서를 효율적으로 분할하는 Splitter를 정의한다.
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000, # 한 청크당 최대 글자 수
chunk_overlap=200 # 인접한 청크 간에 겹치는 글자 수
)
# 3. 문서를 분할하여 리스트 형태로 만든다.
documents = text_splitter.split_documents(raw_documents)
print(f"분할된 전체 문서 청크 수: {len(documents)}")
STEP 3. 벡터 데이터베이스 및 임베딩 모델 구축
OpenAI의 공식 임베딩 API를 활용해 분할된 텍스트들을 수학적 벡터로 바꾸고, 이를 오픈소스 인메모리 벡터 데이터베이스인 Chroma에 적재한다.
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
# 1. 임베딩용 OpenAI 모델을 선언한다.
embeddings = OpenAIEmbeddings()
# 2. Chroma DB를 구축하고 문서들을 기하학적 공간에 적재시킨다.
vectorstore = Chroma.from_documents(
documents=documents,
embedding=embeddings
)
print(f"벡터 데이터베이스에 저장된 문서 수: {vectorstore._collection.count()}")
STEP 4. 검색기(Retriever) 및 체인(Chain) 설계
이제 사용자의 질문에 답변하기 위해 문서를 뽑아주는 검색기(Retriever)를 선언하고, 검색된 문서를 LLM에 한데 묶어서 주입하는 create_stuff_documents_chain 및 전체 검색 흐름을 관장하는 create_retrieval_chain을 구현한다.
from langchain_openai import ChatOpenAI
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts import ChatPromptTemplate
from langchain.chains import create_retrieval_chain
# 1. 검색기(Retriever) 생성 (가장 유사도가 높은 상위 2개의 문서만 가져온다)
retriever = vectorstore.as_retriever(search_kwargs={"k": 2})
# 2. LLM 선언 (정확하고 사실에 입각한 답변을 위해 temperature는 0으로 지정)
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
# 3. LLM에 입력할 프롬프트 템플릿(System Prompt 포함) 정의
system_prompt = (
"주어진 컨텍스트(Context) 정보만을 엄격하게 참고하여 질문에 답해주세요.\n"
"만약 답변을 찾을 수 없거나 모르는 내용이라면 지어내지 말고 '주어진 문서에서 정보를 찾을 수 없습니다.'라고 답변하세요.\n\n"
"Context:\n{context}"
)
prompt = ChatPromptTemplate.from_messages([
("system", system_prompt),
("human", "{input}"),
])
# 4. 검색된 문서를 컨텍스트로 묶어주는 체인 생성
question_answer_chain = create_stuff_documents_chain(llm, prompt)
# 5. 최종 검색 증강 생성(RAG) 파이프라인 체인을 완성한다.
rag_chain = create_retrieval_chain(retriever, question_answer_chain)
STEP 5. 질의응답 실행
작성된 RAG 파이프라인 체인을 실행하여 외부 지식이 정확하게 반영되는지 직접 검증한다.
# 문서 내용에 기반한 질문을 정의한다.
query = "RAG의 기본 개념과 LangChain 프레임워크의 장점은 무엇인가요?"
# 체인을 실행하여 답변을 도출한다.
response = rag_chain.invoke({"input": query})
# 최종 결과 출력
print("=== 질문 ===")
print(query)
print("\n=== 생성된 답변 ===")
print(response["answer"])
STEP 6. 대화형 인터페이스(UI) 연동
실무에서는 터미널이 아니라 사용자가 편하게 웹 환경에서 대화할 수 있는 데모 페이지가 필요하다. 파이썬 경량 UI 라이브러리인 Gradio를 얹어서 간단하게 챗봇 웹앱을 열어본다.
import gradio as gr
# Gradio 인터페이스와 연동되는 답변 호출용 헬퍼 함수
def answer_invoke(message, history):
response = rag_chain.invoke({"input": message})
return response["answer"]
# Gradio ChatInterface 생성 및 실행
demo = gr.ChatInterface(
fn=answer_invoke,
title="📚 문서 기반 전문 QA 에이전트 봇",
description="업로드된 문서를 바탕으로 가장 정확하고 신뢰할 수 있는 사실만 답변합니다."
)
# 웹 서버를 실행하여 로컬 포트(7860 등)로 접속한다.
demo.launch(share=True)
5. 실전 팁 및 예외 상황 대응 (Practical Tips & Exception Handling)
실무에서 대규모 문서를 사용해 RAG 시스템을 프로덕션 수준으로 운영하려면 단순히 API를 호출하는 것을 넘어 발생할 수 있는 잠재적인 한계점들을 예방해야 한다.
1. 청크 크기(Chunk Size)와 겹침(Overlap)의 정교한 튜닝
- 문제점: 청크 크기가 너무 작으면 연관 문맥이 조각나서 검색 성능이 떨어지고, 반대로 너무 크면 불필요한 노이즈 텍스트가 많이 포함되어 비용이 증가하고 모델이 집중하지 못한다.
- 해결책: 일반적인 텍스트 문서는 chunk_size=1000에 overlap=200이 최적의 기준점이지만, 소스코드가 포함된 기술 문서나 수치 데이터가 많은 보고서 등은 구조가 완전히 깨지지 않도록 구분자(separator)를 \n\n이나 테이블 정규식 등으로 정교하게 제어해야 한다.
2. 검색 정합성 향상 기술 (Query Transformation & Re-ranking)
- 문제점: 사용자가 질문을 모호하게 입력하면 벡터 데이터베이스에서 의미가 전혀 엉뚱한 문서를 뽑아올 위험이 크다.
- 해결책: 사용자의 단순 질문을 검색에 최적화된 구체적인 문장들로 다시 풀어서 쓰게 하거나(Query Expansion), 1차로 벡터 데이터베이스에서 러프하게 많은 수(예: k=20)의 문서를 검색한 뒤에 고성능 검색 랭커(예: Cohere Re-ranker)를 이용해 가장 밀접한 문서 3~4개만 정밀하게 필터링하여 LLM에 최종 전달하는 '다단계 재정렬' 파이프라인 설계를 고려해야 한다.
3. 완전한 침묵 유도 (No Hallucination)
- 문제점: 훈련 데이터의 사소한 공통점이나 LLM의 본래 성향 때문에, 검색된 문서에 없는 지식을 마음대로 혼합하여 창조해 내는 경우가 여전히 생길 수 있다.
- 해결책: 프롬프트 템플릿 레벨에서 강력한 가이드라인을 작성해야 한다. 예외 시나리오에 대해 *"검색된 컨텍스트 문서에 정확한 팩트가 부재하다면, 어설픈 추론은 절대 하지 말고 '죄송합니다. 제공해주신 사내 규정에서 관련 조항을 찾을 수 없습니다.'라고 답변할 것"*과 같이 '침묵 정책(Fail-fast policy)'을 단호하게 명문화하는 것이 안전하다.
6. 결론 및 자가 진단 Q&A (Conclusion & Q&A)
랭체인은 복잡한 대형 언어 모델과 다양하게 얽혀 있는 외부 정형/비정형 문서 데이터베이스를 아주 우아하게 연결해 주는 연결 통로이자 고속도로와 같다. 단순히 대화형 비서 역할을 하는 기존의 인공지능을 넘어, 기업 내부의 실제 지식베이스를 유기적으로 주입하는 RAG(검색 증강 생성) 아키텍처를 만났을 때 비로소 인공지능은 실무에 실질적인 가치를 창출하는 핵심 비즈니스 도구로 거듭나게 된다.
이번 기회에 랭체인 핵심 컴포넌트를 머릿속에 완벽하게 조감하고, 실습으로 짜놓은 파이프라인 코드를 로컬 환경에서 직접 다양한 문서로 변환하여 실행해 보는 것을 추천한다.
❓ 자가 진단 복습 질문
Q1. RAG(검색 증강 생성)가 미세 조정(Fine-tuning)과 비교했을 때 가지는 결정적인 차별점과 장점은 무엇인가?
- A1: 파인 튜닝은 모델 자체의 매개변수(Weight)를 직접 업데이트하여 도메인 지식을 체득하게 만드는 방식이다. 따라서 고비용의 GPU 인프라와 장시간의 학습 시간이 소모되며, 정보가 주기적으로 바뀔 때마다 매번 다시 학습시켜야 하는 한계가 존재한다. 반면 RAG는 모델 가중치는 그대로 보존하고, 데이터베이스만 실시간 업데이트한 채 관련 문서를 참고용 컨텍스트로 제공하므로 실시간 최신 정보 적용이 매우 용이하며, 구축 및 유지비용이 비교 불가능할 정도로 저렴하다.
Q2. 텍스트 분할(Text Splitter) 과정에서 각 청크 사이에 겹치는 구간(Overlap)을 의도적으로 부여하는 근본적인 이유는 무엇인가?
- A2: 문서를 임의의 글자 수(예: 1,000자)로 단순 단절시키면 핵심 비즈니스 로직이나 문맥, 혹은 인명과 고유 명사 단어가 정확히 반으로 잘려 청킹될 위험이 존재한다. 이 경우 전후 문맥을 잃어버려 검색 효율성과 임베딩 벡터의 의미 정보가 심각하게 왜곡될 수 있다. 일정 분량을 겹치게 중첩 영역으로 둠으로써 잘려 나가는 앞뒤 흐름의 파편들을 완벽히 상호 보완해 줄 수 있게 된다.
Q3. 벡터 데이터베이스(Vector Store)에 텍스트를 바로 저장하지 않고, 반드시 임베딩 모델(Embedding Model)을 먼저 거쳐야 하는 기하학적이고 기술적인 이유는 무엇인가?
- A3: 컴퓨터는 단순 텍스트("사과"와 "애플")를 직접 대조하면 스펠링이 달라 연관성을 인지하지 못한다. 임베딩 모델은 고차원 벡터 공간 속에 각 단어나 문장이 가진 추상적이고 고차원적인 의미론적 특징(Semantic Feature)을 정교한 실수 좌표로 변환해 준다. 따라서 좌표축 상의 기하학적 거리를 코사인 유사도 연산으로 빠르게 계산함으로써, 문자열 매칭의 한계를 넘어 의미적으로 통하는 핵심 문맥 정보를 즉시 조율하고 발췌할 수 있게 되기 때문이다.
'LLM > RAG' 카테고리의 다른 글
| [RAG] 랭체인(LangChain) 정리: 사전 최적화와 히스토리를 결합한 하이브리드 RAG 파이프라인 완벽 해부 (0) | 2026.06.06 |
|---|---|
| [RAG] 검색 성능 극대화 Dense(Chroma/FAISS)와 Sparse(Kiwi-BM25) 정리 (0) | 2026.06.01 |
| [RAG] 문서 Loader부터 다국어 검색 챗봇 정리 (0) | 2026.06.01 |
| [RAG] LCEL(랭체인 표현 언어)과 Runnable을 이용한 RAG 파이프라인 (0) | 2026.05.31 |