The keycloak project ended with four open questions that design could not
settle. A two-VM lab was built to answer them by measurement, and this is
that material: 26 experiments, 125 raw command outputs, 22 browser captures.
Follows the import procedure in README.md.
source/ the originating repository verbatim — 78 documents, 28 SVGs,
8 manifests, plus .source-revision recording the commit
final/ the SSOT
document.md 729 lines written from the 29 experiment documents, not
concatenated: what was predicted, what was measured, and
where the measurement itself was wrong
evidence/raw 125 outputs, flattened to <experiment>__<file> because
the originals collided (01-baseline.txt appeared three
times) and the audit only globs the top level
evidence/meta one per raw file; command and exitCode are null and the
README says why rather than inventing them
evidence/browser 22 captures
assets/ three diagrams through techviz
.techviz/ their VizSpecs
A separate project rather than an addition to keycloak: the B-layer answers
that project's four questions, but the A, C and D layers are about cluster
failure, SSO and operations, and one document.md should hold one subject.
The four question records there can point here through 관계.
Recorded rather than papered over: only three of the 28 diagrams were
remade. The repository forbids hand-drawn SVG and forbids titles inside the
canvas; all 28 originals carry both, so converting them is redrawing, not
reformatting. They stay in source/ and the gap is written into the document.
verify-pipeline.py passes. audit-records.py reports no issues.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
102 lines
5.0 KiB
Markdown
102 lines
5.0 KiB
Markdown
---
|
|
kind: CASE
|
|
slug: validator-declared-and-never-injected
|
|
title: 같은 자동 설정 안에서 검증기 하나만 감싸이지 않아 트랜잭션 조건이 검사되지 않는다
|
|
topic: runtime-reachability-and-composition
|
|
project: clean-architecture-backend-template
|
|
status: 게시 전
|
|
sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916
|
|
rootTreeNode: case:validator-declared-and-never-injected
|
|
evidenceCapturedOn: 2026-09-01
|
|
body: case-validator-declared-and-never-injected.body.md
|
|
assets:
|
|
- key: validator-declared-and-never-injected
|
|
file: ../../../final/evidence/rendered/validator-declared-and-never-injected.svg
|
|
evidence:
|
|
- ../../../final/evidence/raw/validator-declared-and-never-injected.txt
|
|
source:
|
|
- 원본 분석 절은 analysis/messaging/messaging-spring-boot-starter.md#L148 이다.
|
|
---
|
|
|
|
# 같은 자동 설정 안에서 검증기 하나만 감싸이지 않아 트랜잭션 조건이 검사되지 않는다
|
|
|
|
한 자동 설정이 검증기 둘을 만든다. 하나는 시작 검증 도우미로 감싸여 컨텍스트 구성 중에 돌고, 다른 하나는 빈으로 발행만 된다. 그 도우미의 자바독이 서술한 이전 결함이 정확히 그 형태다.
|
|
|
|
## 관계
|
|
|
|
- **시작 검증기가 유일한 소비자인 설정 키 넷이 아무것도 게이트하지 않는다**
|
|
같은 통독에서 나온 짝이다.
|
|
- **검증기는 발행이 아니라 주입이 강제다**
|
|
이 사례에서 뽑은 규칙이다.
|
|
- **능력 상수가 프로파일 조건보다 오래 살아서 중복 제거 없는 목적지가 가드를 통과한다**
|
|
같은 어댑터 계열의 짝이 되는 사례다.
|
|
|
|
## 문제
|
|
|
|
시작 검증 도우미가 이 가족에서 이미 한 번 고쳐진 결함을 자바독에 기록한다.
|
|
|
|
브로커 두 종과 보안 검증기가 전부 빈이었는데 아무 데도 주입되지 않았다는 것이다. 컨텍스트가 브로커마다 검증기를 발행하고 아무것도 검증하지 않았다는 것이다.
|
|
|
|
그 수정이 적용된 뒤의 상태를 확인했다.
|
|
|
|
## 결론
|
|
|
|
같은 자동 설정 안에서 하나가 빠져 있다.
|
|
|
|
브로커 프로파일 검증기는 도우미로 감싸여 초기화 콜백에서 돈다.
|
|
|
|
같은 파일의 트랜잭션 프로파일 검증기는 빈으로 발행만 된다. 대응하는 도우미 선언이 없다.
|
|
|
|
다른 브로커의 자동 설정은 검증기가 하나뿐이고 그것을 감싼다. 그러므로 이 가족에서 감싸이지 않은 검증기는 이 하나다.
|
|
|
|
그 검증기가 무엇을 막는지는 자기 자바독이 적는다.
|
|
|
|
트랜잭션 식별자 접두와 멱등 생산자와 모든 복제 확인과 수동 오프셋 커밋을 요구하고, 마지막 규칙이 핵심이다. 부수효과가 데이터베이스에 사는 목적지가 브로커 트랜잭션을 함께 선언하면, 팀이 트랜잭션이라는 낱말을 두 번 읽고 전체 경로가 원자적이라고 결론짓는다는 것이다. 두 절반은 여전히 갈라질 수 있다.
|
|
|
|
그 검증이 지금 돌지 않는다.
|
|
|
|
그리고 같은 어댑터의 능력 선언은 브로커 트랜잭션을 무조건 참으로 답한다. 두 겹이 함께 비어 있다.
|
|
|
|
여기서 리프 경계를 넘어야만 보이는 것이 하나 있다. 검증기는 어댑터 리프가 소유하고, 그것을 시작 시 부르는 배선은 스타터가 소유한다. 어느 쪽 문서도 혼자서는 이 검증이 실행되지 않는다는 것을 말할 수 없다.
|
|
|
|
## 검증 환경
|
|
|
|
Spring Boot : 4.0.8
|
|
확인 방식 : 자동 설정의 빈 선언 대조와 형제 자동 설정 비교
|
|
소스 수정 : x
|
|
|
|
## 재현 조건
|
|
|
|
원문은 document-detail 의 analysis/messaging/messaging-spring-boot-starter.md 에 있다.
|
|
|
|
1. 시작 검증 도우미의 자바독을 읽는다.
|
|
2. 브로커 자동 설정의 빈 선언을 순서대로 읽는다.
|
|
3. 각 검증기에 대응하는 도우미 선언이 있는지 확인한다.
|
|
4. 다른 브로커의 자동 설정과 비교한다.
|
|
5. 감싸이지 않은 검증기가 무엇을 요구하는지 읽는다.
|
|
|
|
## 본문
|
|
|
|
<!-- body:start -->
|
|
|
|
`KafkaProfileValidator` 는 `StartupProfileValidation` 으로 감싸여 `afterPropertiesSet` 에서 돌고, 같은 파일의 `KafkaTransactionProfileValidator` 는 빈으로 발행만 된다. Rabbit 쪽은 하나뿐인 검증기를 감싼다.
|
|
|
|
## KafkaProfileValidator 참조 위치
|
|
|
|
:::evidence key="validator-declared-and-never-injected" alt="코드베이스에서 KafkaProfileValidator 를 검색한 출력 3줄. 이 기록이 세는 참조가 그 출력에 그대로 보인다." caption="KafkaProfileValidator 코드베이스 검색 — 3줄 · exit 0" zoom="true"
|
|
:::
|
|
|
|
## 검사되지 않는 네 조건
|
|
|
|
트랜잭션 식별자 접두·멱등 생산자·`acks=all`·수동 커밋 요구가 시작 시 검사되지 않고, 어댑터는 `brokerTransaction=true` 를 무조건 답한다.
|
|
|
|
## 어느 문서도 혼자서는 이 사실을 말할 수 없다
|
|
|
|
검증기의 절반은 `messaging-kafka` 가, 배선의 절반은 스타터가 소유한다.
|
|
|
|
## 확인하지 못한 것
|
|
|
|
조건을 어긴 프로파일로 컨텍스트를 세워 검증이 돌지 않는 것을 재현하지 않았다.
|
|
|
|
<!-- body:end -->
|