--- kind: CONCEPT slug: application-core-c04 title: 효과가 없었다고 증명할 수 없으면 자동 재시도 권한을 주지 않는다 topic: state-machines-and-ownership project: clean-architecture-backend-template status: 게시 전 sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 rootTreeNode: concept:application-core-c04 evidenceCapturedOn: 2026-09-01 assets: - key: application-core-c04 file: ../../../final/evidence/rendered/application-core-c04.svg - key: application-core-c04-diagram file: ../../../final/assets/diagrams/application-core-c04.svg evidence: - ../../../final/evidence/raw/application-core-c04.txt source: - 원본 분석 절은 analysis/03-application-core.md#L118 이다. module: application-core --- # 효과가 없었다고 증명할 수 없으면 자동 재시도 권한을 주지 않는다 idempotency V2와 inbox와 outbox 셋이 각각 응답을 잃은 구간을 상태로 보존한다. ## 관계 - **legacy storage/notification compatibility surface의 제거 조건 추적** 같은 분석 리프에서 끌어낸 규칙이다. ## 본문 초기 contract는 scope + request fingerprint로 claim/replay를 제공하고, same key/different fingerprint를 conflict로 분리한다. completed result는 replay하고 in-flight는 bounded poll한다. 이 버전은 "DB operation의 효과가 이미 발생했지만 응답만 잃은 상태"를 충분히 표현하지 못한다. ## 응답을 잃은 구간을 맡는 세 장치 :::evidence key="application-core-c04-diagram" alt="응답을 잃은 구간에서 멱등 기록과 인박스와 아웃박스 세 상자로 화살표가 나가고 화살표마다 담당 방식이 붙은 구조" caption="응답을 잃은 구간을 맡는 세 장치" zoom="false" ::: V2는 owner-safe CAS handle에 scope/token/attempt/revision/claimOperationId를 넣고 stale owner mutation을 거부한다. processing-start를 durable하게 확인하기 전에는 body를 실행하지 않으며 claim/start/completion의 unknown result는 inspect/reconcile 대상으로 남긴다. ## 증명할 수 없는 것을 재시도 근거로 쓰지 않는다 processing start 이후 ordinary RuntimeException은 효과가 없다고 증명할 수 없으므로 `EFFECT_UNKNOWN_ABANDONED` 쪽으로 분류되고 자동 replay 권한을 주지 않는다. 명시적인 `RetryableNoEffect`만 안전 재시도 근거로 취급한다. ## RetryableNoEffect 참조 위치 :::evidence key="application-core-c04" alt="코드베이스에서 RetryableNoEffect 를 검색한 출력 4줄. 이 기록이 세는 참조가 그 출력에 그대로 보인다." caption="RetryableNoEffect 코드베이스 검색 — 4줄 · exit 0" zoom="true" ::: ## bounded 하게 만든 것들 scope digest는 versioned keyed digest + operation code로 정규화되고 raw identity는 외부 surface에서 제거된다. lease/replay TTL과 owner token grammar도 bounded다. **Historical evidence.** V2 contract가 인접한 package에 중복 복제돼 구현체들이 서로 다른 nominal type을 참조한 문제가 있었고, singular contract를 유지하는 regression test가 존재한다. ## inbox 상태 모델 Inbox contract는 same-store 처리와 owner-safe receive/process state를 모델링한다. `RECEIVED -> PROCESSING -> COMPLETED/RETRYABLE/DEAD` 상태를 가지고 ACK는 handler transaction commit 이후에만 가능하다. expired owner가 늦게 결과를 기록하는 것을 owner token/attempt/revision/operation identity로 막는다.