--- kind: CASE slug: analysis-finding-a06-f002 title: 가드가 막겠다는 문장이 실제로 있는 두 파일이 탐색 범위 밖이다 topic: multitenancy-isolation project: clean-architecture-backend-template status: 게시 전 sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 rootTreeNode: case:analysis-finding-a06-f002 evidenceCapturedOn: 2026-09-04 body: case-analysis-finding-a06-f002.body.md assets: - key: analysis-finding-a06-f002 file: ../../../final/evidence/rendered/analysis-finding-a06-f002.svg evidence: - ../../../final/evidence/raw/analysis-finding-a06-f002.txt source: - 원본 분석 절은 analysis/06-adapter-outbound-persistence-mongo.md §5 이다. --- # 가드가 막겠다는 문장이 실제로 있는 두 파일이 탐색 범위 밖이다 `MongoNamespaceContractTest:25`~`:26` 은 결함을 "문서가 운영자에게 폐기된 키를 쓰라고 말하는 것" 으로 정의한다. 그 가드의 탐색 범위는 두 리프 아래 `/src/main/` 경로의 `.java`·`.yml`·`.properties` 뿐이고, 그 정의에 정확히 들어맞는 `README.md:37` 과 `CLAUDE.md:25` 는 확장자로도 경로로도 걸리지 않는다. ## 관계 - **빠뜨림이 통과가 되는 게이트는 게이트가 아니다** 그 규칙이 요구하는 것을 이 가드는 자바 소스 쪽에서만 지킨다. `:37`~`:39` 가 목록이 비었으면 실패하는데, `.yml` 을 보는 쪽에는 같은 단언이 없다. - **문서와 상수가 서로 일치하는 것으로는 아무것도 증명되지 않는다** 그 규칙이 요구하는 검사 경계 명시가 이 가드에 없다. 무엇이 훑이고 무엇이 빠지는지 어디에도 적혀 있지 않아, 초록불이 저장소의 어떤 문서도 폐기된 키를 안내하지 않는다는 뜻으로 읽힌다. - **과대 진술 문서를 과소보다 먼저 고친다** 같은 안내가 `README.md:37` 과 `:53` 과 `CLAUDE.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 14~31번 줄이 실린다. 17~18번 자바독은 spring.data.mongodb 로 시작하는 키가 스프링 부트 4 메타데이터에서 오류 수준으로 폐기됐고 정본이 spring.mongodb 라고 적고, 18~22번은 런타임이 한 번도 틀린 쪽에 있지 않았으며 모든 Compose 레인이 SPRING_MONGODB_URI 를 줬는데 MongoPersistenceSettings 의 자바독이 운영자를 폐기된 키로 안내했고 자바독이 그것을 그 표류가 앉기에 가장 나쁜 자리라고 적는다고 옮긴다. 24~26번이 결함을 정의하는데 주석은 제거하고 보며 옛 네임스페이스가 폐기됐다고 기록한 문장은 결함의 반대이고 결함은 문서가 운영자에게 그것을 쓰라고 말하는 것이며, 자원 파일은 통째로 본다고 적는다. 30번이 RETIRED_NAMESPACE 상수다. 이어서 32~65번의 두 시험이 실린다. 34번 시험은 주석을 뗀 자바 소스를 훑고 37~39번이 그 목록이 비지 않았는지 먼저 단언하며, 56번 시험은 yml 과 properties 를 통째로 보는데 같은 단언이 없다. 다음으로 67~110번의 범위 코드가 실린다. 68~70번 withoutJavaComments 가 블록 주석과 줄 주석을 지우고, 72~90번 productionSources 가 73번에서 repositoryRoot 아래 src 를 루트로 잡아 74번의 두 리프를 훑으며 81번이 확장자로, 82번이 경로에 /src/main/ 이 있는지로, 83번이 /build/ 를 빼는 것으로 거른다. 100~110번 repositoryRoot 는 현재 디렉터리에서 src/config/architecture/modules.json 이 나올 때까지 부모로 올라가고 못 찾으면 107번이 예외를 던진다. 이어서 시험 소스를 뺀 그 키의 등장 자리가 나오는데 docs/superpowers 아래 계획과 증거와 설계 문서 셋, persistence-mongo 의 CLAUDE.md 와 README.md, 그리고 MongoPersistenceSettings.java 여섯이다. 그중 범위 안은 마지막 하나뿐이고 나머지 다섯은 범위 밖이다. 다음으로 MongoPersistenceSettings 5~22번이 실리는데 9~12번이 연결 URI 를 여기서 모델링하지 않고 스프링 자신의 spring.mongodb.uri 에서 읽으며 이 클래스는 모듈의 opt-in 스위치만 소유한다고 적고, 14~19번이 정본 네임스페이스와 폐기된 쪽을 대비하면서 이 자바독이 폐기된 쪽을 가리켰던 것이 왜 나쁜 자리였는지와 MongoNamespaceContractTest 가 되돌아가는 것을 막는다고 적는다. 그 키가 나오는 줄은 15번 하나이고 자바독 안이다. 이어서 운영자가 읽는 두 문서가 실린다. README.md 35~38번은 properties 코드 블록인데 36번이 모듈 스위치를 켜고 37번이 spring.data.mongodb.uri 를 mongodb://localhost:27017/portfolio 로 적으며, 52~54번은 URI 와 데이터베이스와 자격증명이 그 표준 설정을 쓴다고 적고, CLAUDE.md 24~26번도 연결 URI 와 데이터베이스가 그 설정에서 온다고 적는다. 마지막으로 시험 소스에 그 키가 나오는 자리가 실리는데, MongoNamespaceContractTest 17번과 30번은 자바독과 상수이고 MongoPersistenceConfigTest 20번과 64번은 withPropertyValues 로 spring.data.mongodb.database 를 실제로 바인딩한다." caption="가드가 결함을 정의하는 자바독 · 두 시험과 자바 소스 쪽에만 있는 자기 검증 · 범위를 만드는 코드와 저장소 루트 탐색 · 그 키가 남은 여섯 파일과 범위 판정 · 범위 안 하나가 자바독인 것 · 범위 밖 두 문서의 원문 · 시험 소스의 실제 바인딩 둘 — 162줄 · exit 0" zoom="true" ::: `:17`\~`:18` 은 `spring.data.mongodb.*` 가 스프링 부트 4 메타데이터에서 오류 수준으로 폐기됐고 정본이 `spring.mongodb.*` 라고 적는다. `:18`\~`:22` 는 런타임이 한 번도 틀린 쪽에 있지 않았다고 적는다. 모든 Compose 레인이 `SPRING_MONGODB_URI` 를 준다. 뒤처진 것은 문서 쪽이다. 스위치를 소유한 클래스를 읽은 운영자가 거기 적힌 프로퍼티를 설정하면, 고르지 않은 폐기를 자기가 고르지 않은 채 물려받는다고 자바독은 적는다. `:24`\~`:26` 이 결함을 정의한다. 주석은 제거하고 보는데, 옛 네임스페이스가 폐기됐다고 기록한 문장은 결함의 반대이기 때문이다. 결함은 문서가 운영자에게 그것을 쓰라고 말하는 것이다. 자원 파일은 통째로 보는데, YAML 안의 키는 주석일 수 없기 때문이다. ## 두 시험과 자바 소스 쪽에만 있는 자기 검증 `:34` 의 `noProductionSourceNamesTheDeprecatedNamespace` 는 주석을 뗀 자바 소스를 훑는다. `:38`\~`:39` 가 목록이 비지 않았는지 먼저 단언하는데, 아무 소스에도 닿지 않은 검색은 모든 참조를 없다고 보고하기 때문이다. `:56` 의 `noShippedResourceBindsTheDeprecatedNamespace` 는 `.yml` 과 `.properties` 를 통째로 본다. 이쪽에는 목록이 비지 않았는지 보는 단언이 없다. 자원 스캔이 0 개를 훑어도 조용히 통과한다. ## 탐색 범위 `:72`\~`:90` 의 `productionSources` 가 범위를 만든다. `:73` 이 `repositoryRoot()` 아래 `src` 를 루트로 잡고, `:74` 가 `adapter/outbound/persistence-mongo` 와 `app-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`\~`:38` 은 `properties` 코드 블록이다. `:36` 이 모듈 스위치를 켜고 `:37` 이 `spring.data.mongodb.uri=mongodb://localhost:27017/portfolio` 를 적는다. 그대로 옮겨 붙일 수 있는 완전한 한 줄이다. `:52`\~`:54` 는 URI 와 데이터베이스와 자격증명이 표준 `spring.data.mongodb.*` 설정을 쓴다고 적는다. `CLAUDE.md:24`\~`:26` 도 연결 URI 와 데이터베이스가 그 설정에서 온다고 적는다. 셋 다 폐기 사실을 기록하는 문장이 아니라 그 키를 쓰라는 안내다. ## 시험 소스에는 실제 바인딩이 둘 있다 `MongoPersistenceConfigTest:20` 이 `withPropertyValues("spring.data.mongodb.database=portfolio")` 를 부르고, `:64` 가 같은 키를 모듈 스위치와 함께 넘긴다. 산문이 아니라 실제 프로퍼티 바인딩이다. 범위가 `/src/main/` 을 요구하므로 이쪽도 두 단언에 걸리지 않는다. ## 원문에 없는 것 원문은 가드의 탐색 범위가 운영자가 읽는 두 문서를 덮지 않는다고 적는다. 여기에 더한 것은 범위 안에 남은 것이 무엇이고 범위 밖에 또 무엇이 있는지다. 범위 안의 유일한 등장 `MongoPersistenceSettings:15` 는 이미 고쳐져 기록만 남은 문장이고 `withoutJavaComments` 가 지운다. 그래서 두 단언이 지금 걸 수 있는 문장은 범위 안에 하나도 없다. 범위 밖에는 문서 둘 말고도 시험 소스의 실제 바인딩 둘이 더 있다. ## 확인하지 못한 것 그 안내를 따라 폐기된 키를 넣은 배포가 실제로 있었는지는 저장소 밖의 일이다. 확장자 조건에 마크다운을 더하면 계획 문서 셋도 걸릴 텐데, 그것을 가르는 방법은 생각해 두지 않았다. 그 계약 시험을 돌리지 않았다. 단언과 범위 코드를 읽는 데까지다.