home

[사이오닉 Spring AI 시리즈 2] 나만의 RAG 챗봇 만들기

Category
8 more properties
LLM은 아무리 뛰어난 모델이라도 학습한 지식 안에서만 답변할 수 있습니다. 특정 기업의 사내 문서나 최신 법률처럼 외부 도메인 지식이 필요한 경우에는, 전혀 엉뚱한 답을 하거나 틀린 정보(Hallucination)를 말하기도 하죠.
이런 문제를 해결하기 위해 등장한 개념이 RAG(Retrieval-Augmented Generation)입니다. RAG는 외부 데이터를 검색해 LLM에게 맥락(Context)으로 제공함으로써, 보다 정확하고 신뢰할 수 있는 답변을 생성하도록 돕습니다.
그리고 이 복잡한 RAG 파이프라인을 Spring 기반으로 깔끔하게 구현할 수 있게 도와주는 프레임워크가 바로 Spring AI입니다. 이번 글에서는 Spring AI를 활용해 나만의 RAG 챗봇을 만드는 전체 구조와 컴포넌트 설계 방법을 소개합니다.

RAG 파이프라인 구성

Spring AI로 구성할 수 있는 대표적인 RAG 흐름은 다음과 같습니다.

1단계: 데이터 색인 (Data Indexing)

1.
문서 청킹(Chunking)
PDF 또는 텍스트 문서 업로드
텍스트 추출 및 정제
LLM이 이해하기 좋은 단위로 청킹(Text Splitting)
2.
벡터화 및 저장
각 청크에 대해 임베딩 생성
메타데이터 추가
벡터 저장소에 저장

2단계: 질의응답 (Data Retrieval & Generation)

사용자의 질문을 벡터화
유사한 문서 청크 검색
검색된 문서를 LLM에게 컨텍스트로 전달
최종 응답 생성

Spring AI로 구현하면 어떤 점이 좋을까?

이러한 RAG 시스템을 직접 구현하려면 다음과 같은 구성 요소들을 하나하나 직접 작성해야 합니다.
텍스트 청킹 전략
임베딩 모델 연동 (OpenAI, HuggingFace 등)
벡터 저장소 연동 (PgVector, Qdrant 등)
메타데이터 기반 필터링
LLM 프롬프트 구성 및 요청 처리
하지만 Spring AI는 이 복잡한 요소들을 표준화된 컴포넌트로 추상화해줍니다.
덕분에 개발자는 LLM 서비스 구현 그 자체에 집중할 수 있고, 인프라 구현 부담은 크게 줄어듭니다.

RAG 핵심 컴포넌트

Spring AI에서 RAG 파이프라인을구성하는 핵심 컴포넌트는 다음과 같습니다.

EmbeddingModel – 텍스트를 벡터로 변환하는 인터페이스

LLM과 검색 시스템을 연결하는 첫 번째 단계는 임베딩입니다.
Spring AI는 EmbeddingModel 인터페이스를 통해 다양한 벤더의 임베딩 모델을 쉽게 연동할 수 있게 해줍니다.
public interface EmbeddingModel extends Model<EmbeddingRequest, EmbeddingResponse> { EmbeddingResponse call(EmbeddingRequest request); default float[] embed(String text) float[] embed(Document document); default List<float[]> embed(List<String> texts) default EmbeddingResponse embedForResponse(List<String> texts) default int dimensions() }
Java
복사

VectorStore – 문서 벡터 저장 및 검색을 위한 인터페이스

벡터 저장소는 임베딩된 문서를 저장하고, 질문과 유사한 문서를 빠르게 검색해주는 핵심 컴포넌트입니다.
public interface VectorStore extends DocumentWriter { default String getName() { ... }; void add(List<Document> documents); void delete(List<String> idList); void delete(Filter.Expression filterExpression); default void delete(String filterExpression) { ... }; List<Document> similaritySearch(String query); List<Document> similaritySearch(SearchRequest request); }
Java
복사
예를 들어, 처음에는 인메모리 기반의 SimpleVectorStore로 시작했다가
서비스 확장 시 PgVector, Pinecone, Milvus 등 외부 DB로 손쉽게 전환할 수 있습니다.
VectorStore prodStore = PgVectorStore.builder(jdbcTemplate, model) .dimensions(1536) .distanceType(PgDistanceType.COSINE_DISTANCE) .build();
Java
복사

TokenTextSplitter - 텍스트 분할 전략 내장

LLM은 긴 문서를 한 번에 처리하지 못하기 때문에, 문서를 적절한 길이로 나누는 작업이 필요합니다.
Spring AI는 이를 위해 TextSplitter를 기본으로 제공합니다.
TextSplitter splitter = new TokenTextSplitter() List<Document> chunks = splitter.split(document);
Java
복사

FilterExpression - 메타데이터 기반 검색 필터링

단순히 유사한 문서를 찾는 것만으로는 부족할 수 있습니다. Spring AI는 문서 메타데이터를 기반으로 조건 필터링을 지원합니다. SQL의 WHERE 절과 유사한 방식으로 필터링할 수 있습니다. 예를 들어 “2023년 이후 계약서”처럼 조건을 추가하여 더욱 정확한 검색이 가능합니다.
SearchRequest request = SearchRequest.builder() .query("계약 기간은 언제야?") .filterExpression("type == 'contract' AND year >= 2023") .build(); List<Document> results = vectorStore.similaritySearch(request);
Java
복사
SQL의 WHERE 절과 유사한 방식으로 필터링할 수 있습니다.

직접 구현 vs Spring AI

항목
직접 구현 시
Spring AI 사용 시
임베딩 연동
OpenAI API 직접 호출
EmbeddingModel 설정만
벡터 저장소
PgVector 연동 및 SQL 구현
VectorStore 추상화
텍스트 청크 처리
분할 로직 직접 작성
TextSplitter 제공
메타데이터 필터링
WHERE 절 직접 구성
filterExpression 지원
벤더 교체
코드 전체 변경
설정만 변경

실습을 위한 간단한 API 구성

아래는 실제 프로젝트에 적용할 수 있는 API 구성입니다.
POST /api/v1/rag/documents → 문서를 업로드하고 임베딩 후 저장 POST /api/v1/rag/query → 사용자의 질문을 받아 검색 및 답변 생성
Bash
복사

마무리

RAG는 신뢰도 높은 LLM 서비스를 만들기 위한 현실적인 대안이며, Spring AI는 이를 구현하는 데 필요한 대부분의 요소들을 이미 잘 추상화된 상태로 제공합니다.
복잡한 파이프라인을 직접 만들 필요 없이, Spring의 익숙한 개발 패턴 안에서 강력한 RAG 시스템을 빠르게 구성할 수 있다는 점은 매우 큰 장점입니다.
다음 실습에서는 이론에서 소개한 컴포넌트를 실제 코드로 구성하며 실전에서 바로 활용할 수 있는 챗봇을 만들어보겠습니다.