docs(live): 유료 예약 원자성 검증 기록을 남긴다

This commit is contained in:
2026-07-15 20:54:10 +09:00
parent 074042270c
commit 426f4ae76e

View File

@@ -113,6 +113,30 @@ fun makeReservation(request: MakeLiveReservationRequest, memberId: Long): MakeLi
--- ---
### Phase 4: 유료 예약 원자성 회귀 보강
- [x] **Task 4.1: 예약 저장 실패 시 결제와 예약의 전체 롤백 검증**
- Modify: `src/test/kotlin/kr/co/vividnext/sodalive/live/reservation/LiveReservationServiceIntegrationTest.kt`
- Modify: `docs/20260715_라이브_예약_LazyInitializationException_수정/plan-task.md`
- RED: 실제 `CanPaymentService`와 JPA 저장소를 사용하는 유료 예약 fixture를 만들고, `LiveReservationRepository.save()` spy에서 `UseCan` 증가를 확인한 뒤 `DataIntegrityViolationException`을 던지게 한다.
- RED: 기존 production code가 이미 `@Transactional`을 포함하므로, 임시로 `noRollbackFor = [DataIntegrityViolationException::class]` 변이를 적용했을 때 결제 잔액 검증이 실패하는지 확인한 뒤 즉시 원복한다.
- GREEN: 원래 `@Transactional`에서 같은 테스트가 통과하고 회원 캔 잔액, 충전 잔액, 사용 내역 건수와 예약 존재 여부가 호출 전 상태와 같은지 검증한다.
- REFACTOR: 새 production code나 별도 추상화를 추가하지 않고 기존 통합 테스트 fixture만 최소 확장한다.
- Verify: `./gradlew --no-daemon test --rerun-tasks --tests kr.co.vividnext.sodalive.live.reservation.LiveReservationServiceIntegrationTest`
- 기대 결과: 2개 테스트가 실행되고 모두 통과하며, 테스트 결과 XML의 failures/errors가 0이다.
- 범위 분리: 전체 테스트/clean build의 KAPT 산출물 재현성 문제는 이번 기능 변경에 포함하지 않고 별도 빌드 작업으로 분리한다.
- 검증 기록:
- 무엇: 유료 예약의 저장 실패 뒤 회원 캔, 충전 잔액, `UseCan` 건수와 예약 존재 여부를 별도 트랜잭션에서 다시 조회했다.
- 왜: `CanPaymentService.spendCan()`의 기본 `REQUIRED` 전파가 외부 예약 트랜잭션에 참여해 결제 변경도 함께 롤백되는지 직접 확인하기 위해서다.
- 결제 선행 확인: 예약 저장 실패를 발생시키기 직전에 같은 트랜잭션의 `UseCan` 건수가 호출 전보다 1 증가했는지 확인해 결제가 저장보다 먼저 실행됐음을 고정했다.
- RED: `makeReservation()`에 임시 `noRollbackFor = [DataIntegrityViolationException::class]` 변이를 적용하고 새 단일 테스트를 실행했다. XML은 tests=1, failures=1이며 회원 캔 검증이 expected 100, actual 0으로 실패했다.
- GREEN: 변이를 즉시 원복하고 같은 단일 테스트를 재실행했다. `BUILD SUCCESSFUL in 58s`, XML은 tests=1, failures=0, errors=0이다.
- 리뷰 보정: 예약 저장 실패 직전 `UseCan` 증가 assertion을 추가한 뒤 정상 경계에서 단일 테스트가 `BUILD SUCCESSFUL in 39s`로 통과했다. 같은 변이를 다시 적용하면 XML tests=1, failures=1과 expected 100, actual 0을 재현했다.
- 최종 GREEN: 변이를 원복하고 `./gradlew --no-daemon test --rerun-tasks --tests kr.co.vividnext.sodalive.live.reservation.LiveReservationServiceIntegrationTest`를 실행했다. `BUILD SUCCESSFUL in 3m 47s`, XML은 tests=2, failures=0, errors=0이다.
- production code 원복 확인: `git diff -- src/main/kotlin/kr/co/vividnext/sodalive/live/reservation/LiveReservationService.kt`가 출력 없이 종료됐다.
---
## 검증 기록 ## 검증 기록
- 계획 작성 시점에는 production code와 테스트를 변경하지 않았다. - 계획 작성 시점에는 production code와 테스트를 변경하지 않았다.
@@ -125,3 +149,6 @@ fun makeReservation(request: MakeLiveReservationRequest, memberId: Long): MakeLi
- 2026-07-15: 관련 단일 테스트, `ktlintCheck`, `tasks --all`, `git diff --check`가 최종 통과했다. - 2026-07-15: 관련 단일 테스트, `ktlintCheck`, `tasks --all`, `git diff --check`가 최종 통과했다.
- 2026-07-15: 완료 선언 전 `./gradlew --no-daemon test --rerun-tasks --tests kr.co.vividnext.sodalive.live.reservation.LiveReservationServiceIntegrationTest`를 실행해 캐시 없이 `BUILD SUCCESSFUL in 3m 39s`를 확인했다. 출력된 deprecation/unchecked cast 경고는 기존 파일에서 발생했으며 이번 변경 파일과 무관하다. - 2026-07-15: 완료 선언 전 `./gradlew --no-daemon test --rerun-tasks --tests kr.co.vividnext.sodalive.live.reservation.LiveReservationServiceIntegrationTest`를 실행해 캐시 없이 `BUILD SUCCESSFUL in 3m 39s`를 확인했다. 출력된 deprecation/unchecked cast 경고는 기존 파일에서 발생했으며 이번 변경 파일과 무관하다.
- 2026-07-15: 브랜치 완료 전 전체 회귀 검증으로 `./gradlew --no-daemon test`를 실행해 `BUILD SUCCESSFUL in 5m 48s`를 확인했다. - 2026-07-15: 브랜치 완료 전 전체 회귀 검증으로 `./gradlew --no-daemon test`를 실행해 `BUILD SUCCESSFUL in 5m 48s`를 확인했다.
- 2026-07-15: 유료 예약 원자성 회귀 테스트 보강 후 같은 통합 테스트 클래스를 `--rerun-tasks`로 실행해 `BUILD SUCCESSFUL in 3m 47s`, XML tests=2, failures=0, errors=0을 확인했다.
- 2026-07-15: `./gradlew --no-daemon ktlintCheck``BUILD SUCCESSFUL in 22s`, `./gradlew --no-daemon tasks --all``BUILD SUCCESSFUL in 6s`로 통과했다.
- 2026-07-15: 최초 캐시 사용 단일 테스트 명령은 `:test NO-SOURCE`와 빈 테스트 산출물로 종료되어 검증 증거로 인정하지 않았다. `--rerun-tasks` 실행에서는 실제 테스트 XML을 확인했으며, 이 산출물 재현성 현상의 원인 조사와 수정은 별도 빌드 작업으로 분리한다.