--- kind: CASE slug: analysis-finding-a19-f019 title: admin 스위치가 가드를 켜고 서비스는 켜지 않는다 topic: messaging-and-outbox project: clean-architecture-backend-template status: 게시 전 sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 rootTreeNode: case:analysis-finding-a19-f019 evidenceCapturedOn: 2026-09-01 body: case-analysis-finding-a19-f019.body.md assets: - key: analysis-finding-a19-f019 file: ../../../final/evidence/rendered/analysis-finding-a19-f019.svg evidence: - ../../../final/evidence/raw/analysis-finding-a19-f019.txt source: - 원본 분석 절은 analysis/19-messaging-platform.md#L975 이다. --- # admin 스위치가 가드를 켜고 서비스는 켜지 않는다 관리 스위치를 켜면 빈 넷이 만들어진다. 전부 가드와 기록과 검증기다. 그중 관리 서비스 인터페이스의 유일한 구현과 승인 검증자의 유일한 구현은 만들어지지 않는다. 부재가 문서화된 것은 하나뿐이다. ## 관계 - **관리 계약 leaf는 main 25파일 1613줄에 테스트 파일이 1개다** 같은 하위 범위의 다른 사례다. - **멱등 실행 계층과 durable-operation 표면이 픽스처에서만 조립된다** 다른 가족의 같은 형태다. - **켰다고 생각한 기능이 없다는 것을 사고 한가운데에서 발견한다** 이 사례가 그 규칙의 형태다. ## 문제 관리 스위치를 켜면 무엇이 만들어지는지 확인했다. ## 결론 빈 넷이다. 파괴적 연산 가드와 관리 연산 기록, 관리 내구성 검증기, 그리고 위상 조사기가 있을 때 합성 위상 검증기다. 만들지 않는 것이 다섯이다. 파괴적 관리자는 주 참조가 둘이고 부재가 자바독에 명시되어 있다. 기본 관리 서비스는 주 참조가 0 이고 부재 이유가 없다. 해시 기반 승인 검증자도 주 참조가 0 이고 테스트가 넷이다. 부재 이유가 없다. 위상 검증 실행체도 0 이고 이유가 없다. 재구동 서비스와 재생 서비스도 각각 주 참조가 있고 이유가 없다. 기본 관리 서비스는 관리 서비스 인터페이스의 유일한 구현이다. 즉 관리 평면을 켜도 관리 서비스가 없다. 해시 기반 승인 검증자는 승인 검증자의 유일한 구현이고, 테스트 넷이 그것을 검증한다. 위조 테스트도 포함된다. 승인된 재구동 계획과 재생 계획과 검증된 승인과 계획 요약으로 이루어진 승인 사슬 전체가 검증자 없이는 시작될 수 없다. 부재의 등급이 넷 다 다르지 않은데 문서화는 하나만 됐다. 파괴적 관리자의 부재에는 명확한 이유가 있다. 이 실행체는 관리 자격증명을 갖지 않는다는 것이다. 나머지 넷에는 이유가 적혀 있지 않고, 그중 둘은 파괴적이지 않은 관리 동작에 필요한 것이다. 재구동과 재생의 승인과 실행이다. 실패 시나리오는 이렇다. 운영 절차서에 따라 사고 대응 중 재구동을 실행하려 한다. 관리 스위치를 켠다. 부팅은 성공하고 가드와 기록과 내구성 검증기가 올라온다. 그런데 관리 서비스 빈이 없으므로 재구동을 호출할 대상이 없다. 사고 한가운데에서 켰다고 생각한 기능이 없다는 것을 발견한다. 내구성 검증기의 자바독이 경계한 상황과 정확히 같은 시점이다. 그 간극은 그 연산을 돌리게 만든 사고 도중에만 드러나며 그것이 발견하기에 가장 나쁜 순간이라는 것이다. 판정은 P2 다. ## 검증 환경 Spring Boot : 4.0.8 확인 방식 : 자동 설정 빈 목록과 타입별 참조 계수 소스 수정 : x ## 재현 조건 원문은 final/evidence/raw/252 계열에 있다. 1. 관리 자동 설정의 조건과 빈 목록을 읽는다. 2. 관리 평면에 필요한 타입 목록을 만든다. 3. 각 타입의 주 참조를 센다. 4. 각 타입의 부재가 문서화되었는지 확인한다. 5. 관리 서비스 인터페이스의 구현을 센다. ## 본문 `app.messaging.admin.enabled=true`가 만드는 bean은 넷이다 — `DestructiveOperationGuard`, `AdminOperationJournal`, `MessagingAdminDurabilityValidator`, (`BrokerTopologyInspector`가 있을 때) `CompositeTopologyValidator`. ## DestructiveOperationGuard 참조 위치 :::evidence key="analysis-finding-a19-f019" alt="코드베이스에서 DestructiveOperationGuard 를 검색한 출력 6줄. 이 기록이 세는 참조가 그 출력에 그대로 보인다." caption="DestructiveOperationGuard 코드베이스 검색 — 6줄 · exit 0" zoom="true" ::: ## 만들지 않는 것 | 타입 | leaf | main 참조 | 부재가 문서화됐는가 | |---|---|---|---| | `DestructiveMessagingAdmin` | admin-runtime | 2 | **예** — javadoc이 명시 | | `DefaultMessagingAdminService` | admin-runtime | **0** | 아니오 | | `HmacApprovalVerifier` | admin-api | **0** (test 4) | 아니오 | | `TopologyValidationRuntime` | admin-runtime | **0** | 아니오 | | `RedriveService` / `ReplayService` | admin-runtime | 2 / 1 | 아니오 | `DefaultMessagingAdminService`는 `MessagingAdminService`(인터페이스, main 참조 2)의 유일한 구현이다. 즉 admin plane을 켜도 admin 서비스가 없다. `HmacApprovalVerifier`는 `ApprovalVerifier`의 유일한 구현이고, `ApprovedRedrivePlan`/`ApprovedReplayPlan`/`VerifiedApproval`/`PlanDigest`(main 참조 10)로 이루어진 승인 사슬 전체가 **검증자 없이는 시작될 수 없다.** ## 부재의 등급이 다르지 않은데 문서화는 하나만 됐다 `DestructiveMessagingAdmin`의 부재에는 명확한 이유가 있다("이 런타임은 admin 자격 증명을 갖지 않는다"). 나머지 넷에는 이유가 적혀 있지 않고, 그중 둘은 파괴적이지 않은 admin 동작(redrive/replay의 승인·실행)에 필요한 것이다. ## 실패 시나리오 운영 절차서(`docs/messaging/retry-dlq-redrive.md`)에 따라 사고 대응 중 redrive를 실행하려 한다. `app.messaging.admin.enabled=true`로 켠다. 부팅은 성공하고 가드·journal·durability 검증기가 올라온다. 그런데 `MessagingAdminService` bean이 없으므로 redrive를 호출할 대상이 없다 — `MessagingAdminDurabilityValidator`의 javadoc이 경계한 상황("the gap only shows up during the incident the operation was run to resolve, which is **the worst possible moment to discover it**")과 정확히 같은 시점이다. P2. ## 확인하지 못한 것 관리 스위치를 켜고 재구동을 호출해 대상이 없는 것을 재현하지 않았다. 빈 목록상 그 결과가 나온다.