위스키 애호가를 위한 리뷰, 구매 플랫폼
- 다양한 검색 조건으로 위스키 탐색
- 평점과 리뷰 기반 커뮤니티
- 안전한 결제 시스템
- JAVA 21
- Spring Boot, Spring Security
- JPA
- Docker
- Gradle
- 객체지향 설계원칙을 적용하여 유지보수에 용이한 코드 구현
- 서버 구축과 CI/CD 자동화를 통한 인프라와 배포 전반에 대한 이해
- 트레이드 오프를 고려한 기술 선택
whiskey/
├── module-api (Controller, Security)
├── module-domain (Entity, Service, Repository)
└── module-common (API Response)
- 위스키 검색 & 필터링 (가격대, 도수, 지역 등)
- 평점 & 리뷰 시스템
- 결제 & 주문 관리
- JWT 기반 인증
- 배경
-
결제 예약 후 30분 이내 미결제 자동 취소 필요
-
사용자가 예약만 하고 결제를 하지않으면 재고가 묶이는 문제 발생
-
방안 A : DB 스케쥴러 (Spring @Scheduled + DB 폴링)
✅ 구현이 단순함
✅ 트랜잭션 처리 명확
❌ 설정한 시간마다 예약 테이블 스캔 -> DB 부하 발생!
❌ 최악의 경우, 시간 지연 발생 (1분으로 설정한 경우, 59초 지연 발생) -
방안 B : Quartz와 같은 외부 스케쥴러
✅ 정확한 시간에 실행 가능, 클러스터링 지원
✅ Cron 표현식으로 다양한 스케쥴링 지원
❌ 추가 의존성, 인프라 복잡도 증가
❌ 현재 단일 작업(예약 만료 체크)만 있으므로 오버 엔지니어링이라 판단 -
방안 C : Redis Sorted Set (선택)
✅ 만료 시간을 score로 설정, O(logN) 조회
✅ DB 부하없는 빠른 처리 보장
❌ Redis 장애 발생 시 만료처리 중단 -
최종결정
- Redis Sorted Set에 만료시간을 score로 설정
- 1분마다 현재 시간 이하 score를 가진 예약 조회 후 취소 처리
- Redis 장애 대비 DB에도 만료시간 저장 (최악의 경우, DB 폴링으로 복구 가능)
-
결과
- DB 부하없이 안정적인 만료 처리
- 평균 처리 지연 < 1분
- Redis 메모리 사용량 : 예약 1만건 기준 ~2MB
-
- 배경
-
리뷰 작성시 위스키 평점을 실시간 업데이트
-
평점은 자주 조회되므로 Redis 캐싱
-
리뷰 저장과 Redis 업데이트를 같은 트랜잭션에서 처리하면?
- Redis 실패시 전체 롤백? (사용자는 리뷰만 작성했는데 실패)
- Redis 성공했는데 DB 롤백? (데이터 불일치)
-
방안 A : 동기처리 (@Transactional 내부)
❌ Redis 장애시 리뷰 저장 실패
❌ 외부 시스템(Redis)이 트랜잭션에 영향을 줌 -
방안 B : 비동기 처리 (@Async)
✅ 빠른 평점 반영
❌ 트랜잭션 커밋 전에 실행될 가능성이 있음
❌ DB 롤백되어도 평점은 계산됨 (데이터 불일치) -
방안 C : @TransactionalEventListener (선택)
✅ DB 커밋이 성공했을 때만 캐시 업데이트
✅ Redis 실패해도 리뷰 저장은 성공
❌ Redis 업데이트 실패하면 평점 반영 안됨 -
최종결정
- 리뷰 저장 트랜잭션과 Redis 업데이트 분리
- 트랜잭션 커밋 후 이벤트 발행(AFTER_COMMIT)
- Redis 실패시 다음 조회때 DB에서 재계산하여 캐시 갱신
-
결과
- 리뷰 작성 실패율 0% (Redis 장애와 무관)
- 최악의 경우 평점 반영 지연 (사용자 경험에는 문제없음)
-
- 배경
-
Toss Payments API 호출이 네트워크 상태에 따라 지연됨
-
주문 생성 트랜잭션 내부에서 결제 API 호출
-
결제 API 대기 중 DB 커넥션 홀딩 -> 커넥션 풀 고갈 위험
-
방안 A : 트랜잭션 타임아웃 설정
❌ 여전히 커넥션 점유 문제 발생 -
방안 B : 외부 API를 트랜잭션 밖으로 분리 (선택)
✅ DB 커넥션 빠르게 반환
✅ 결제를 실패하면 보상 트랜잭션으로 주문 취소
❌ 구현 복잡도 증가 (3단계 과정 진행) -
최종결정
- 주문 생성 -> 결제 API 호출 -> 주문 확정(재고 처리) 3단계로 분리
- 결제 API 타임아웃 발생하면 Spring Retry로 최대 3회 재시도
- 재시도 모두 실패시 주문을 실패상태로 확정하고 관리자에게 전달
-
결과
- DB 커넥션 점유시간 감소
- 일시적 네트워크 오류 대응 가능
-