# PRD: 쿠폰 충전 회원 락 보강 ## 1. Overview - 쿠폰 사용으로 캔을 지급하는 흐름에서도 회원 잔액 변경 전에 회원 row lock을 확보하도록 보강한다. - 상세 구현 방향은 `docs/plan-task/20260518_쿠폰충전회원락보강.md`를 기준으로 한다. - 이번 문서는 구현 전 요구사항과 최소 작업 범위를 확정하기 위한 신규 작업 문서다. --- ## 2. Problem - 일반 결제 완료 흐름은 `memberRepository.findByIdForUpdate(...)`로 회원 row lock을 잡은 뒤 `member.charge(...)`를 호출한다. - 쿠폰 충전 흐름인 `ChargeService.chargeByCoupon(...)`은 컨트롤러/인증 계층에서 전달받은 `member` 인스턴스에 바로 `member.charge(...)`를 호출한다. - 같은 회원에게 쿠폰 충전과 다른 충전/보너스 지급이 동시에 발생하면 회원 잔액 갱신의 동시성 안전성이 일반 결제 흐름과 달라질 수 있다. --- ## 3. Goals - `CouponType.CAN` 쿠폰 사용 시 회원 row lock을 잡은 엔티티에 `member.charge(...)`를 호출한다. - 기존 쿠폰 사용 성공/실패 응답과 메시지는 변경하지 않는다. - 쿠폰 중복 사용 방지 로직은 기존 동작을 유지한다. - 일반 결제 완료 흐름의 락 패턴과 일관되게 동작하도록 최소 수정한다. - 회귀 테스트로 쿠폰 캔 지급 시 `memberRepository.findByIdForUpdate(...)`가 사용되는지 검증한다. --- ## 4. Non-Goals - 쿠폰 정책, 쿠폰 타입, 쿠폰 발급/조회 로직을 변경하지 않는다. - 포인트 쿠폰 지급 로직은 이번 범위에서 변경하지 않는다. - `Member.charge(...)` 구현 방식과 잔액 컬럼 구조를 변경하지 않는다. - 신규 API나 관리자 기능을 추가하지 않는다. --- ## 5. Core Features ### Feature A. 쿠폰 캔 지급 회원 row lock 적용 #### Requirements - `ChargeService.chargeByCoupon(...)`의 `CouponType.CAN` 분기에서 회원 ID로 `memberRepository.findByIdForUpdate(...)`를 호출한다. - lock 조회에 실패하면 기존 인증 실패 계열 예외 메시지 패턴을 따른다. - `Charge`의 `member` 연결과 `member.charge(...)` 호출은 lock 조회로 얻은 회원 엔티티를 기준으로 수행한다. - `member.charge(...)`는 기존처럼 `+=` 기반 증가 로직을 그대로 사용한다. #### Edge Cases - 이미 사용된 쿠폰이면 기존처럼 `can.coupon.already_used` 예외가 우선 발생해야 한다. - 유효하지 않은 쿠폰 번호이면 기존처럼 `can.coupon.invalid_number_contact` 예외가 발생해야 한다. - 회원 row lock 조회 결과가 없으면 쿠폰 사용과 캔 지급이 진행되지 않아야 한다. --- ## 6. Technical Constraints - Kotlin/Spring/JPA 기존 스타일을 따른다. - `ChargeService`의 기존 트랜잭션 경계를 유지한다. - 공개 API 스키마와 응답 DTO를 변경하지 않는다. - 불필요한 리팩터링 없이 쿠폰 캔 지급 경로만 최소 수정한다. --- ## 7. Metrics - 쿠폰 캔 지급 성공률 - 쿠폰 중복 사용 차단 건수 - 쿠폰 캔 지급 중 회원 lock 조회 실패 건수 - 동시 충전 상황에서 회원 캔 잔액 유실 재발 여부 --- ## 8. Related Documents - `docs/plan-task/20260518_쿠폰충전회원락보강.md` - `docs/prd/20260518_충전이벤트보너스지급안정화_prd.md`