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>
5.6 KiB
kind, slug, title, topic, project, status, sourceRevision, rootTreeNode, evidenceCapturedOn, body, assets, evidence, source
| kind | slug | title | topic | project | status | sourceRevision | rootTreeNode | evidenceCapturedOn | body | assets | evidence | source | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| CASE | analysis-finding-a20-f004 | 조립 경계가 정책 객체 9개를 만들고 서버를 만들지 않는다 | grpc-and-streaming | clean-architecture-backend-template | 게시 전 | 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 | case:analysis-finding-a20-f004 | 2026-09-01 | case-analysis-finding-a20-f004.body.md |
|
|
|
조립 경계가 정책 객체 9개를 만들고 서버를 만들지 않는다
플랫폼 자동 설정이 등록하는 아홉 빈은 전부 프로파일과 정책과 레지스트리다. 서버도 인터셉터 사슬도 서비스 어댑터 등록도 없다. 그것을 담당하는 타입들이 주 참조 0 이다.
관계
- 크로스 스택 게이트가 검증하는 조립은 픽스처의 조립이고 플랫폼의 조립이 아니다 다른 가족의 같은 형태다.
- 저장소 어디에도 참조가 없는 타입 3개 같은 리프의 더 나아간 사례다.
- 손으로 만드는 목록은 적어도 한 번은 거꾸로 만든다 인터셉터 사슬 클래스가 존재하는 이유다.
문제
이 플랫폼의 자동 설정이 무엇을 등록하는지 확인했다.
결론
아홉 빈이 전부 프로파일과 정책과 레지스트리다.
서버도, 인터셉터 사슬도, 서비스 어댑터 등록도 없다.
그리고 그것을 담당하는 타입들이 주 참조 0 이다. 테스트 참조는 각각 하나씩 있다.
서버 인터셉터 사슬과 두 인터셉터, 서비스 어댑터 규격, 재시도 조정자와 소유권 검증자, 스트림 승인과 직렬 기록기와 간극 탐지기와 수명 조정자, 배수 조정자와 스냅숏 서비스, 형 있는 스텁 공장과 호출 문맥이다.
인터셉터 사슬 클래스의 자바독이 자기 존재 이유를 적는다.
그 뒤집힘이 호출 지점의 목록 리터럴이 아니라 이 클래스가 존재하는 이유라는 것이다.
프레임워크의 감싸기 함수가 각 인터셉터를 이전 것 위에 감싸므로 마지막에 넘긴 것이 실행 시점에 가장 바깥이 되며, 그것은 순서가 읽히는 방식과 반대라는 것이다.
이 목록을 손으로 만드는 모든 코드베이스가 적어도 한 번은 거꾸로 만든다는 것이다.
그 클래스가 주 참조 0 이다.
판정은 P2 다.
검증 환경
Spring Boot : 4.0.8 확인 방식 : 자동 설정 빈 목록과 타입별 참조 계수 소스 수정 : x
재현 조건
원문은 final/evidence/raw/255 계열에 있다.
- 플랫폼 자동 설정의 빈 목록을 확인한다.
- 각 빈의 성격을 분류한다.
- 서버와 인터셉터 사슬과 어댑터 등록을 검색한다.
- 그 역할의 타입들을 나열하고 참조를 센다.
- 인터셉터 사슬 클래스의 자바독을 읽는다.
본문
GrpcPlatformAutoConfiguration이 등록하는 9개는 전부 프로파일·정책·레지스트리다. 서버도, 인터셉터 체인도, 서비스 어댑터 등록도 없다.
GrpcPlatformAutoConfiguration 참조 위치
:::evidence key="analysis-finding-a20-f004" alt="코드베이스에서 GrpcPlatformAutoConfiguration 를 검색한 출력 2줄. 이 기록이 세는 참조가 그 출력에 그대로 보인다." caption="GrpcPlatformAutoConfiguration 코드베이스 검색 — 2줄 · exit 0" zoom="true" :::
그것을 담당하는 타입들이 main 참조 0이다
| 타입 | leaf | 역할 (javadoc) | main | test |
|---|---|---|---|---|
GrpcServerInterceptorChain |
server | "Builds the server interceptor chain in the Stable order" | 0 | 1 |
ProtovalidateGrpcInterceptor |
policy | 요청 검증 인터셉터 | 0 | 1 |
GrpcIdempotencyInterceptor |
policy | 멱등성 인터셉터 | 0 | 1 |
GrpcServiceAdapter |
server | typed service adapter SPI | 0 | 1 |
GrpcRetryCoordinator · GrpcRetryOwnershipValidator |
policy | 재시도 소유권 | 0 | 1 |
GrpcStreamAdmission · GrpcSerializedStreamWriter · GrpcStreamGapDetector · GrpcStreamLifecycleCoordinator |
policy | 단일 writer·갭 탐지 | 0 | 1 |
GrpcDrainCoordinator · GrpcPlatformSnapshotService |
admin | drain·정책 스냅샷 | 0 | 1 |
GrpcTypedStubFactory · GrpcClientCallContext |
client | typed stub·호출 컨텍스트 | 0 | 1 |
그 클래스가 존재하는 이유가 그대로 위험이 된다
GrpcServerInterceptorChain의 javadoc — "ServerInterceptors.intercept wraps each interceptor around the previous one, so the last one passed is the outermost at runtime — the opposite of how the order reads. Every codebase that builds this list by hand gets it backwards at least once, and the symptom is an exception boundary that catches nothing." 그 클래스를 조립에서 쓰는 곳이 없으므로, 채택자가 인터셉터 목록을 직접 만들면 그 javadoc이 서술한 실수를 그대로 하게 된다.
73이라는 숫자를 그대로 결함 수로 읽으면 안 된다
260개 main 타입 중 73개가 main 참조 0이지만, build-only 라이브러리 가족에서 공개 API 표면이 내부 참조를 갖지 않는 것은 정상이다. 위 표는 그중 가족 내부의 다른 코드가 불러야 하는 조립·기계 타입만 골라낸 것이다. P2.
확인하지 못한 것
이 플랫폼을 켜고 서버가 뜨지 않는 것을 재현하지 않았다. 빈 목록상 그 결과가 나온다.