Solo Backend Project

감성카트

멀티 모듈 마이크로서비스 기반 이커머스 플랫폼. 상품 관리, 주문 처리, 재고 관리, 배송, 결제, 검색까지 전체 이커머스 도메인을 8개 모듈로 분리하고, 메시지 기반 비동기 통신으로 모듈 간 결합도를 최소화.

Java 21Spring Boot 3.4RabbitMQElasticSearchRedis StreamsRedissonOpenFeignResilience4jSeleniumArchUnit
GitHub에서 보기

8개 멀티 모듈 구조

도메인별 모듈 분리로 독립적 개발·배포·테스트가 가능한 마이크로서비스 모노레포

Product

상품 CRUD, 카테고리, 브랜드 관리

Order

주문 생성, 결제, 상태 관리

Member

회원, OAuth2 (카카오/네이버)

Stock

재고 관리, Redisson 분산 락

Shipment

배송 생성, 추적, 상태 관리

Auth

JWT, OAuth2, 인증/인가

Crawler

Selenium 쿠팡 상품 크롤링

Common

공통 DTO, 예외, 유틸리티

핵심 기술 구현

동시성 제어, 검색 엔진, 메시징 전략까지 — 이커머스의 핵심 기술 과제 해결

Redisson MultiLock 동시성 제어

다수 사용자의 동시 주문 시 상품별 재고 차감의 원자성 보장. 락 키를 정렬된 순서로 획득하여 데드락 방지. DB 비관적 락 대비 DB 부하 분산.

ElasticSearch Nori 한국어 검색

Nori 형태소 분석기로 한국어 자연어 검색. combinedField copy_to 패턴으로 상품명/설명/브랜드 통합 검색. 자동완성 + 유사 상품 추천(MLT).

Redis 쓰기 버퍼 패턴

판매 수량을 Redis HINCRBY로 실시간 집계 → 60초 배치로 ES Painless 스크립트 업데이트. ES 쓰기 부하 60배 감소.

RabbitMQ + Redis Streams 하이브리드

도메인 이벤트(주문→재고, 결제→배송)는 RabbitMQ로 안정성 확보. 검색 인덱스/인기도 집계는 Redis Streams로 저지연 처리.

Resilience4j Circuit Breaker

OpenFeign 기반 모듈 간 동기 호출에 Circuit Breaker 적용. 장애 전파 차단 + fallback 응답으로 부분 장애 격리.

Search & Popularity Pipeline
Order Event
RabbitMQ
Redis HINCRBY
60s Batch Flush
ES Painless Update

주문 이벤트 발생 시 RabbitMQ를 통해 Redis에 판매 수량을 원자적으로 집계(HINCRBY). 60초 주기로 누적된 변경분을 배치 플러시하여 ElasticSearch의 salesCount를 Painless 스크립트로 업데이트. 건별 실시간 쓰기 대비 ES 쓰기 부하 60배 감소.

아키텍처 하이라이트

01

멀티 모듈 모노레포 전략

8개 도메인 모듈(product, order, member, stock, shipment, auth, crawler) + common 모듈. 공통 DTO/예외/유틸리티를 common에 집중하여 코드 중복 제거. 단일 빌드 파이프라인으로 의존성 버전 통합 관리.

:product · :order · :stock · :shipment · :auth · :member · :crawler · :common
02

Redisson MultiLock 동시 주문 처리

주문에 포함된 N개 상품의 재고를 동시에 차감할 때 정합성 보장. 상품 ID 기준 정렬로 락 획득 순서를 고정하여 데드락 방지. DB 비관적 락 대비 데이터베이스 부하 분산.

lock:stock:{productId} · sorted acquisition · Redisson fairLock
03

ElasticSearch 검색 아키텍처

Nori 한국어 분석기 + combinedField copy_to 패턴으로 상품명/설명/브랜드를 단일 필드로 통합 검색. prefix query 자동완성, More-Like-This 유사 상품 추천, 관리자 교차 엔티티 검색(4개 인덱스). TLS/JKS 보안 연결.

nori_tokenizer · combined_field · more_like_this · 4 indices
04

하이브리드 메시징 전략

RabbitMQ: 주문생성→재고차감, 결제완료→배송생성 등 도메인 이벤트 (메시지 유실 불가). Redis Streams: 검색 인덱스 업데이트, 인기도 집계 등 (저지연 우선, 유실 허용). 동기 호출은 OpenFeign + Circuit Breaker로 장애 전파 차단.

RabbitMQ exchanges · Redis XADD/XREAD · Resilience4j circuitBreaker
8
Modules
2
Message Brokers
4
ES Indices
70%+
Coverage
60x
Write Reduction

기술적 의사결정

DECISION 01

왜 멀티 모듈 모노레포인가

멀티 레포 대비 공통 모듈 공유가 용이하고 단일 빌드 파이프라인으로 의존성 버전 통합 관리. 초기 개발 속도와 리팩토링 편의성 확보. 추후 각 모듈을 독립 서비스로 분리할 수 있는 구조.

DECISION 02

왜 Redisson MultiLock인가

DB 비관적 락은 커넥션을 오래 점유하여 DB 부하 유발. Redisson 분산 락은 Redis 기반으로 부하를 분산하고, MultiLock은 N개 상품 락을 정렬 순서로 획득하여 데드락을 원천 차단.

DECISION 03

왜 Redis 쓰기 버퍼 + 배치 플러시인가

주문마다 ES를 직접 업데이트하면 쓰기 부하가 검색 성능을 저해. Redis HINCRBY로 원자적 집계 후 60초 배치로 ES에 반영하여 쓰기 부하 60배 감소. 실시간성은 60초 지연으로 충분.

DECISION 04

왜 RabbitMQ + Redis Streams 하이브리드인가

단일 브로커는 모든 요구사항을 최적으로 충족하기 어려움. 주문/결제 같은 핵심 이벤트는 RabbitMQ의 메시지 보장, 검색/집계 같은 보조 이벤트는 Redis Streams의 저지연으로 역할 분리.

DECISION 05

왜 Nori combinedField copy_to인가

필드별 개별 검색은 쿼리 복잡도가 높고 관련성 스코어 통합이 어려움. copy_to로 상품명/설명/브랜드를 combined_field에 통합하여 단일 쿼리로 복합 검색 + 통합 스코어링.

실전 트러블슈팅

TROUBLESHOOT 01

다중 상품 동시 주문의 재고 정합성과 데드락 방지 설계

문제 정의

여러 사용자가 동시에 N개 상품을 포함한 주문을 할 때, 상품별 재고 차감이 원자적이지 않으면 재고가 음수로 내려감. 단순 분산 락은 다중 상품 주문 시 데드락 위험 — 사용자 A가 상품 1→2 순서, 사용자 B가 상품 2→1 순서로 락을 잡으면 교착 상태 발생.

설계 과정

1단계 DB 비관적 락 검토 → 커넥션 장시간 점유로 동시성 높을 때 커넥션 풀 고갈 위험. 2단계 Redisson 단일 락 → 상품별로는 해결되지만 다중 상품 주문 시 데드락 미해결. 3단계 락 획득 순서를 고정하면 데드락이 원천 차단된다는 점에 착안.

핵심 설계

Redisson MultiLock + 상품 ID 정렬 전략. 주문 내 모든 상품 ID를 오름차순 정렬 → 정렬된 순서로 lock:stock:{productId} 획득 → 전체 재고 차감 → 역순 해제. 모든 스레드가 동일 순서로 락을 잡으므로 데드락 원천 차단.

결과

동시 주문 환경에서 재고 정합성 100% 보장 + 데드락 제로. DB 커넥션 부하를 Redis로 분산하여 DB 비관적 락 대비 커넥션 점유 시간 대폭 감소. 추후 인스턴스 확장에도 Redis 기반이라 스케일링에 안전.

TROUBLESHOOT 02

검색 인기도 실시간 반영 vs ElasticSearch 쓰기 부하의 트레이드오프 해결

문제 정의

상품 검색 결과에 판매 수량(인기도)을 실시간 반영하고 싶지만, 주문 발생마다 ElasticSearch를 직접 업데이트하면 쓰기 부하가 검색 쿼리 성능을 저해. 특히 세일/이벤트 시 주문 폭증 → ES 쓰기 폭증 → 검색 응답 지연이라는 cascading failure 위험.

설계 과정

방안 1 ES 직접 업데이트 → 쓰기 부하가 검색과 경합, 세일 시 장애 위험. 방안 2 Cron 배치 (1시간) → 인기도 반영이 너무 늦음. 방안 3 쓰기 버퍼 패턴 — Redis에서 원자적 집계 후 짧은 주기로 배치 플러시. "60초 이내 반영이면 사용자 체감상 실시간"이라는 판단.

핵심 설계

RabbitMQ → Redis HINCRBY → 60초 배치 → ES Painless. 주문 이벤트를 RabbitMQ로 수신 → Redis HINCRBY product:sales:{id}로 원자적 집계 (N건의 주문이 1개의 카운터에 누적) → 60초 스케줄러가 변경분을 ES Painless 스크립트로 salesCount에 delta 적용 후 Redis 키 삭제.

결과

ES 쓰기 부하 60배 감소 (건별 → 60초 배치). 검색 쿼리 성능과 인기도 반영을 모두 확보. 세일/이벤트 트래픽 폭증에도 Redis가 쓰기를 흡수하여 ES 안정성 보호. 60초 지연은 사용자 체감상 무시 가능한 수준.