Files
sodalive-backend-spring-boot/docs/prd/20260518_쿠폰충전회원락보강_prd.md

70 lines
3.3 KiB
Markdown

# 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`