[TIL] 결제/쿠폰 도메인 동시성 제어 전략: 정합성 평가 및 아키텍처 개선 방안

김재진·2026년 3월 19일

내일배움캠프

목록 보기
60/70

프로젝트의 핵심 도메인인 결제/환불(Payment/Refund)쿠폰(Coupon) 시스템에 적용된 동시성 제어(락) 방식을 리뷰하고,
현재 아키텍처의 정합성 평가, 잠재적 문제점, 개선 방안을 정리했다.


1) 결제/환불 도메인: DB 비관적 락(Pessimistic Lock)

✅ 적합성 평가: 매우 적합

결제/환불은 ‘돈’이 오가는 도메인으로 처리량보다 완벽한 데이터 정합성(Consistency) 이 최우선이다.
비관적 락은 같은 결제 건에 대해 동시에 환불/검증 요청이 들어왔을 때 DB 레벨에서 Row Lock으로 직렬화하여 중복 처리를 방지하므로 도메인 특성과 잘 맞는다.

🚨 잠재적 문제점: 트랜잭션 장기 점유로 인한 병목

현재 구조의 가장 큰 위험 요소는 락을 쥔 상태에서 외부 API(예: 포트원 등)를 호출한다는 점이다.

  • @Transactional 내부에서 외부 API를 호출하면 네트워크 지연(Latency) 발생 시
    • DB 커넥션 점유 시간이 늘어나고
    • 락이 오래 유지되며
    • 트래픽 증가 시 커넥션 풀(HikariCP) 고갈 → 시스템 전반 장애로 확산될 수 있다.

💡 개선 방안: 트랜잭션과 외부 API 호출 분리 + 멱등성/상태머신 고려

목표는 DB 락 점유 시간을 최소화하는 것이다.

  • (트랜잭션 외부) 외부 결제/환불 API 호출(검증/처리)
  • (트랜잭션 내부) API 성공 이후에만 DB에 락을 걸고 상태 업데이트 (PAID -> REFUNDED 등)

다만 환불은 외부 API가 중복 호출되면 더 위험할 수 있으므로 아래 안전장치도 함께 고려한다.

  • 외부 API 호출에 멱등키(idempotency key) 를 적용해 중복 실행을 방지한다.
  • 또는 DB에 REFUND_REQUESTED 같은 중간 상태를 먼저 확보(CAS/락) 한 뒤 외부 호출 → 최종 반영하는 상태머신/사가(Saga) 패턴을 검토한다.

2) 쿠폰 도메인: Redis 분산 락(Distributed Lock)

✅ 적합성 평가: 매우 적합

선착순 쿠폰 발급은 특정 시간대에 트래픽이 폭발하는 특성이 있다.
이를 DB 비관적 락으로 제어하면 DB가 병목/장애 지점이 되기 쉽다.
멀티 인스턴스(Scale-out) 환경에서 Redis를 단일 진입점으로 삼아 메모리 레벨에서 빠르게 중복 진입을 차단하는 분산 락 전략은 현재 상황에 매우 적합하다.

🚨 잠재적 문제점: TTL 만료와 유지보수성 취약

1) TTL 만료 딜레마

락 TTL이 3초로 고정되어 있을 경우,

  • GC Pause, 순간적인 서버 지연, DB 저장 지연 등으로 로직이 3초를 초과하면
  • 로직이 끝나기 전에 락이 풀리고 다음 요청이 진입하면서 초과 발급(동시성 붕괴) 이 발생할 수 있다.

2) 어노테이션 기반 키 생성(argIndex) 취약

현재 @RedisLock에서 메서드 파라미터 인덱스(argIndex = 0)로 락 키를 생성하면,

  • 추후 파라미터 순서만 바뀌어도 엉뚱한 키가 생성되어
  • 락이 사실상 무력화되는 치명적인 버그로 이어질 수 있다.

💡 개선 방안: 안전망 구축 + 명시적 키 매핑(SpEL)

1) TTL 최적화 및 모니터링/보호

  • 비즈니스 로직의 p95/p99 처리 시간을 프로파일링하여 TTL을 여유 있게 조정(예: 5초 이상)
  • 가능하다면 락 연장(renewal/watchdog) 또는 처리시간 상한/타임아웃 정책을 도입
  • 최후의 방어선으로 DB 유니크 인덱스(예: MemberCoupon(member_id, coupon_id) 유니크)를 설정해 중복 발급을 원천 차단
  • 유니크 충돌 발생 시 알람(Slack 등)과 로그를 강화하여 조기 탐지

2) 키 생성 방식 개선: SpEL(Spring Expression Language) 도입

인덱스 기반 키 생성을 제거하고 SpEL로 명시적으로 키를 구성한다.

  • 예: @RedisLock(key = "'coupon:' + #couponId")
    • 파라미터 순서 변경에도 안전
    • 가독성과 유지보수성 향상

참고: SpEL로 #couponId처럼 파라미터명을 쓰려면 빌드/컴파일 설정에 따라 파라미터명 보존 옵션이 필요할 수 있으므로 테스트로 보장한다.


🌟 맺음말

단순히 “락을 걸어 동시성 이슈를 막았다”에서 끝나지 않고,
선택한 기술이 장애/병목을 만들지, 그리고 미래의 팀원에게 유지보수 리스크를 남기지 않도록 고민하는 과정이 의미 있었다.

profile
개발공부 처음해보는 사람

0개의 댓글