[Dev-Fortune] 시리즈 다시보기


1. 서론: ‘Dev-Fortune’ 프로젝트를 마무리하며

기획부터 시작해 로컬 Ollama 모델 탑재, RAG 기반 지식 주입, 페르소나 튜닝, 그리고 비동기 SSE 스트리밍까지 달려온 7부작 ‘Dev-Fortune(개발자 사주 챗봇)’ 시리즈가 대단원의 막을 내렸습니다.

이 프로젝트의 본질적인 목표는 단순히 재미있는 토이 프로젝트를 만드는 것에 그치지 않았습니다. **“파이썬(Python)이 독점하다시피 했던 생성형 AI(GenAI) 애플리케이션 개발 생태계를 순수 자바와 Spring Boot 엔지니어링 환경에서 얼마나 깔끔하고 견고하게 구현할 수 있는가?”**에 대한 검증이었습니다.

Spring AI의 성숙한 추상화 모델 덕분에 복잡한 RAG 파이프라인과 비동기 스트리밍을 단 몇 백 줄의 자바 코드로 깔끔하게 완성할 수 있었습니다. 하지만 동시에 프로토타입 수준의 로컬 인메모리 아키텍처가 가진 명확한 한계점들도 마주하게 되었습니다. 이번 완결편에서는 프로젝트 과정에서 발견한 한계점들을 솔직하게 짚어보고, 이를 상용 프로덕션 레벨로 격상시키기 위한 구체적인 기술 고도화 로드맵을 제시합니다.


2. 현재 아키텍처의 한계와 병목 분석 (AS-IS)

현재 구현된 프로토타입 시스템은 초기 검증용으로는 훌륭하지만, 실제 수천 명의 동시 접속자가 몰리는 서비스 환경에서는 다음과 같은 구조적 제약이 발생합니다.

1) SimpleVectorStore의 인메모리 휘발성

  • 문제점: 현재 사용 중인 SimpleVectorStore는 모든 임베딩 벡터를 JVM 힙 메모리에 저장합니다. 서버가 재시작될 때마다 수백 건의 사주 JSON 데이터를 다시 파싱하고 Ollama 임베딩 모델을 호출해 벡터를 재계산해야 하므로, 서버 부팅 시간이 수십 초 이상 지연됩니다.
  • 확장성 한계: 데이터가 수만, 수십만 건으로 늘어날 경우 JVM OOM(OutOfMemoryError)을 유발할 위험이 큽니다.

2) 무상태(Stateless) 단발성 대화

  • 문제점: 현재 엔드포인트는 매 요청마다 단일 질문과 답변만 처리합니다. “아까 말한 안티패턴을 해결하려면 어떻게 코드를 고쳐야 해?“와 같은 후속 멀티턴(Multi-turn) 대화에서 이전 맥락을 전혀 기억하지 못합니다.

3) 로컬 Ollama 단일 인스턴스의 동시성 병목

  • 문제점: 로컬 PC나 단일 서버의 Ollama 데몬은 GPU VRAM 크기에 따라 동시 요청 처리 개수(Batch Size)가 엄격하게 제한됩니다. 여러 사용자가 동시에 질문을 던지면 대기 큐(Request Queue)가 쌓이며 응답 지연(Latency Spike)이 기하급수적으로 증가합니다.

3. 프로덕션 TO-BE 아키텍처 로드맵

위의 문제점들을 극복하기 위해 설계한 차세대 엔터프라이즈 아키텍처의 비교도입니다.

flowchart TB subgraph "AS-IS (현재 프로토타입)" direction TB A1[클라이언트 요청] --> B1[Spring Boot App] B1 --> C1[(SimpleVectorStore)] B1 --> D1[Ollama: 단일 인스턴스] style C1 fill:#ffebee,stroke:#c62828 style D1 fill:#ffebee,stroke:#c62828 Note1[JVM 힙 종속 / 휘발성 / 단발 대화] end
subgraph "TO-BE (프로덕션 고도화)"
    direction TB
    A2[클라이언트 요청] --> LB[Nginx / Cloud Load Balancer]
    LB --> B2[Spring Boot Cluster #1, #2, #N]
    
    B2 --> C2[(PostgreSQL + pgvector)]
    B2 --> D2[(Redis: ChatMemory & Caching)]
    B2 --> E2[vLLM / Triton 추론 클러스터]
    
    style C2 fill:#e8f5e9,stroke:#2e7d32
    style D2 fill:#e8f5e9,stroke:#2e7d32
    style E2 fill:#e8f5e9,stroke:#2e7d32
    Note2[영구 벡터 인덱싱 / 멀티턴 세션 / 수평 확장]
end

4. 3대 핵심 고도화 상세 가이드

1) Vector Store 영구화: PostgreSQL + pgvector 도입

휘발성 메모리를 걷어내고, 엔터프라이즈 환경에서 검증된 PostgreSQL의 벡터 확장 기능인 pgvector로 전환합니다. HNSW(Hierarchical Navigable Small World) 인덱스를 적용하면 수백만 건의 벡터에서도 밀리초 단위의 초고속 검색이 가능합니다.

-- pgvector 확장 활성화 및 HNSW 인덱스 생성
CREATE EXTENSION IF NOT EXISTS vector;

CREATE TABLE saju_vector_store (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    content TEXT NOT NULL,
    metadata JSONB,
    embedding vector(1536)
);

-- 코사인 거리 기준 HNSW 고속 인덱스
CREATE INDEX idx_saju_embedding_hnsw 
ON saju_vector_store USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);

Spring AI에서는 PgVectorStore 빈을 선언하고 application.yml에 데이터소스만 지정하면 코드 수정 없이 즉시 교체할 수 있습니다.

spring:
  ai:
    vectorstore:
      pgvector:
        table-name: saju_vector_store
        index-type: HNSW
        distance-type: COSINE_DISTANCE
        dimensions: 1536

2) Redis 기반 ChatMemory를 통한 멀티턴(Multi-turn) 대화

사용자의 대화 세션을 기억하기 위해 Spring AI의 MessageChatMemoryAdvisor와 Redis를 결합합니다. 대화 내역의 슬라이딩 윈도우(Sliding Window)를 유지하여 토큰 비용 폭증을 방지합니다.

@Bean
public ChatClient chatClient(ChatClient.Builder builder, RedisConnectionFactory redisFactory) {
    ChatMemory chatMemory = new RedisChatMemory(redisFactory);
    
    return builder
            .defaultAdvisors(
                // 최근 10개의 대화 메시지만 프롬프트 컨텍스트에 자동 주입
                new MessageChatMemoryAdvisor(chatMemory, "default", 10)
            )
            .build();
}

3) 동적 멀티 페르소나 셀렉터 (Dynamic Persona Selector)

기존의 고정된 ‘시니컬한 시니어 개발자’ 단일 모드에서 벗어나, 사용자가 상담받고 싶은 멘토의 성향을 직접 고를 수 있는 멀티 페르소나 구조로 확장합니다:

  • 따뜻한 에인절 리드: “실수해도 괜찮아, 롤백하고 다시 PR 올리면 돼.” (공감형 코칭)
  • 까칠한 보안 아키텍트: “SQL 인젝션 위험이 감지되었습니다. 사주를 볼 때가 아닙니다.” (엄격한 코드리뷰형)
  • 스타트업 공격수 CTO: “기술 부채는 내일 갚고, 일단 오늘 프로덕션에 배포해!” (속도 중심)

5. 프로토타입 vs 프로덕션 비교 요약표

비교 항목 현재 구현 (AS-IS) 미래 확장 (TO-BE) 도입 기대 효과
Vector DB SimpleVectorStore (RAM) PostgreSQL (pgvector) 서버 재부팅 시 재색인 불필요, 영구 보관
대화 기억 무상태 (Stateless) Redis ChatMemory 자연스러운 연속 멀티턴 상담 가능
LLM 서빙 단일 로컬 Ollama (11434) vLLM 분산 추론 클러스터 고성능 PagedAttention 기반 대규모 동시성 확보
페르소나 단일 하드코딩 프롬프트 전략 패턴 기반 동적 페르소나 사용자 맞춤형 재미 및 재방문율 증대
모니터링 콘솔 표준 출력 로그 Spring Actuator + Langfuse/OpenTelemetry 토큰 소비량, 검색 품질, LLM 지연 시간 실시간 추적

6. 에필로그: 자바 개발자가 마주한 생성형 AI의 미래

과거에는 LLM이나 벡터 검색 엔진을 결합한 지능형 시스템을 구축하려면 반드시 파이썬 스택(LangChain, LlamaIndex, FastAPI)을 별도로 띄우고 자바 메인 서버와 IPC 통신을 해야 하는 번거로움이 있었습니다.

그러나 Spring AI의 등장으로 우리는 친숙한 Spring Boot의 DI/IoC 컨테이너, 선언적 설정, 리액티브 스트림(WebFlux), 풍부한 엔터프라이즈 데이터베이스 생태계 위에서 생성형 AI를 완벽하게 제어할 수 있게 되었습니다.

‘Dev-Fortune’ 프로젝트는 여기서 일단락되지만, 여러분의 서비스에도 Spring AI를 활용한 비즈니스 지능을 직접 결합해 보시기 바랍니다.

“버그는 코드에만 있는 것이 아닙니다. 여러분의 일상과 루틴도 지속적으로 리팩토링하고 디버깅하세요!” 그동안 Dev-Fortune 시리즈를 함께해 주셔서 진심으로 감사드립니다.