--- kind: CASE slug: observation-downgraded-by-the-composition title: 관측을 필수 생성자 인자로 만든 수정을 조립이 6인자 생성자로 되돌렸다 topic: assembly-ownership project: clean-architecture-backend-template status: 게시 전 sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 rootTreeNode: case:observation-downgraded-by-the-composition evidenceCapturedOn: 2026-09-01 body: case-observation-downgraded-by-the-composition.body.md assets: - key: observation-downgraded-by-the-composition file: ../../../final/evidence/rendered/observation-downgraded-by-the-composition.svg evidence: - ../../../final/evidence/raw/observation-downgraded-by-the-composition.txt - ../../../final/evidence/raw/tl-messaging-observation-noop.txt source: - 원본 분석 절은 final/document.md#5-2 · final/document.md#a19 §5.1 이다. --- # 관측을 필수 생성자 인자로 만든 수정을 조립이 6인자 생성자로 되돌렸다 발행기는 관측 구현을 필수 인자로 요구하는 생성자를 갖고 있다. 자동설정이 관측 인자가 없는 짧은 생성자를 부르고, 그 생성자는 no-op 관측으로 위임한다. 출하 배포에서 메시징 관측은 아무것도 기록하지 않는다. ## 관계 - **@Bean이 있다는 것은 조립 증거가 아니다** 타입이 요구하는 것과 조립이 넘기는 것이 다를 수 있다는 사례다. - **조립 결함을 판정하려면 조립하는 쪽을 먼저 읽어야 한다** 발행기만 읽으면 관측이 필수로 보인다. - **outbox가 둘이고, 출하되는 것은 messaging 플랫폼 쪽이 아니다** 같은 조립 지점에서 나온 다른 공백이다. ## 문제 DefaultMessagePublisher 는 생성자가 둘이다. 7인자 : 관측 인자가 없고, NO_OBSERVATION 으로 위임한다 8인자 : 관측을 받고 Objects.requireNonNull 로 검사한다 8인자 쪽 javadoc 은 그것이 모든 결과를 기록하는 발행기라고 적는다. 7인자 쪽은 주입 가능한 시계를 위한 것이라고 적는다. NO_OBSERVATION 은 필드 javadoc 이 그 성격을 밝힌다. 메트릭을 배선하지 않은 배포를 위한 no-op 관측이다. 여섯 개 기록 메서드가 전부 빈 본문이다. ## 결론 자동설정이 6인자 호출을 한다. MessagingCoreAutoConfiguration 의 messagingPublisher 빈은 여섯 개 협력자만 받아 6인자 생성자를 호출한다. 그 생성자는 다시 7인자로, 7인자는 8인자로 NO_OBSERVATION 을 넣어 위임한다. 결과적으로 출하 배포의 발행기는 발행도, 전달도, 정산도, 백로그도, 진단도 기록하지 않는다. 관측 인터페이스는 존재하고 구현체도 존재하지만 그 사이를 잇는 조립이 없다. 이 실패의 성격은 조용하다. 빈은 생성되고 발행은 정상 동작하며 로그에도 신호가 없다. no-op 구현이 명시적으로 존재하기 때문에 널 참조도 예외도 나지 않는다. 관측이 꺼진 것과 관측이 배선되지 않은 것이 런타임에서 구별되지 않는다. @ConditionalOnMissingBean 이 붙어 있으므로 애플리케이션이 자기 MessagePublisher 빈을 등록하면 이 자동설정은 물러난다. 그러나 이 저장소의 출하 컴포지션은 그렇게 하지 않는다. ## 검증 환경 OpenJDK : 21.0.12 Gradle : 9.0.0 Spring Boot : 4.0.8 확인 방식 : 정적 도달성 확인. 애플리케이션을 부팅하지 않았다 소스 수정 : x ## 재현 조건 원문은 final/evidence/raw/tl-messaging-observation-noop.txt 에 있다. 1. DefaultMessagePublisher 의 생성자 두 개와 NO_OBSERVATION 필드를 읽는다. 2. 6인자 호출이 7인자로, 7인자가 8인자로 위임하며 관측 자리에 NO_OBSERVATION 이 들어가는 경로를 확인한다. 3. MessagingCoreAutoConfiguration 의 messagingPublisher 빈이 몇 개의 인자로 생성자를 부르는지 확인한다. ## 본문 관측을 선택적 데코레이터가 아니라 필수 생성자 인자로 만든 수정이 runtime-core에 있고 그 javadoc이 "an unobserved publish path is how 'the dashboards were empty during the incident' happens"로 이유를 적는다. ## 관측을 필수 인자로 만든 수정 :::evidence key="observation-downgraded-by-the-composition" alt="분석 문서 final/document.md 에서 이 기록의 근거 절을 그대로 잘라낸 18줄. 코드베이스를 측정한 것이 아니라 원본 판정이 무엇을 적었는지를 보여 준다." caption="final/document.md 발췌 — 18줄" zoom="true" ::: ## 조립이 그 수정을 되돌린다 자동설정은 6인자 생성자를 골라 `NO_OBSERVATION`(다섯 메서드 전부 빈 본문)을 주입하고, 방출자 넷은 main 참조 0이며 등록되는 것은 협력자 둘뿐이다. ## 같은 경로가 예외 메시지를 의도적으로 버린다 둘이 합쳐지면 진단 흔적이 남지 않는다. ## 확인하지 못한 것 애플리케이션을 부팅해 액추에이터에서 messaging 메트릭 시리즈의 부재를 관측하지 않았다. 부팅 한 번이면 확증된다.