--- kind: CASE slug: a19-f015-compatibilitymatrix-extension title: CompatibilityMatrix 의 EXTENSION 등급을 쓰는 항목이 없다 topic: messaging-and-outbox project: clean-architecture-backend-template status: 게시 전 sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 rootTreeNode: case:a19-f015-compatibilitymatrix-extension evidenceCapturedOn: 2026-09-04 body: case-a19-f015-compatibilitymatrix-extension.body.md assets: - key: a19-f015-compatibilitymatrix-extension file: ../../../final/evidence/rendered/a19-f015-compatibilitymatrix-extension.svg evidence: - ../../../final/evidence/raw/a19-f015-compatibilitymatrix-extension.txt source: - 원본 분석 절은 final/document.md#a19#L801 이다. --- # CompatibilityMatrix 의 EXTENSION 등급을 쓰는 항목이 없다 `messaging-testkit` 의 `CompatibilityMatrix` 가 어댑터 지원 등급을 `Tier` 셋으로 두는데 `ENTRIES` 다섯 중 `EXTENSION` 을 쓰는 항목이 0 이다. 그 등급 설명에 해당하는 `messaging-spring-cloud-stream-bridge` 는 브로커 버전을 인증하지 않아 `Entry` 생성자를 통과할 수 없다. ## 관계 - **build-only 등급이 90개 파일의 미조립을 오늘의 사고에서 면제한다** 그 기록은 빌드에만 참여하는 모듈이 조립 검사에서 빠지는 것을 다룬다. 이 leaf 도 `runtime_memberships` 가 비어 있는데, 여기서 어긋난 것은 조립이 아니라 등급 표기다. - **기계가 읽는 능력 매니페스트와 사람이 읽는 등급표가 커서 서명에 대해 다르게 답한다** 둘 다 코드가 든 표와 운영자가 읽는 문서 표가 같은 대상에 대해 서로 다른 값을 적고 있는 경우다. - **지원 등급은 추론이 아니라 선언이고 증거 없이는 올라가지 않는다** 그 결정은 증거가 있을 때만 등급을 올린다고 정한다. 여기서는 그 규칙 이전에, 등급 값 하나가 어떤 항목에도 붙지 못한 채 열거형에만 남아 있다. ## 문제 지원 등급이 messaging-testkit 의 CompatibilityMatrix 안에 표로 들어 있다. 세 등급 중 하나가 어느 항목에도 쓰이지 않는데, 그 등급 설명에 맞는 모듈이 저장소에 있다. ## 결론 Tier 값 셋 중 EXTENSION 을 쓰는 항목이 ENTRIES 에 없다. 다섯 항목은 messaging-kafka 하나가 STABLE 이고 나머지 넷이 EXPERIMENTAL 이다. 그 등급 설명이 말하는 어댑터 SPI 전용·지원 집합 밖에 해당하는 모듈은 messaging-spring-cloud-stream-bridge 다. main 6 파일 507 줄인데 표에 이름이 없다. 행이 되지 못하는 기계적 이유가 있다. Entry 의 간결 생성자는 브로커 버전 목록이 비면 거부한다. 이 모듈은 브로커가 아니라 Spring Cloud Stream 바인더 위의 이음매라 인증할 브로커 버전이 없다. 운영자용 docs/messaging/support-matrix.md 는 등급을 한 표로 관리하지 않는다. 앞쪽 표에서 Extension 이 붙은 행은 :37 하나뿐이다. 거기 적힌 어댑터 이름을, 코드 쪽 시험은 표에 없는 이름의 예시로 쓴다. 기능 등급 표(:66)의 :78 에는 이 bridge 가 있고 등급 칸이 Optional 인데, 그 낱말은 Tier 에 없다. 이 타입을 참조하는 main 코드는 자기 파일 하나뿐이다. 미등록 이름을 거부한다는 보장이 걸리는 범위는 시험과 계약 스위트다. 판정은 P3 이고 원문과 같다. 다만 원문은 이 bridge 가 운영자 문서에도 없다고 적었는데 :78 에 행이 있다. ## 검증 환경 확인 방식 : Tier 열거값과 각 자바독 확인, ENTRIES 항목 수와 등급별 계수, bridge leaf 의 main 파일 수·줄 수와 modules.json 의 runtime_memberships 확인, Entry 간결 생성자의 거부 조건 확인, of 의 거부 줄과 그것을 고정하는 시험 확인, 그 시험이 넘기는 이름 검색, CompatibilityMatrix 를 쓰는 main 코드 계수, 운영자 문서의 등급 표 전수와 각 표의 해당 행 확인, bridge 자바독과 정책 가드의 조건·예외 코드 확인 소스 수정 : x ## 재현 조건 1. CompatibilityMatrix.Tier 의 값 셋과 각 값의 자바독을 읽는다. 2. ENTRIES 의 항목을 세고 각 항목의 Tier 를 모은다. 3. 등급별 사용 횟수를 집계해 쓰이지 않는 값을 찾는다. 4. 그 값의 설명에 해당하는 모듈을 저장소에서 찾고 파일 수와 줄 수를 잰다. 5. modules.json 에서 그 모듈의 runtime_memberships 를 읽는다. 6. Entry 의 간결 생성자가 무엇을 거부하는지 읽고, 그 모듈이 그 조건을 만족할 수 있는지 본다. 7. of 가 등록되지 않은 이름을 어떻게 처리하는지와 그것을 고정하는 시험, 그 시험이 넘기는 이름을 확인한다. 8. CompatibilityMatrix 를 자기 파일 밖에서 쓰는 코드를 소스 세트별로 센다. 9. 운영자 문서에서 등급 표를 전부 찾고, 각 표에서 그 등급 값과 그 모듈 이름이 나오는 행을 읽는다. ## 본문 `messaging-testkit` 의 `CompatibilityMatrix` 는 어댑터별 지원 등급을 코드 안의 표로 들고 있다. `Tier` 열거값은 셋이고, `ENTRIES` 는 다섯 항목이다. ## Tier 셋과 ENTRIES 다섯 :::evidence key="a19-f015-compatibilitymatrix-extension" alt="저장소 루트에서 돌린 정적 검색 출력 73줄. CompatibilityMatrix.java 의 Tier 열거값 셋이 각각의 자바독과 함께 나오고, ENTRIES 다섯 항목의 어댑터 이름과 Tier 가 이어지며 STABLE 1 · EXPERIMENTAL 4 · EXTENSION 0 으로 집계된다. messaging-spring-cloud-stream-bridge 는 main 6 파일 507 줄에 modules.json 의 runtime_memberships 가 빈 리스트이고 ENTRIES 에 이름이 0 건이다. Entry 의 간결 생성자가 어댑터 이름 공백과 브로커 버전 목록 비어 있음을 각각 IllegalArgumentException 으로 거부하는 본문이 실리고, of 가 등록되지 않은 이름을 거부하는 줄과 그것을 고정하는 시험이 나오는데 그 시험이 미등록 이름의 예로 messaging-artemis 를 넘긴다. 같은 이름이 다른 시험의 픽스처와 계약 스위트에도 나오고, CompatibilityMatrix 를 자기 파일 밖에서 쓰는 main 코드는 0 건이다. 운영자 문서에는 등급 표가 브로커 등급과 기능 등급 둘로 있고 앞쪽의 유일한 Extension 행이 Artemis/JMS, 뒤쪽 78번 줄이 Spring Cloud Stream bridge 를 Optional 로 적는다. 마지막으로 bridge 자신의 자바독과 StreamBridgePolicyGuard 의 조건 넷이 각각의 예외 코드와 함께 나온다." caption="Tier 셋과 ENTRIES 의 등급 분포 · 표 밖의 bridge leaf · Entry 가 요구하는 브로커 버전 · 미등록 이름 거부와 그 시험이 쓰는 이름 · 자기 파일 밖 main 사용 0 · 운영자 문서의 등급 표 둘 · 가드의 조건 넷과 예외 코드 — 73줄 · exit 0" zoom="true" ::: `:23`\~`:32` 의 `Tier` 는 `STABLE`, `EXPERIMENTAL`, `EXTENSION` 셋이다. `:30` 의 `EXTENSION` 에는 어댑터 SPI 전용이고 지원 집합 밖이라는 설명이 붙어 있다. `ENTRIES` 는 다섯이다. `messaging-kafka` 가 `STABLE`(`:85`), 나머지 넷 — `messaging-rabbit`(`:92`), `messaging-kafka-share-experimental`(`:97`), `messaging-pulsar-experimental`(`:104`), `messaging-nats-experimental`(`:109`) — 이 `EXPERIMENTAL` 이다. `Tier.EXTENSION` 을 쓰는 항목은 0 이다. ## ENTRIES 밖에 있는 messaging-spring-cloud-stream-bridge `messaging-spring-cloud-stream-bridge` 는 main 6 파일 507 줄이고 `ENTRIES` 에 이름이 없다. `MessagingBindingBridge:8` 의 자바독에는 상호운용 이음매이지 두 번째 메시징 API 가 아니라고 적혀 있다. `StreamBridgePolicyGuard.validate` 는 네 자리에서 거부한다 — `:30` 은 스위치가 꺼져 있으면 `STREAM_BRIDGE_DISABLED`, `:36` 은 순서 범위를 선언한 목적지를 `STREAM_BRIDGE_ORDERING_UNSUPPORTED`, `:41` 은 재시도 모드가 `NONE` 이 아니면 `STREAM_BRIDGE_RETRY_UNSUPPORTED`, `:46` 은 데드레터가 켜져 있으면 `STREAM_BRIDGE_DLQ_UNSUPPORTED` 로 던진다. 뒤 셋의 메시지는 그런 목적지가 네이티브 어댑터로 가야 한다고 적는다. `EXTENSION` 자바독은 어댑터 SPI 전용이고 지원 집합 밖이라고 적고, 이 leaf 의 자바독은 두 번째 메시징 API 가 아니라고 적는다. 둘 다 지원 집합 밖에 두는 서술인데 이 leaf 는 `ENTRIES` 에 없다. ## Entry 생성자가 브로커 버전을 요구한다 `Entry` 의 간결 생성자(`:66`\~`:74`)는 어댑터 이름이 공백이면 거부하고(`:67`), 브로커 버전 목록이 비어 있으면 어댑터는 적어도 하나의 브로커 버전을 인증해야 한다는 메시지로 `IllegalArgumentException` 을 던진다(`:70`\~`:71`). 이 leaf 는 브로커가 아니라 Spring Cloud Stream 바인더 위의 이음매다. 인증할 브로커 버전이 없으므로 지금 형태로는 `ENTRIES` 의 행이 될 수 없다. `Tier.EXTENSION` 이 비어 있는 것은 아무도 채우지 않아서만은 아니고, 그 등급 설명에 맞는 대상이 이 record 의 요구를 통과하지 못하기 때문이기도 하다. ## 미등록 이름을 of 에 넘겼을 때 `of`(`:126`)는 `ENTRIES` 에 없는 이름을 받으면 `:129` 에서 그 이름이 호환성 표에 없다는 메시지로 던진다. `CompatibilityMatrixTest:83` 의 `anUnknownAdapterIsNotSilentlyTreatedAsSupported` 가 그 동작을 고정하는데, 그 시험이 `of` 에 넘기는 미등록 이름이 `"messaging-artemis"` 다(`:84`). ## 운영자 문서의 두 등급 표 `docs/messaging/support-matrix.md` 에는 등급 표가 둘이다. `:29` 의 `## 브로커 등급` 과 `:66` 의 `## 기능 등급` 이다. 앞쪽 표에서 `Extension` 이 붙은 행은 하나뿐이고 `:37` 의 `Artemis/JMS` 다. 인증 기준은 범위 밖, Stable 기능은 adapter SPI만이라고 적혀 있다 — `Tier.EXTENSION` 자바독과 같은 내용이다. 그런데 코드의 시험은 같은 `messaging-artemis` 를 등록되지 않은 이름의 예로 쓴다. 뒤쪽 표 `:78` 에 이 leaf 가 있다. 등급 칸의 값은 `Optional` 이고, 그것은 `Tier` 에 없는 낱말이다. ## 이 표를 읽는 main 코드가 없다 `CompatibilityMatrix` 를 자기 파일 밖에서 쓰는 main 코드는 0 건이다. `of` 의 거부도 `hasLiveBrokerCertification`(`:60`)도 시험과 계약 스위트 안에서만 불린다. 등록되지 않은 이름을 조용히 지원으로 두지 않는다는 보장은 시험 경로에 대한 것이지 런타임에 대한 것이 아니다. ## 원문과 갈리는 자리 원문은 이 leaf 가 코드의 표에도 운영자 문서의 표에도 없다고 적었다. 문서 쪽은 그렇지 않다. `:78` 에 행이 있고 등급 칸이 `Optional` 로 채워져 있다. 원문이 본 것은 `:29` 의 브로커 등급 표이고, 이 leaf 는 `:66` 의 기능 등급 표에 있다. 원문은 `Tier.EXTENSION` 이 쓰이지 않는 것을 미조립의 한 사례로 묶었다. `runtime_memberships` 가 비어 있는 것은 맞지만, 이 record 에 대해서는 `Entry` 의 브로커 버전 요구가 별도의 이유로 작용한다. ## 확인하지 못한 것 JMS 계열 어댑터가 다른 모듈명으로 존재할 가능성은 좁히지 않았다. `src/messaging` 아래에 해당 디렉터리가 없다는 데까지다. `EXTENSION` 항목을 실제로 추가해 생성자에서 거부되는 것을 실행으로 보이지 않았다. 거부 조건을 코드로 읽은 데까지다. 두 등급 표의 어휘가 다른 것이 의도인지 확인하지 않았다.