백엔드
재고 예약 상태 전이·멱등성 보수
Java 21 인메모리 재고 예약 서비스의 다중 상품 원자성, 요청·결제 멱등성, 상태 전이와 만료 경계를 공개 API 계약을 유지한 채 보수하고 자동화 테스트로 검증한다.
Task
성공 조건
- 공개 타입·메서드 시그니처·예외 타입과 Gradle 설정을 유지하며, 모든 변경은 지정된 작업 사본 안에 한정한다.
- 입력을 상태 변경 전에 검증하고, 중복 상품을 최초 등장 순서대로 합산하며 long 오버플로와 다중 상품 재고 부족의 원자성을 처리한다.
- 정규화된 상품별 총수량으로 요청 멱등성을 판정해 같은 요청은 현재 예약을 반환하고, 다른 payload는 재고 확인 전 충돌로 거부한다.
- ACTIVE에서만 confirm·cancel·expire를 허용하고, 만료 시각 경계와 재시도에서 재고를 정확히 한 번만 복구한다.
- 결제 ID를 검증하고 전체 예약에서 유일하게 유지하며, 같은 결제 재시도·다른 결제 충돌과 종결 상태 예외를 계약대로 처리한다.
- 상태 전이와 경계 조건을 자동화 테스트로 추가하고, 격리 작업 사본에서 Gradle 테스트를 실제 실행한다.
Common conditions
핵심 조건
- Java 21·Gradle·JUnit 5와 주입된 Clock만 사용하며 외부 DB, 네트워크, 실제 동시성·잠금은 범위 밖이다.
- reserve는 전체 입력 검증→중복 상품 정규화→요청 멱등성→전체 재고 검사 순서로 처리한다.
- 만료 기준은 now >= expiresAt이며 confirm에서 만료되면 먼저 EXPIRED와 재고 복구를 처리한다.
- cancel·expire의 재시도는 재고를 다시 복구하지 않고, paymentId는 전 예약에서 하나만 사용한다.
Model results
모델별 결과
재고 저장소에 일괄 증감 helper를 추가하고 서비스·계약 테스트를 보수한 구현.
gpt-5.6-sol-low 작업 결과
변경한 파일
src/main/java/com/samebrief/reservation/service/ReservationService.javasrc/main/java/com/samebrief/reservation/store/InMemoryInventoryStore.javasrc/test/java/com/samebrief/reservation/ReservationServiceContractTest.java
작업 사본은 verification/work/14-inventory-reservation-state/gpt-5.6-sol-low에 보존했다.
구현 내용
- 공백 요청 ID와 상품 ID를 포함한 예약 입력 검증
- 중복 상품 합산과
long범위 초과 검출 - 정규화된 상품별 총수량을 이용한 요청 멱등성 판정
- 저장소의
decreaseAll을 통한 다중 상품 재고 사전 확인과 일괄 차감 - 저장소의
increaseAll을 통한 취소·만료 재고 복구 사전 계산과 일괄 반영 - 만료 시각을 포함하는
now >= expiresAt경계 처리 - 만료된
ACTIVE예약의 상태 전이와 재고 복구 후 확정 예외 처리 - 동일 결제 재시도, 다른 결제 충돌과 결제 ID 전역 고유성 처리
- 취소·만료 종결 상태의 반복 호출에서 재고 중복 복구 방지
원자성에 필요한 다중 상품 연산을 인메모리 재고 저장소에 모아, 전체 검증 또는 계산이 끝난 뒤 수량을 반영하는 방식을 선택했다. 실제 병렬 처리, 잠금이나 외부 인프라는 추가하지 않았다.
발견한 기존 오류
- 중복 상품 수량이 합산되지 않고 마지막 행으로 덮어써졌다.
- 중복 상품 합산 오버플로를 검출하지 않았다.
- 다중 상품 예약 실패 전에 일부 재고가 차감될 수 있었다.
- 같은 요청 ID의 다른 상품 구성을 멱등성 충돌로 처리하지 않았다.
- 만료 시각과 정확히 같은 순간을 만료에서 제외했다.
- 확정 요청 시 만료 상태 전이와 재고 복구를 수행하지 않았다.
- 결제 확정 재시도와 결제 ID 전역 고유성 규칙이 누락됐다.
- 취소 또는 만료된 예약을 다시 취소하면 재고가 중복 복구될 수 있었다.
테스트 결과
실행 명령:
./gradlew clean test --no-daemon
결과:
- 기본 테스트: 4개 통과
- 추가 계약 테스트: 14개 통과
- 전체: 18개 통과, 실패 0개
BUILD SUCCESSFUL
Recommendation
이 과업의 추천 모델: GPT-5.6 Sol · xhigh
네 결과는 동일한 독립 28개 시나리오를 모두 통과해 기능 정확성은 동률이다. xhigh는 서비스·테스트 두 파일만 보수해 가장 작은 변경 범위로 같은 계약을 충족했고, high 결과가 기록한 채점 자료 경로 노출도 없어 최종 추천으로 정했다.
추천 근거 보기
판단 조건
- 공개 타입·메서드 시그니처·예외 타입과 Gradle 설정을 유지하며, 모든 변경은 지정된 작업 사본 안에 한정한다.
- 상태 전이와 경계 조건을 자동화 테스트로 추가하고, 격리 작업 사본에서 Gradle 테스트를 실제 실행한다.
반영한 평가 항목
정확성 · 완성도 · 실무성 · 가독성 · 효율성
잘 맞는 경우
- 순차 인메모리 예약 서비스의 상태 전이·멱등성 보수
- 공개 계약을 유지하면서 최소 변경으로 경계 회귀 테스트를 남겨야 하는 Java 백엔드 작업
주의할 점
- 추천 우위는 독립 채점의 기능 차이가 아니라 변경 범위와 실행 격리 기록에서 나온다.
- 실제 동시성, 영속 트랜잭션, 운영 관측성은 과업·검증 범위 밖이다.
Prompt and environment
공통 프롬프트와 실행 환경
프롬프트 전문 보기
# 14. 재고 예약 상태 전이·멱등성 보수
Java 21 재고 예약 서비스의 기존 구현을 보수한다. 공개 API 계약은 바꾸지 않고 다중 상품 예약의 원자성, 중복 상품 합산, 요청·결제 멱등성, 예약 상태 전이, 만료 경계와 재고 복구를 바로잡고 JUnit 5 테스트를 추가한다.
실행 환경은 Java 21, Gradle 8.x, JUnit 5와 인메모리 저장소다. 시간은 주입된 Clock으로만 읽는다. 실제 스레드 경합, DB 잠금, 외부 인프라는 구현하지 않는다. 실행 명령은 ./gradlew test다.
ReserveCommand가 null이거나 requestId·상품 ID가 null·빈값·공백이면, 상품 목록이 null·비었거나 행이 null이면, 수량이 1 미만이면 InvalidReservationRequestException으로 상태 변경 없이 거부한다. 같은 상품은 첫 등장 순서를 보존해 합산하고 합산 long 오버플로도 거부한다.
reserve는 정규화한 모든 상품의 재고를 먼저 확인한다. 하나라도 부족하거나 존재하지 않으면 OutOfStockException이고 어떤 재고·예약·멱등성 기록도 남기지 않는다. 성공 시에만 모든 재고를 한 번씩 차감해 ACTIVE 예약을 저장한다. createdAt은 호출 시각, expiresAt은 정확히 15분 뒤다.
성공한 requestId의 동일한 정규화 구성 재시도는 현재 상태의 기존 예약을 반환한다. 다른 구성은 IdempotencyConflictException이며 재고 검사보다 먼저 처리한다. 실패한 요청은 키를 점유하지 않는다.
상태 전이는 ACTIVE→CONFIRMED, ACTIVE→CANCELLED, ACTIVE→EXPIRED만 허용한다. cancel은 ACTIVE 예약을 한 번만 복구하며 CANCELLED·EXPIRED 재시도는 no-op, CONFIRMED 취소는 InvalidReservationTransitionException이다. expireDueReservations는 now >= expiresAt인 ACTIVE만 만료하고 이번에 만료된 건수만 반환한다.
confirm은 paymentId를 먼저 검증하고 예약을 조회한다. ACTIVE 예약이 now >= expiresAt이면 EXPIRED로 바꾸고 재고를 한 번 복구한 뒤 ReservationExpiredException을 던진다. 만료 전 ACTIVE만 확정할 수 있다. 같은 paymentId 재시도는 기존 예약을 반환하고 다른 paymentId는 PaymentConflictException이다. paymentId는 전체 예약에서 유일하다. CANCELLED 확정은 InvalidReservationTransitionException, EXPIRED 확정은 ReservationExpiredException이다.
각 실행은 깨끗한 새 컨텍스트와 전용 작업 사본만 사용하고, 다른 결과·채점 자료를 참조하지 않는다. 구현 뒤 자체 작업 사본에서 실제 테스트를 실행하고 변경 파일, 테스트 결과, 발견한 오류와 트레이드오프를 남긴다.
- Java 21.0.11 · Gradle 8.14.3 · JUnit 5.12.2
- 인메모리 저장소 · 주입 Clock · 외부 DB·네트워크·파일 시스템 없음
- 실행 명령: ./gradlew test