--- kind: CONCEPT slug: messaging-runtime-core-c05 title: 같은 코드가 두 completion에 쓰인다 topic: delivery-and-settlement-models project: clean-architecture-backend-template status: 게시 전 sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 rootTreeNode: concept:messaging-runtime-core-c05 evidenceCapturedOn: 2026-09-01 assets: - key: messaging-runtime-core-c05 file: ../../../final/evidence/rendered/messaging-runtime-core-c05.svg evidence: - ../../../final/evidence/raw/messaging-runtime-core-c05.txt source: - 원본 분석 절은 final/document.md#a19-messaging-runtime-core#L397 이다. module: messaging-runtime-core --- # 같은 코드가 두 completion에 쓰인다 DefaultMessagePublisher가 만드는 결과: 같은 코드 PUBLISH_DEADLINE_EXCEEDED가 두 completion에 쓰인다. 전송 전이면 REJECTED, 후면 AMBIGUOUS다. ## 관계 - **만들어 두고 흘리지 않는 진단값은 진단이 아니다** 같은 분석 리프에서 끌어낸 규칙이다. - **증가한다고 문서화한 값이 리터럴이면 그 사실을 적는다** 같은 분석 리프에서 끌어낸 규칙이다. - **안정 코드는 운영자의 행동이 갈리는 지점마다 나눈다** 같은 분석 리프에서 끌어낸 규칙이다. - **`CompletionStage`를 반환하는 메서드는 동기적으로 던지지 않는다** 같은 분석 리프에서 끌어낸 규칙이다. ## 본문 `DefaultMessagePublisher`가 만드는 결과에서 같은 코드 `PUBLISH_DEADLINE_EXCEEDED`가 **두 completion에 쓰인다.** 전송 전이면 `REJECTED`, 후면 `AMBIGUOUS`다. 코드만 보는 대시보드는 두 경우를 구분할 수 없다 — completion을 함께 봐야 한다. §17. ## DefaultMessagePublisher 참조 위치 :::evidence key="messaging-runtime-core-c05" alt="코드베이스에서 DefaultMessagePublisher 를 검색한 출력 22줄. 이 기록이 세는 참조가 그 출력에 그대로 보인다." caption="DefaultMessagePublisher 코드베이스 검색 — 22줄 · exit 0" zoom="true" ::: ## sanitized 가 타입 이름만 남긴다 `sanitized(Throwable)`가 메시지가 아니라 **타입 이름만** 남긴다. `messaging-core-api`의 `FailureDescriptor` javadoc("no payload, no stack trace, no credential")과 같은 관심사다. `isDeadline`과 `sanitized` 둘 다 `CompletionException`을 한 겹 벗긴다 — 비동기 경로에서 원인이 감싸지기 때문이다. ## 처리기 쪽은 던지지 않는다 `DefaultDeliveryProcessor`는 예외를 던지지 않는다. 이중 정산만 `failedFuture`로 보고한다.