docs(charge): 충전 ID 응답 계약과 검증 결과를 기록한다

This commit is contained in:
2026-11-03 15:50:53 +09:00
parent a6a4e9069b
commit 23673b4465
2 changed files with 67 additions and 4 deletions
@@ -2,13 +2,13 @@
| 항목 | 내용 |
|---|---|
| 상태 | 구현·테스트·리뷰 완료, 미배포 |
| 상태 | Phase 1·2 구현·검증·리뷰 완료 |
| 작성일 | 2026-11-03 (KST) |
| 요구사항 기준 | [prd.md](prd.md) |
| API 기준 | `prd.md` §4 API 계약 |
| 현재 Phase | Phase 1: 결제 완료 정보의 안전한 판정과 전달 |
| 현재 Phase | Phase 2: 완료 응답 chargeId 확장 |
| 현재 활성 Goal | 없음 |
| 다음 Goal | 없음; 운영 적용·클라이언트 검증은 별도 |
| 다음 Goal | 없음; 운영 적용·클라이언트·CI 환경 확인은 별도 |
## 목표
@@ -19,6 +19,7 @@
| Phase | 상태 | 완료 Task | 활성/다음 Goal | 차단 또는 남은 조건 |
|---:|---|---:|---|---|
| 1 | 구현 및 Gate 완료 | 4/4 | 없음 | 운영 DB rollout은 별도 승인 |
| 2 | 구현 및 Gate 완료 | 1/1 | 없음 | 운영·클라이언트·CI 환경 검증은 별도 |
- 사용자 “문서대로 진행” 승인으로 P1-T1부터 실행한다. 완료 증거가 있는 Task만 체크하며 운영 DB 적용·실결제·배포는 하지 않는다.
- `P1-T1 -> P1-T2 -> P1-T3 -> P1-T4 -> P1-GATE` 순서로 진행한다. 특히 `ChargeService` 변경은 한 번에 하나의 goal만 실행한다.
@@ -238,6 +239,34 @@
**Expected:** focused tests 성공과 charge event 관련 직접 영향 회귀 성공. 위 조건이 전체 회귀 조건을 만족하지 않으면 전체 `test`/`build`를 관성적으로 실행하지 않고 생략 근거와 대체 검증을 기록한다. H2 test 통과는 MySQL 실제 컬럼 타입 및 운영 lock 격리를 검증하지 않는다.
### Phase 2: 완료 응답 chargeId 확장
- [x] **Task 2.1: 기존 충전 ID 반환**
**Goal 실행 `P2-T1`:** 다섯 완료 API가 기존 네 필드와 실제 저장된 `Charge.id`를 `chargeId: Long`으로 반환한다.
- **시작 조건:** 사용자 chargeId 추가 구현 승인, Phase 1 완료. 기존 완료 증거와 커밋은 유지한다.
- **완료 증거:** 기존 실제 HTTP 테스트가 ID 누락으로 RED, 추가 후 모든 완료 경로·재조회·웹훅 후 verify에서 저장 ID와 일치한다.
- **범위 밖:** DB·SQL·결제 처리·격리 수준·Jenkins/Gradle 메모리·Google 재시도 정책·SDK 연동 변경, 커밋·푸시.
- **Files:** `src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeCompleteResponse.kt`, `src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeService.kt`, `src/test/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeControllerIntegrationTest.kt`, 현재 PRD·계획.
- **Interfaces:** `ChargeCompleteResponse(chargeId: Long, price: BigDecimal, currencyCode: String, isFirstCharged: Boolean, isIap: Boolean)`; 기존 `completeResponse(charge)`가 저장된 ID를 매핑한다.
- [x] **RED:** HTTP 공통 검증을 다섯 key로 확장하고 예상 Charge.id의 정확한 정수 일치를 검사한다. Google은 실제 생성된 행에서 기대 ID를 읽는다. 재조회 및 웹훅 후 verify에서도 같은 ID를 검사한다.
- [x] **RED 확인:** `./gradlew test --tests 'kr.co.vividnext.sodalive.can.charge.ChargeControllerIntegrationTest.shouldReturnChargeCompleteDataForAllFiveVerificationEndpoints' --offline --console=plain`에서 기존 응답의 chargeId 누락 assertion 실패를 확인한다.
- [x] **GREEN:** DTO에 `chargeId: Long` 한 필드, 공통 mapper에 `chargeId = charge.id!!` 한 매핑만 추가한다.
- [x] **GREEN 확인:** `./gradlew test --tests 'kr.co.vividnext.sodalive.can.charge.*' --offline --console=plain` 및 `./gradlew ktlintCheck --offline --console=plain`로 응답·연결된 충전 회귀를 검증한다.
- [x] **REFACTOR:** 추가 추상화 없이 변경 diff와 PRD 현재 계약을 대조한다. 실제 HTTP ID·나머지 필드·생성/webhook 불변 증거를 기록한다.
#### Phase 2 Gate
**Goal 실행 `P2-GATE`:** chargeId 후속 변경의 계약·회귀·범위를 최종 검증한다.
- **시작 조건:** P2-T1 완료.
- **완료 증거:** 저장 ID와 응답 ID의 실제 HTTP 일치, Kotlin compile/lint, 요청된 전체 회귀 및 독립 리뷰 결과.
- **범위 밖:** Jenkins exit 137의 원인 추정 수정, 운영 DB·실결제·클라이언트 코드 변경.
- **TDD 예외 사유/대체 검증:** read-only Gate다. P2-T1의 RED/GREEN과 diff를 검토하고 ULW 전체 검증 명령 `./gradlew test build ktlintCheck --continue --offline --console=plain`을 실행한다. Jenkins 환경을 재현하거나 해결했다고 주장하지 않는다.
- [x] 전체 테스트·빌드·lint 및 실제 HTTP evidence를 확인한다.
- [x] 독립 리뷰와 작업용 리소스 정리를 완료하고 문서를 갱신한다.
## 데이터베이스 rollout 참고, 실행 금지
Task 1.2에서 [20261103_charge_first_charged_ddl.sql](20261103_charge_first_charged_ddl.sql)을 작성했다. 아래 SQL은 해당 파일의 내용이다. Hibernate dialect의 정적 매핑은 테스트했지만 실제 MySQL 환경의 적용·검증은 별도 운영 승인 대상이다. 애플리케이션 배포 전에 컬럼을 먼저 반영해야 한다.
@@ -251,6 +280,31 @@ NULL 과거 행은 그대로 둔다. backfill 문장은 없다. 선행 컬럼
## Progress
### P2 커밋 직전 재검증, 2026-11-03 KST
- 사용자 `/commit-with-validation` 요청으로 chargeId 후속 변경의 커밋을 승인받고 전역 공용 절차와 저장소 규칙을 확인했다.
- `./gradlew test --tests 'kr.co.vividnext.sodalive.can.charge.*' ktlintCheck --offline --console=plain` exit 0, `BUILD SUCCESSFUL in 3m 27s`. 테스트는 실제 실행됐으며 XML 4 suites / 188 tests / failures=0 / errors=0 / skipped=0을 확인했다. compile·lint는 UP-TO-DATE다.
- `git diff --check` exit 0. DTO·공통 mapper·HTTP 회귀 테스트를 하나의 변경 단위로, PRD·계획을 계약 및 검증 기록 단위로 커밋한다.
- production/test 추가 수정이 없는 커밋 요청이므로 전체 회귀·build는 반복하지 않았다. 현재 XML/HTML은 위 focused 결과로 교체됐다. 운영 DB·실결제·클라이언트 호환성·Jenkins 환경은 이번에도 미검증이다.
### P2-T1 chargeId 후속 확장, 2026-11-03 KST
- 사용자 승인: chargeId 추가 제안에 대한 “수정하자”. 기존 PRD·계획에 PAY-006/P2-T1을 먼저 추가했으며 중복 계획 문서는 생성하지 않았다.
- RED: 위 다섯 endpoint focused 명령 exit 1, 5 tests / 5 failures / 0 errors / 0 skipped. 실제 HTTP 응답의 key가 기존 네 개여서 chargeId 포함 다섯 key assertion이 실패했다. 테스트 설정/컴파일 오류가 아니다.
- 구현: production 변경은 DTO의 `val chargeId: Long`과 공통 `completeResponse`의 `chargeId = charge.id!!` 두 줄뿐이다. 결제·잠금·최초 판정·DB·SQL·CI 설정은 변경하지 않았다.
- GREEN: `./gradlew test --tests 'kr.co.vividnext.sodalive.can.charge.*' ktlintCheck --continue --offline --console=plain` exit 0, 1m 41s, 4 suites / 188 tests / failures=0 / errors=0 / skipped=0. Kotlin compile와 ktlintCheck를 실행했다. LSP 도구는 제공되지 않는다.
- 실제 HTTP: 기존 RANDOM_PORT/TestRestTemplate/JWT/저장소 흐름에서 모든 chargeId는 JSON 정수이며 fixture 또는 Google 생성 행의 ID와 일치했다. true/false/null replay는 같은 ID를 두 번 반환했고 Payverse signed webhook 후 verify도 원래 chargeId를 반환했다. 생성/webhook 응답과 기존 네 필드는 그대로다.
- 현재 증거: `build/test-results/test/TEST-kr.co.vividnext.sodalive.can.charge.ChargeControllerIntegrationTest.xml`의 `HTTP EVIDENCE`. 전체 검증 및 독립 리뷰는 P2-GATE에서 이어서 수행한다. Jenkins exit 137은 별도 미해결 환경 조사이며 로컬 성공을 Jenkins 해결로 간주하지 않는다.
### P2-GATE 후속 최종 검증, 2026-11-03 KST
- 전체 명령: `./gradlew test build ktlintCheck --continue --offline --console=plain` exit 0, `BUILD SUCCESSFUL in 20m 8s`. 최종 XML 341 suites / 3,128 tests / failures=0 / errors=0 / skipped=0. `test`는 실제 실행됐고 lint·compile은 직전 성공 결과의 UP-TO-DATE를 포함한다.
- 실제 HTTP 증거: 기존 22개 HTTP 테스트에서 56회 POST했으며 Google 신규 생성 행, 기존 charge 재조회 및 Payverse 웹훅 선완료 뒤 verify의 ID가 기대 저장 ID와 정확히 일치했다. 예: `/charge/payverse/verify`는 `data={"chargeId":12,"price":9.9900,"currencyCode":"USD","isFirstCharged":true,"isIap":false}`를 반환했다. 같은 PG charge 재조회 두 번은 모두 chargeId 25를 반환했다. 금액·통화·두 boolean 및 생성/webhook shape는 기존 assertion을 유지했다.
- 독립 리뷰: 실제 5개 파일 diff, 공통 생성자 및 HTTP assertion·문서 계약을 검토해 Spec PASS / Quality PASS, 차단 사항 없음. 최초 리뷰 결과 전달 오류 후 재검토 응답을 직접 수신해 확인했다. 리뷰 이후 production/test 변경은 없다.
- 변경 범위: production 두 줄, 기존 HTTP 테스트의 ID 검증 확장, 기존 PRD·계획 갱신만 포함한다. 새 DB 컬럼·SQL·결제 처리·Jenkins/Gradle 메모리 설정 변경은 없으며 커밋·푸시는 하지 않았다.
- 한계: LSP 도구 미제공으로 Kotlin compile·ktlint를 사용했다. 외부 provider는 테스트 대역이며 운영 DB·실결제·클라이언트 호환성·Jenkins 메모리 오류는 미검증이다. 전체 XML/HTML은 `build/test-results/test/`, `build/reports/tests/test/`에 있으며 후속 실행 시 덮어써질 수 있다.
- 정리·범위 확인: test worker·Redis 잔류 없음, 작업 전용 Gradle PID 41633 및 자식 Kotlin PID 41668 TERM 종료 후 ps 부재 확인. 전용 `charge-id.qGHVkX` 경로·하위 캐시·임시 로그와 notepad를 제거했다. 공유 캐시/다른 프로세스는 유지했다. `git diff --check` exit 0이며 수정 파일은 DTO·mapper·기존 HTTP 테스트·PRD·계획 5개뿐이다.
### 커밋 직전 재검증, 2026-11-03 KST
- 사용자 `/commit-with-validation` 요청으로 전역 공용 절차와 저장소 커밋 규칙을 확인했다.