Now & Go — AI 여행 플래너
4인 팀 · 백엔드 공통 모듈 · 아키텍처 설계 2025.09 — 2026.03검색 → 추천 → 대리결제를 잇는 여행 커머스 / Java · Spring Boot 3 · Python · Kafka · Redis · Elasticsearch
- Role
- 백엔드 공통 모듈과 아키텍처 설계를 맡아 팀 전체 커밋의 약 46%를 담당했습니다. 아래 세 가지가 제가 직접 설계·구현한 축입니다.
73
Gradle Modules
15
공통 모듈
46%
팀 커밋 담당
88k
Java LOC
01 — Search & Recommendation
RAG 파이프라인
- Problem
- 일반 LLM은 플랫폼의 최신 상품·실시간 재고를 알 수 없어 환각 위험이 큽니다. 대화 기억도 도구 호출도 필요 없는 우리 요구에는 LangChain이 오버스펙이고, 검색의 핵심이 카테고리 슬롯 보장이라 ES 쿼리 제어권을 포기할 수 없었습니다.
- Solution
- Haiku로 의도를 뽑고 → Titan으로 임베딩 → ES에서 슬롯 보장 + kNN·BM25 RRF로 후보를 만들고 → PostgreSQL에서 라이브 SKU를 붙인 뒤 → Sonnet이 계획을 생성하고 → 서버가 제약을 검증해 위반 시 재시도합니다.
6
Pipeline Stages
2
Search Modes
3
Negation Layers
02 — Concurrency
분산락 & Outbox
- Problem
- PostgreSQL 직접 조회 방식은 동시 접속 시 같은 좌석 행에 락 경합이 걸리고 대기 트랜잭션이 쌓이며 커넥션 풀이 고갈됩니다. 결제까지 15분을 기다려야 하는데 그동안 DB 커넥션을 잡고 있을 수 없었습니다.
- Solution
- 좌석 선점을 Redis로 옮기고 선점(15분 TTL) → 확정(10초 락) 2단계로 나눴습니다. 해제는 Lua로 소유자 확인과 삭제를 원자적으로 묶어 남의 락을 지우는 사고를 막았습니다.
- Insight
- 이건 락이라기보다 예약 홀드입니다. TTL을 락 수행시간이 아니라 결제 세션 길이로 잡았기 때문에, 사용자가 결제를 포기해도 15분 뒤 좌석이 자동으로 풀립니다 — 만료 배치가 필요 없습니다.
- 획득 —
SET key uuid NX EX 900한 커맨드로 원자적. SETNX 후 별도 EXPIRE는 그 사이 프로세스가 죽으면 TTL 없는 영구 락이 남습니다. - 해제 — GET으로 확인하고 DEL 하면 그 사이 TTL이 만료돼 남의 락을 지울 수 있어, Lua 스크립트로 확인과 삭제를 원자적으로 묶었습니다.
- 확정 — 결제 콜백은 at-least-once라 중복이 옵니다.
lock:confirm-seats:{orderNo}로 이중 확정을 차단했습니다. - Outbox — DB 커밋 후 Kafka 발행이 실패하면 이벤트가 유실됩니다. 트랜잭션 안에서 outbox에 적재하고 폴링 잡이 발행하도록 바꿔 6개 서비스에 일괄 적용했습니다.
- 알려진 한계 — watchdog이 없어 TTL을 초과하는 작업은 상호배제가 깨질 수 있고, 단일 Redis 기준이라 RedLock은 아닙니다. 최종 방어선은 DB 유니크 제약이어야 합니다.
6
Services (Outbox)
0
Lost Events
2
Lock Stages



















