Files
document-haness/docs/clean-architecture-backend-template/tech-log-studio/multitenancy-isolation/case/case-analysis-finding-a06-f002.md
T
DongHyeonkaandClaude Opus 5 b2963105a8 docs(keycloak-session-store): import the session-storage lab as a new project
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>
2026-09-04 22:51:59 +09:00

14 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-a06-f002 가드가 막겠다는 문장이 실제로 있는 두 파일이 탐색 범위 밖이다 multitenancy-isolation clean-architecture-backend-template 게시 전 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 case:analysis-finding-a06-f002 2026-09-04 case-analysis-finding-a06-f002.body.md
key file
analysis-finding-a06-f002 ../../../final/evidence/rendered/analysis-finding-a06-f002.svg
../../../final/evidence/raw/analysis-finding-a06-f002.txt
원본 분석 절은 analysis/06-adapter-outbound-persistence-mongo.md §5 이다.

가드가 막겠다는 문장이 실제로 있는 두 파일이 탐색 범위 밖이다

MongoNamespaceContractTest:25~:26 은 결함을 "문서가 운영자에게 폐기된 키를 쓰라고 말하는 것" 으로 정의한다. 그 가드의 탐색 범위는 두 리프 아래 /src/main/ 경로의 .java·.yml·.properties 뿐이고, 그 정의에 정확히 들어맞는 README.md:37CLAUDE.md:25 는 확장자로도 경로로도 걸리지 않는다.

관계

  • 빠뜨림이 통과가 되는 게이트는 게이트가 아니다 그 규칙이 요구하는 것을 이 가드는 자바 소스 쪽에서만 지킨다. :37~:39 가 목록이 비었으면 실패하는데, .yml 을 보는 쪽에는 같은 단언이 없다.
  • 문서와 상수가 서로 일치하는 것으로는 아무것도 증명되지 않는다 그 규칙이 요구하는 검사 경계 명시가 이 가드에 없다. 무엇이 훑이고 무엇이 빠지는지 어디에도 적혀 있지 않아, 초록불이 저장소의 어떤 문서도 폐기된 키를 안내하지 않는다는 뜻으로 읽힌다.
  • 과대 진술 문서를 과소보다 먼저 고친다 같은 안내가 README.md:37:53CLAUDE.md:25 셋에 있다. 하나만 고치면 나머지 둘이 남는다.

문제

이 모듈에는 폐기된 프로퍼티 이름의 재유입을 막는 계약 시험이 있다. 그 클래스 자바독에 무엇을 결함으로 볼지가 한 문장으로 적혀 있다.

그 정의에 해당하는 자리를 탐색 범위가 덮는지 확인했다.

결론

범위는 :72~:90 이 만든다. repositoryRoot():100~:110 이 src/config/architecture/modules.json 을 찾아 위로 올라가며 정한 루트 아래 adapter/outbound/persistence-mongo 와 app-bootstrap 두 리프를 훑고, 확장자가 맞고 경로에 /src/main/ 이 있고 /build/ 가 없는 파일만 남긴다.

시험은 둘이다. :34 가 주석을 뗀 자바 소스에 그 키가 없는지 보고, :56 이 출하되는 .yml 과 .properties 에 없는지 본다. :37~:39 는 목록이 비었으면 검색이 헛돈 것이라며 그것부터 단언한다. :56 의 자원 시험에는 같은 단언이 없다.

그 키가 있는 자리는 시험 소스를 빼면 여섯이다. 설계 계획 문서 셋과 persistence-mongo 의 CLAUDE.md 와 README.md 와 MongoPersistenceSettings.java 다.

여섯 중 자바 파일 하나만 조건을 통과한다. 그 파일에서 키가 나오는 곳은 :15 이고 자바독 안이며, 이 자바독이 예전에 그 키를 가리켰다는 기록이다. 주석을 떼고 보는 규칙이 정확히 이런 문장을 위해 있다.

훑는 대상이 없는 것은 아니다. 두 리프의 /src/main/ 아래 .java 와 app-bootstrap 의 .yml 넷을 실제로 읽고, 그 안에서 주석을 떼고 나면 걸 것이 남지 않는다.

나머지 다섯은 범위 밖이다. 그중 둘이 이 모듈의 운영자용 문서다.

README.md:37 은 properties 코드 블록 안에 그 키의 완전한 설정 한 줄을 적어 둔다. :53 과 CLAUDE.md:25 는 URI 와 데이터베이스가 그 네임스페이스에서 온다고 산문으로 적는다.

즉 자바독이 정의한 결함 — 문서가 운영자에게 폐기된 키를 쓰라고 말하는 것 — 이 지금 그 모듈의 두 문서에 그대로 있고, 그것을 막으려고 만든 가드의 범위가 그 둘에 닿지 않는다.

시험 소스에는 그 키를 산문이 아니라 실제로 바인딩하는 자리도 둘 있다. MongoPersistenceConfigTest:20 과 :64 의 withPropertyValues 다. /src/main/ 조건이 이쪽도 함께 걸러 낸다.

검증 환경

OpenJDK : 21.0.12 확인 방식 : 가드의 클래스 자바독과 두 시험과 탐색 범위 코드 인용, 폐기된 키가 있는 자리를 시험 소스를 빼고 전수 검색, 각 자리가 그 범위에 드는지 확장자와 경로로 판정, 범위 안 자바 파일에서 그 키가 놓인 줄 인용, 운영자용 두 문서의 해당 줄과 앞뒤 인용 소스 수정 : x

재현 조건

  1. 가드의 클래스 자바독에서 막으려는 결함의 정의를 인용한다.
  2. 두 시험이 각각 무엇을 보는지, 그리고 자기 검증을 어떻게 하는지 인용한다.
  3. 탐색 범위를 만드는 코드를 인용한다.
  4. 폐기된 키가 있는 자리를 시험 소스를 빼고 전부 찾는다.
  5. 각 자리가 그 범위의 확장자와 경로 조건에 드는지 판정한다.
  6. 범위 안에 든 파일에서 그 키가 놓인 줄과, 범위 밖 운영자용 문서의 해당 줄을 인용한다.

본문

MongoNamespaceContractTest 는 폐기된 Mongo 프로퍼티 이름이 되돌아오는 것을 막는다. 클래스 자바독이 그 결함을 한 문장으로 정의한다.

가드가 정의한 결함

:::evidence key="analysis-finding-a06-f002" alt="저장소 루트에서 돌린 정적 검색 출력 162줄. 먼저 MongoNamespaceContractTest 1431번 줄이 실린다. 1718번 자바독은 spring.data.mongodb 로 시작하는 키가 스프링 부트 4 메타데이터에서 오류 수준으로 폐기됐고 정본이 spring.mongodb 라고 적고, 1822번은 런타임이 한 번도 틀린 쪽에 있지 않았으며 모든 Compose 레인이 SPRING_MONGODB_URI 를 줬는데 MongoPersistenceSettings 의 자바독이 운영자를 폐기된 키로 안내했고 자바독이 그것을 그 표류가 앉기에 가장 나쁜 자리라고 적는다고 옮긴다. 2426번이 결함을 정의하는데 주석은 제거하고 보며 옛 네임스페이스가 폐기됐다고 기록한 문장은 결함의 반대이고 결함은 문서가 운영자에게 그것을 쓰라고 말하는 것이며, 자원 파일은 통째로 본다고 적는다. 30번이 RETIRED_NAMESPACE 상수다. 이어서 3265번의 두 시험이 실린다. 34번 시험은 주석을 뗀 자바 소스를 훑고 3739번이 그 목록이 비지 않았는지 먼저 단언하며, 56번 시험은 yml 과 properties 를 통째로 보는데 같은 단언이 없다. 다음으로 67110번의 범위 코드가 실린다. 6870번 withoutJavaComments 가 블록 주석과 줄 주석을 지우고, 7290번 productionSources 가 73번에서 repositoryRoot 아래 src 를 루트로 잡아 74번의 두 리프를 훑으며 81번이 확장자로, 82번이 경로에 /src/main/ 이 있는지로, 83번이 /build/ 를 빼는 것으로 거른다. 100110번 repositoryRoot 는 현재 디렉터리에서 src/config/architecture/modules.json 이 나올 때까지 부모로 올라가고 못 찾으면 107번이 예외를 던진다. 이어서 시험 소스를 뺀 그 키의 등장 자리가 나오는데 docs/superpowers 아래 계획과 증거와 설계 문서 셋, persistence-mongo 의 CLAUDE.md 와 README.md, 그리고 MongoPersistenceSettings.java 여섯이다. 그중 범위 안은 마지막 하나뿐이고 나머지 다섯은 범위 밖이다. 다음으로 MongoPersistenceSettings 522번이 실리는데 912번이 연결 URI 를 여기서 모델링하지 않고 스프링 자신의 spring.mongodb.uri 에서 읽으며 이 클래스는 모듈의 opt-in 스위치만 소유한다고 적고, 1419번이 정본 네임스페이스와 폐기된 쪽을 대비하면서 이 자바독이 폐기된 쪽을 가리켰던 것이 왜 나쁜 자리였는지와 MongoNamespaceContractTest 가 되돌아가는 것을 막는다고 적는다. 그 키가 나오는 줄은 15번 하나이고 자바독 안이다. 이어서 운영자가 읽는 두 문서가 실린다. README.md 3538번은 properties 코드 블록인데 36번이 모듈 스위치를 켜고 37번이 spring.data.mongodb.uri 를 mongodb://localhost:27017/portfolio 로 적으며, 5254번은 URI 와 데이터베이스와 자격증명이 그 표준 설정을 쓴다고 적고, CLAUDE.md 2426번도 연결 URI 와 데이터베이스가 그 설정에서 온다고 적는다. 마지막으로 시험 소스에 그 키가 나오는 자리가 실리는데, MongoNamespaceContractTest 17번과 30번은 자바독과 상수이고 MongoPersistenceConfigTest 20번과 64번은 withPropertyValues 로 spring.data.mongodb.database 를 실제로 바인딩한다." caption="가드가 결함을 정의하는 자바독 · 두 시험과 자바 소스 쪽에만 있는 자기 검증 · 범위를 만드는 코드와 저장소 루트 탐색 · 그 키가 남은 여섯 파일과 범위 판정 · 범위 안 하나가 자바독인 것 · 범위 밖 두 문서의 원문 · 시험 소스의 실제 바인딩 둘 — 162줄 · exit 0" zoom="true" :::

:17~:18spring.data.mongodb.* 가 스프링 부트 4 메타데이터에서 오류 수준으로 폐기됐고 정본이 spring.mongodb.* 라고 적는다.

:18~:22 는 런타임이 한 번도 틀린 쪽에 있지 않았다고 적는다. 모든 Compose 레인이 SPRING_MONGODB_URI 를 준다. 뒤처진 것은 문서 쪽이다. 스위치를 소유한 클래스를 읽은 운영자가 거기 적힌 프로퍼티를 설정하면, 고르지 않은 폐기를 자기가 고르지 않은 채 물려받는다고 자바독은 적는다.

:24~:26 이 결함을 정의한다. 주석은 제거하고 보는데, 옛 네임스페이스가 폐기됐다고 기록한 문장은 결함의 반대이기 때문이다. 결함은 문서가 운영자에게 그것을 쓰라고 말하는 것이다. 자원 파일은 통째로 보는데, YAML 안의 키는 주석일 수 없기 때문이다.

두 시험과 자바 소스 쪽에만 있는 자기 검증

:34noProductionSourceNamesTheDeprecatedNamespace 는 주석을 뗀 자바 소스를 훑는다. :38~:39 가 목록이 비지 않았는지 먼저 단언하는데, 아무 소스에도 닿지 않은 검색은 모든 참조를 없다고 보고하기 때문이다.

:56noShippedResourceBindsTheDeprecatedNamespace.yml.properties 를 통째로 본다. 이쪽에는 목록이 비지 않았는지 보는 단언이 없다. 자원 스캔이 0 개를 훑어도 조용히 통과한다.

탐색 범위

:72~:90productionSources 가 범위를 만든다. :73repositoryRoot() 아래 src 를 루트로 잡고, :74adapter/outbound/persistence-mongoapp-bootstrap 둘을 훑으며, :81 이 확장자로, :82 가 경로에 /src/main/ 이 있는지로, :83/build/ 를 빼는 것으로 거른다.

repositoryRoot():100~:110 은 현재 작업 디렉터리에서 src/config/architecture/modules.json 이 나올 때까지 부모로 올라간다. 못 찾으면 :107 이 예외를 던진다.

확장자는 두 시험이 넘기는 .java.yml.properties 셋이다.

그 키가 남아 있는 여섯 파일

시험 소스를 빼면 여섯이다. docs/superpowers 아래 계획·증거·설계 문서 셋, persistence-mongo/CLAUDE.md, persistence-mongo/README.md, 그리고 MongoPersistenceSettings.java 다.

범위 안에 드는 것은 마지막 하나다. 나머지 다섯은 확장자가 .md 이거나 경로에 /src/main/ 이 없다. 두 문서는 모듈 루트에 있으므로 둘 다에 해당한다.

범위 안에 든 하나는 MongoPersistenceSettings 의 자바독이다

MongoPersistenceSettings:5~:22 에서 그 키가 나오는 줄은 :15 하나다. :9~:12 가 연결 URI 를 여기서 모델링하지 않고 스프링 자신의 spring.mongodb.uri 에서 읽는다고 적고, 이 클래스는 모듈의 opt-in 스위치만 소유한다고 적는다. 자바독 안이고, 이 자바독이 폐기된 쪽을 가리켰던 과거와 그것이 왜 나쁜 자리였는지를 기록한다. :19 는 그 계약 시험이 되돌아가는 것을 막는다고도 적는다.

가드가 주석을 떼고 보는 이유가 이 문장이다.

범위 밖 두 문서는 결함의 정의에 그대로 들어맞는다

README.md:35~:38properties 코드 블록이다. :36 이 모듈 스위치를 켜고 :37spring.data.mongodb.uri=mongodb://localhost:27017/portfolio 를 적는다. 그대로 옮겨 붙일 수 있는 완전한 한 줄이다.

:52~:54 는 URI 와 데이터베이스와 자격증명이 표준 spring.data.mongodb.* 설정을 쓴다고 적는다.

CLAUDE.md:24~:26 도 연결 URI 와 데이터베이스가 그 설정에서 온다고 적는다.

셋 다 폐기 사실을 기록하는 문장이 아니라 그 키를 쓰라는 안내다.

시험 소스에는 실제 바인딩이 둘 있다

MongoPersistenceConfigTest:20withPropertyValues("spring.data.mongodb.database=portfolio") 를 부르고, :64 가 같은 키를 모듈 스위치와 함께 넘긴다.

산문이 아니라 실제 프로퍼티 바인딩이다. 범위가 /src/main/ 을 요구하므로 이쪽도 두 단언에 걸리지 않는다.

원문에 없는 것

원문은 가드의 탐색 범위가 운영자가 읽는 두 문서를 덮지 않는다고 적는다. 여기에 더한 것은 범위 안에 남은 것이 무엇이고 범위 밖에 또 무엇이 있는지다.

범위 안의 유일한 등장 MongoPersistenceSettings:15 는 이미 고쳐져 기록만 남은 문장이고 withoutJavaComments 가 지운다. 그래서 두 단언이 지금 걸 수 있는 문장은 범위 안에 하나도 없다. 범위 밖에는 문서 둘 말고도 시험 소스의 실제 바인딩 둘이 더 있다.

확인하지 못한 것

그 안내를 따라 폐기된 키를 넣은 배포가 실제로 있었는지는 저장소 밖의 일이다.

확장자 조건에 마크다운을 더하면 계획 문서 셋도 걸릴 텐데, 그것을 가르는 방법은 생각해 두지 않았다.

그 계약 시험을 돌리지 않았다. 단언과 범위 코드를 읽는 데까지다.