--- kind: CASE slug: messaging-runtime-core-f01 title: 관측이 구현·호출부·주입 자리를 모두 갖추고도 출하에서 no-op이다 topic: runtime-reachability-and-composition project: clean-architecture-backend-template status: 게시 전 sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 rootTreeNode: case:messaging-runtime-core-f01 evidenceCapturedOn: 2026-09-01 assets: - key: messaging-runtime-core-f01 file: ../../../final/evidence/rendered/messaging-runtime-core-f01.svg - key: messaging-runtime-core-f01-diagram file: ../../../final/assets/diagrams/messaging-runtime-core-f01.svg evidence: - ../../../final/evidence/raw/messaging-runtime-core-f01.txt source: - 원본 분석 절은 analysis/messaging/messaging-runtime-core.md#L710 이다. module: messaging-runtime-core priority: P2 --- # 관측이 구현·호출부·주입 자리를 모두 갖추고도 출하에서 no-op이다 DefaultMessagePublisher가 모든 발행 결과를 observation.recordPublish(...)로 기록하고, 관측을 "constructor argument rather than an optional decorator"로 받는다. MessagingMetrics가 MessagingObservation을 구현한다. ## 관계 - **만들어 두고 흘리지 않는 진단값은 진단이 아니다** 같은 분석 리프에서 끌어낸 규칙이다. - **증가한다고 문서화한 값이 리터럴이면 그 사실을 적는다** 같은 분석 리프에서 끌어낸 규칙이다. - **안정 코드는 운영자의 행동이 갈리는 지점마다 나눈다** 같은 분석 리프에서 끌어낸 규칙이다. - **`CompletionStage`를 반환하는 메서드는 동기적으로 던지지 않는다** 같은 분석 리프에서 끌어낸 규칙이다. ## 문제 DefaultMessagePublisher가 모든 발행 결과를 observation.recordPublish(...)로 기록하고, 관측을 "constructor argument rather than an optional decorator"로 받는다. MessagingMetrics가 MessagingObservation을 구현한다. ## 결론 그런데 출하 조립(MessagingCoreAutoConfiguration:446)은 6인자 생성자를 써서 NO_OBSERVATION을 넣고, MessagingMetrics는 저장소 전체에서 자기 테스트에서만 생성된다. starter는 MessagingMetrics의 협력자 둘(MessagingRedactor:253, CardinalityGuard:264)을 bean으로 만든다. 이 필드의 javadoc이 정확히 이 상황을 막으려고 쓰였다 — "an unobserved publish path is how 'the dashboards were empty during the incident' happens". 그리고 같은 javadoc이 이전 결함을 "bean은 있고 호출 경로가 없었다"로 기록한다. 지금은 반대다 — 호출 경로가 있고 bean이 없다. 관측 결과는 같다. 고침이 간극을 닫은 게 아니라 반대편으로 옮겼다. "decorator가 아니라 생성자 인자"라는 선택도 막지 못했는데, 인자를 기본값으로 채우는 짧은 생성자가 함께 있기 때문이다. ## 검증 환경 OpenJDK : 21.0.12 java -version 으로 확인 Gradle : 9.0.0 src/gradle/wrapper/gradle-wrapper.properties 의 distributionUrl 로 확인 확인 방식 : DefaultMessagePublisher 참조 22건 검색과 출하 조립이 고르는 생성자의 인자 수 확인 소스 수정 : x ## 재현 조건 원문은 analysis/messaging/messaging-runtime-core.md#L710 에 있다. ## 본문 `DefaultMessagePublisher`가 모든 발행 결과를 `observation.recordPublish(...)`로 기록하고, 관측을 "constructor argument rather than an optional decorator"로 받는다. `MessagingMetrics`가 `MessagingObservation`을 구현한다. ## 조립이 되돌린 것 :::evidence key="messaging-runtime-core-f01-diagram" alt="MessagingMetrics 구현과 recordPublish 호출부와 생성자 인자 자리가 갖춰진 것 안에 놓이고 출하 조립의 6인자 생성자가 바깥에 빗금으로 놓인다" caption="조립이 되돌린 것" zoom="false" ::: 그런데 출하 조립(`MessagingCoreAutoConfiguration:446`)은 **6인자 생성자**를 써서 `NO_OBSERVATION`을 넣고, `MessagingMetrics`는 저장소 전체에서 자기 테스트에서만 생성된다. ## DefaultMessagePublisher 참조 위치 :::evidence key="messaging-runtime-core-f01" alt="코드베이스에서 DefaultMessagePublisher 를 검색한 출력 22줄. 이 기록이 세는 참조가 그 출력에 그대로 보인다." caption="DefaultMessagePublisher 코드베이스 검색 — 22줄 · exit 0" zoom="true" ::: ## 협력자 둘은 bean 으로 만들어진다 starter가 `MessagingRedactor:253`, `CardinalityGuard:264` 를 만든다. 이 필드의 javadoc이 정확히 이 상황을 막으려고 쓰였다 — "an unobserved publish path is how 'the dashboards were empty during the incident' happens". ## 고침이 간극을 반대편으로 옮겼다 같은 javadoc이 이전 결함을 "bean은 있고 호출 경로가 없었다"로 기록한다. 지금은 반대다 — 호출 경로가 있고 bean이 없다. 관측 결과는 같다. "decorator가 아니라 생성자 인자"라는 선택도 막지 못했는데, 인자를 기본값으로 채우는 짧은 생성자가 함께 있기 때문이다. ## 확인하지 못한 것 6인자 생성자 선택이 의도인지 확인할 수 없었다. 커밋이 대량 커밋 4개뿐이고 이 선택을 설명하는 기록이 없다.