본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 18. 04:15

LangChain, pgvector, Gemini를 활용한 프로덕션 준비 완료된 RAG 애플리케이션 구축

요약

LangChain, pgvector, Gemini를 사용하여 프로덕션 수준의 RAG 애플리케이션을 구축하는 단계별 가이드입니다. 문서 인제스션부터 벡터 DB 저장, 쿼리 및 생성 단계까지의 전체 아키텍처와 구현 방법을 상세히 다룹니다.

핵심 포인트

  • LangChain을 활용한 RAG 파이프라인 설계 및 구현
  • pgvector를 이용한 PostgreSQL 기반 벡터 데이터베이스 구축
  • Gemini 모델을 활용한 컨텍스트 기반 응답 생성 및 인용 기능
  • 문서 청킹, 메타데이터 강화 및 임베딩 프로세스 최적화

검색 증강 생성 (Retrieval-Augmented Generation, RAG)은 커스텀 문서(PDF, 이력서, 보고서 등)를 대규모 언어 모델 (Large Language Models, LLMs)에 컨텍스트로 제공함으로써, 해당 문서를 쿼리하고 이해하며 통찰력을 추출할 수 있는 애플리케이션을 구축하는 강력한 패턴입니다.

이 가이드는 아키텍처, 코드, 그리고 디버깅 과정에서 얻은 학습 내용을 설명하며 단계별로 완전한 RAG API를 구축하는 과정을 안내합니다.

1. 아키텍처 개요 (Architecture Overview)

전형적인 RAG 파이프라인은 두 부분으로 나뉩니다:

A. 인제스션 단계 (Ingestion Phase, 쓰기 경로)

  1. 문서 로드 (Load Document): PDF 파일에서 텍스트를 읽고 파싱합니다.
  2. 텍스트 정제 (Sanitize Text): 유효하지 않은 데이터베이스 문자(null 바이트 등)를 필터링합니다.
  3. 청킹 (Chunking): 큰 텍스트 페이지를 작고 중첩되는 청크(단락)로 나눕니다.
  4. 컨텍스트 강화 (Context Enrichment): 임베딩 모델이 모든 단락과 주요 컨텍스트를 연관시킬 수 있도록 각 청크 앞에 메타데이터(주제/지원자 이름 등)를 추가합니다.
  5. 벡터 임베딩 (Vector Embedding): 텍스트 청크를 수치 벡터(의미적 의미를 나타내는 좌표)로 변환합니다.
  6. 벡터 DB 저장 (Vector DB Storage): pgvector 확장을 사용하여 PostgreSQL에 텍스트 청크와 해당 임베딩을 저장합니다.

B. 쿼리/채팅 단계 (Query/Chat Phase, 읽기 경로)

  1. 입력 (Input): 사용자가 REST API를 통해 질문을 보냅니다.
  2. 임베딩 (Embedding): 동일한 모델을 사용하여 쿼리를 임베딩으로 변환합니다.
  3. 유사도 검색 (Similarity Search): 벡터 거리를 기반으로 벡터 데이터베이스에서 가장 유사한 상위 k개의 텍스트 청크를 검색합니다.
  4. 컨텍스트 증강 (Context Augmentation): 검색된 청크를 엄격한 지침 기반 프롬프트 템플릿에 입력합니다.
  5. LLM 생성 (LLM Generation): 모델(Gemini 2.5 Flash)에게 제공된 컨텍스트에만 의존하여 응답을 생성하도록 요청하고, 답변과 함께 인용을 반환합니다.

2. 프로젝트 설정 및 구성 (Project Setup & Configuration)

파일: requirements.txt

의존성에는 FastAPI (API 프레임워크), LangChain (오케스트레이션 라이브러리), Google GenAI 통합, 그리고 PostgreSQL/pgvector를 위한 데이터베이스 드라이버가 포함됩니다.

fastapi
uvicorn
python-dotenv
...

파일: .env (환경 변수)

데이터베이스 자격 증명(credentials)과 Google AI Studio API 키를 저장합니다.

DATABASE_URL=postgresql://postgres:postgres@localhost:5432/ragdb
GOOGLE_API_KEY=YOUR_GEMINI_API_KEY

3. 코드 상세 분석 (Code Walkthrough)

1. 설정 및 데이터베이스 연결

app/config.py

모듈 전반에서 접근할 수 있도록 .env로부터 변수를 로드합니다.

from dotenv import load_dotenv
import os

...

app/database.py

PostgreSQL에 연결하기 위한 SQLAlchemy 엔진 인스턴스를 설정합니다.

from sqlalchemy import create_engine
from dotenv import load_dotenv
import os
...

app/vector_store.py

임베딩 모델 (models/gemini-embedding-2)을 인스턴스화하고, PGVector를 통해 PostgreSQL에 연결하여 임베딩을 인덱싱하고 검색합니다.

from langchain_google_genai import GoogleGenerativeAIEmbeddings
from langchain_postgres import PGVector
from config import DATABASE_URL
...

2. 문서 인제스션 (Document Ingestion)

app/ingest.py

이 스크립트는 PDF를 읽고, 텍스트를 정제(sanitize)하며, 청크(chunk)로 나누고, 메타데이터로 청크를 풍부하게 만든(enrich) 후 벡터를 데이터베이스에 저장합니다.

[!NOTE]
PostgreSQL NUL 제약 조건: 표준 Python PDF 로더는 특수 서식을 \x00 (NUL 문자)로 파싱할 수 있습니다. PostgreSQL은 C 스타일의 null-terminated 문자열을 사용하므로, 가공되지 않은 \x00을 쓰려고 시도하면 쓰기 오류가 발생합니다. 따라서 청킹(chunking) 전에 이를 명시적으로 제거합니다.

컨텍스트 풍부화 (Context Enrichment): 청킹 과정에서 문서가 분할되면, 페이지 중간의 텍스트는 컨텍스트(예: 지원자의 이름)가 부족할 수 있습니다. 모든 청크의 앞에 "Candidate: {title}"를 추가함으로써, 대상 이름이 포함된 검색 쿼리가 이러한 청크들을 정확하게 순위화할 수 있도록 보장합니다.

from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from vector_store import vector_store
...

3. 검색 및 응답 생성 (Retrieval and Response Generation)

app/chat.py

3. 검색 및 응답 생성 (Retrieval and Response Generation)

app/chat.py

데이터베이스에서 일치하는 청크를 조회하고, 프롬프트 컨텍스트를 구성한 다음, 이를 LLM (gemini-2.5-flash)에 입력하고 소스 페이지 메타데이터를 컴파일합니다.

from langchain_google_genai import ChatGoogleGenerativeAI
from vector_store import vector_store

...

4. API 노출 (Exposing the API)

app/main.py

FastAPI 서버를 호스팅합니다. 프로젝트 루트 디렉토리에서 실행할 경우, 임포트를 깔끔하게 해결하기 위해 현재 디렉토리 경로를 동적으로 추가합니다.

import sys
import os
# Ensure the root directory imports resolve correctly
...

4. 주요 학습 내용 및 주의 사항 (Key Learnings & Gotchas)

  1. 임베딩 및 모델 할당량 설정:
    • 항상 API 키의 사용 가능한 모델을 먼저 조회해야 합니다 (client.models.list()).
    • gemini-2.5-pro와 같은 프리미엄 모델을 유료가 아닌 등급에서 사용하면 429 RESOURCE_EXHAUSTED (할당량 제한 0) 오류가 발생할 수 있습니다. 비용 효율적이고 할당량이 높은 대안으로 gemini-2.5-flash를 사용하는 것이 좋습니다.
  2. PostgreSQL NUL 바이트 제한:
    • PDF 표준 글꼴 번역은 자주 마커를 출력합니다. 이러한 원시 문자열을 데이터베이스에 쓸 때 PostgreSQL에서 실패할 수 있습니다. 간단한 .replace('�', '') 필터를 구현하는 것이 필수입니다.
  3. **컨텍스트 누수 (LLM이

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0