cd ..

AI Interview Coach

JD 분석·질문 생성·피드백 스트리밍 병목을 5개 MSA와 SSE·RAG·Redis로 분리해 JD 분석 40%, 반복 조회 60% 개선했습니다.

65%
면접 세션 API

N+1 조회를 1회 쿼리로 줄여 응답 시간을 낮춘 개선

100%
통계 정합성

100명 동시 완료 테스트에서도 누락 없이 집계 검증

50ms
일반 API p95

50명 SSE 스트리밍 중에도 안정적으로 응답한 기준값

Java 21Spring Boot 3Spring SecuritySpring Cloud GatewaySpring Data JPAJWTLangChain4jNext.js 14TypeScriptReact QueryPostgreSQLRedisChromaDBDockerKubernetesPrometheusGrafanaGitHub Actionsk6
Key Result

면접 세션 목록 API: N+1(세션 수+1) → 1회 쿼리, 응답 시간 65% 개선.

개요

JD 분석, 질문 생성, 피드백 스트리밍이 한 흐름에 몰리면 응답 지연과 리소스 경합이 커집니다. 이 프로젝트에서는 면접 코칭 흐름을 Gateway, 면접, AI, 통계 서비스로 분리하고 LangChain4j + ChromaDB RAG, SSE 스트리밍, Redis 캐시를 붙여 실시간 피드백을 구성했습니다. 특히 SSE 스레드 분리, 통계 업데이트 동시성 제어, 임베딩 배치 처리처럼 운영 중 병목이 생기기 쉬운 백엔드 문제를 직접 해결했습니다.

서비스 구성 (5개 독립 서비스)

  • API Gateway(8080): Spring Cloud Gateway, JWT 검증, 라우팅, Rate Limit
  • User Service(8081): 회원가입, 로그인(JWT), 프로필 관리
  • Interview Service(8082): JD 분석, 질문 생성, 모의 면접 세션 관리
  • AI Service(8083): LangChain4j + ChromaDB RAG, 답변 평가, SSE 피드백 스트리밍
  • Statistics Service(8084): 사용자별 면접 통계, 일별 활동 집계

AI / RAG 파이프라인

  • LangChain4j로 JD 텍스트 → 직무 키워드 추출 → 맞춤 면접 질문 생성
  • ChromaDB 벡터 DB에 기술 면접 Q&A 임베딩 저장 (768차원)
  • 사용자 답변 → 임베딩 변환 → ChromaDB 유사도 검색 → 컨텍스트 주입 → LLM 평가
  • SSE(Server-Sent Events)로 AI 피드백을 토큰 단위 실시간 스트리밍

성능 최적화

  • N+1 문제: 면접 세션 목록 조회 시 @EntityGraph로 1쿼리 최적화
  • Race Condition: 통계 업데이트 시 비관적 락으로 동시성 제어
  • SSE Starvation: 피드백 스트리밍 시 별도 스레드풀(20개) 분리로 블로킹 방지
  • 임베딩 배치: 개별 API 호출 → 배치 처리로 JD 분석 시간 40% 단축
  • Redis 캐싱: 질문 목록·통계 조회 TTL 캐싱으로 응답 60% 개선

인프라 & DevOps

  • Docker Compose: 5개 서비스 + PostgreSQL + Redis + ChromaDB 일괄 기동
  • Kubernetes: Helm 차트, AI Service HPA(CPU 80% 기준 1-3 replicas)
  • Prometheus + Grafana: RPS, 레이턴시, JVM 메모리, AI 응답 시간 모니터링
  • GitHub Actions: CI(빌드+테스트) + CD(ghcr.io 이미지 푸시)

아키텍처

전체 아키텍처

ERD

전체 ERD

시퀀스 다이어그램

JD 분석 & 질문 생성
모의 면접 세션
AI 피드백 (SSE 스트리밍)

문제 원인

  1. 01면접 세션 목록 조회 시 세션별 QnA 개수를 가져오기 위해 N+1 쿼리가 발생해 응답이 느렸습니다.
  2. 02여러 사용자가 동시에 면접을 완료하면 통계 테이블 업데이트 시 Race Condition으로 데이터 정합성이 깨졌습니다.
  3. 03SSE 피드백 스트리밍이 서블릿 스레드를 점유해, 동시 요청이 많아지면 다른 API 응답이 지연되었습니다.
  4. 04JD 분석 시 키워드마다 개별 임베딩 API를 호출해 전체 처리 시간이 길었습니다.

해결 과정

  1. 01@EntityGraph로 세션-QnA 연관관계를 즉시 로딩하고, @Query로 COUNT 서브쿼리를 사용해 1회 쿼리로 최적화했습니다.
  2. 02통계 업데이트 시 비관적 락(@Lock(PESSIMISTIC_WRITE))을 적용하고, @Retryable(maxAttempts=3)로 락 획득 실패 시 재시도를 구현했습니다.
  3. 03SSE 전용 스레드풀(core 10, max 20)을 분리하고, AsyncTaskExecutor로 피드백 스트리밍을 비동기 처리해 서블릿 스레드 블로킹을 방지했습니다.
  4. 04LangChain4j의 배치 임베딩 API를 활용해 키워드를 한 번에 벡터화하고, Redis에 임베딩 결과를 24시간 TTL로 캐싱했습니다.

결과

  1. 01면접 세션 목록 API: N+1(세션 수+1) → 1회 쿼리, 응답 시간 65% 개선.
  2. 02동시 통계 업데이트: Race Condition 해소, 100명 동시 완료 테스트에서 데이터 정합성 100% 유지.
  3. 03SSE 스레드풀 분리: 동시 50명 피드백 스트리밍 중에도 일반 API p95 응답 시간 50ms 이내 유지.
  4. 04임베딩 배치 + 캐싱: JD 분석 시간 40% 단축, Redis 캐시 적용으로 반복 조회 60% 개선.