Files
llm-wiki/raw/branch-notes/feature-static-analysis-quality-contract.md
T

451 lines
42 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: branch / feature-static-analysis-quality-contract
source_type: branch-note
status: raw
branch: feature-static-analysis-quality-contract
parent_branch:
related_projects: [ca-skeleton, ca-tmpl]
governing_docs: [wiki/projects/ca-tmpl/devops-ci-supply-chain-dx]
tags: [branch, ca-skeleton, ci, static-analysis, build]
created: 2026-06-15
target_merge:
status_label: review
id: BR-CA-SKELETON-OPERATIONAL-CONTRACT-059
kind: project-work-item
project: ca-skeleton-operational-contract
work_item: WI-CA-SKELETON-OPERATIONAL-CONTRACT-059
inherits: [DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-BUILD-001@1, DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-TEST-001@1]
refines: []
overrides: []
depends_on: [WI-CA-SKELETON-OPERATIONAL-CONTRACT-028, WI-CA-SKELETON-OPERATIONAL-CONTRACT-029, WI-CA-SKELETON-OPERATIONAL-CONTRACT-018]
contract_packet: 1
contract_packet_sha256: 0c95c259df15379beb8e37dc590b41edb18028dd131f37b90fd59959d8d99163
---
# branch: feature-static-analysis-quality-contract
> Layer: `raw/branch-notes/` — 단일 브랜치의 **TODO·결정·진행 기록**. 머지/종료 후 verified 결과는 `/ingest`로 `wiki/projects/`에 추출. 원본은 raw에 영구 보관.
> `status_label`: `in-progress` | `review` | `merged` | `abandoned`
> **계층 표기**: "root branch" 라는 별도 개념은 없음. project 의 직접 자식 branch 는 `parent_branch:` 를 **비워두고** `related_projects` 만 채움. 다른 branch 의 자식이면 `parent_branch: <부모 branch 이름>` 명시 + `## Parent` 섹션의 부모 wikilink 필수.
<!-- section-id: branch-parent -->
## 부모 (필수)
> 이 branch 가 어느 작업 묶음에 속하는지. 모든 branch 는 예외 없이 upward link 보유.
**Project 의 직접 자식 branch** (`parent_branch:` 비어있음):
- [[raw/project-notes/ca-skeleton-operational-contract]] — ca-tmpl 운영 계약 canonical SSOT. 본 branch 는 §35-E 신규 branch 권고 **우선순위 #2** (`Implementation Coverage Checklist` C 영역 L2051: "Static analysis / code quality baseline — tool 선택 + 룰셋") 의 전개.
선택 (인접 sibling — 경계 확정용, 본 branch 가 *침범하지 않음*):
- [[raw/branch-notes/feature-ci-quality-gates-contract]] — gate *threshold* + blocking/warning 정책 + gate 순서 owner. 본 branch 는 그 `format / lint` gate row 의 `(toolchain)` 공석을 *채우는* producer.
- [[raw/branch-notes/feature-architecture-enforcement-rules]] — ArchUnit suite owner. formatter/style lint 과 SonarQube custom rule 은 그 branch 의 **명시적 out-of-scope** → 본 branch 가 받음.
- [[raw/branch-notes/feature-build-release-supply-chain-contract]] — dependency locking / SBOM / Cosign / CVE 차단 owner. 본 branch 의 tool JAR 버전은 그 locking 메커니즘에 *편승*만 함.
- [[raw/branch-notes/feature-dependency-vulnerability-management-contract]] — CVE/license/upgrade (현재 빈 template). 본 branch 와 SpotBugs 보안 룰 vs CVE 스캔 경계 주의.
<!-- GENERATED: branch-contract:start -->
<!-- section-id: branch-contract-packet -->
## 브랜치 계약 패킷
- **생성 시 프로젝트 개정**: `1`
- **패킷 스키마**: `contract_packet: 1`
- **완료 조건**: static analysis 도구·threshold·CI failure mapping과 fixture가 명시된다
<!-- section-id: inherited-project-decisions -->
### 상속한 프로젝트 결정
| Decision Ref | Project Summary | Branch Application | Source |
|---|---|---|---|
| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-BUILD-001@1` | build tool은 Gradle Groovy DSL이다 | Work Item 완료 조건에 적용 | [[raw/project-notes/ca-skeleton-operational-contract]] |
| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-TEST-001@1` | test framework는 JUnit 5다 | Work Item 완료 조건에 적용 | [[raw/project-notes/ca-skeleton-operational-contract]] |
<!-- section-id: branch-local-decisions -->
### 브랜치 지역 결정
> 기존 branch-local 결정은 아래 `## Decision Evidence Map / 결정-근거 매핑`의 D-row가 소유하며 이 packet에서 복제하지 않는다.
| Decision ID | Decision | Relation | Supporting Claims | Status |
|---|---|---|---|---|
<!-- section-id: declared-overrides -->
### 선언한 예외
| Override ID | Overrides | Reason | Approval | Status |
|---|---|---|---|---|
<!-- GENERATED: branch-contract:end -->
<!-- section-id: branch-goal -->
## 목표
ca-tmpl skeleton 의 **정적 분석 / 코드 품질 baseline***어떤 정적 분석 도구를 채택하고 어떤 룰셋을 적용할지* 를 결정한다. 부모 §35-E 우선순위 **#2** ("코드 작성 본격화 직전" 박는 architecture-blocking 결정).
- **무엇을 막는가**: (1) 도구·룰셋이 모듈마다 제각각이라 위치 판정 불가, (2) formatter 와 style linter 가 같은 규칙을 중복 강제해 CI 가 무한 reformat 루프에 빠짐, (3) 코드 수준 보안 anti-pattern(SQL injection·weak crypto 등)이 빌드에서 새어나감, (4) 빈 `(toolchain)` gate 로 인해 lint gate 가 실제로 아무 도구도 실행하지 않음.
- **ci-quality-gates 와의 분담**: 그 branch 는 *threshold* (어느 위반이 release-blocking 인가) + gate 순서 owner. 본 branch 는 *tool 선택 + 룰셋 + Gradle wiring*. 본 branch 가 ci-quality-gates 의 `format / lint` gate row 의 `(toolchain)` 공석(literal gate-list `feature-ci-quality-gates-contract.md:194`; 동 노트의 ownership 매트릭스 `:184` 는 이미 본 branch 를 owner 로 기재)을 채우는 producer.
- 이슈: (ca-tmpl repo — 미생성)
- PR: (미생성)
<!-- section-id: branch-scope -->
## 범위
### 포함 범위
- 정적 분석 **도구 선택** (formatter / style linter / bytecode bug finder / code-level security / compile-time checker / aggregate platform 채택 여부) — D1~D7
- 각 도구의 **룰셋 내용 + config 파일 위치** (`config/checkstyle/checkstyle.xml`, `config/spotbugs/exclude.xml` 등)
- **Gradle plugin wiring** (plugin id + 버전 + 모듈 전체 적용 메커니즘 + `./gradlew check` 집계) — D8/D9
- 도구별 위반의 **blocking vs warning 채널 라우팅** (정책 *값* 은 ci-quality-gates 소유 → consume)
- **suppression / baseline 규약** (정적 분석 도구 한정 — Trivy suppression 은 ci-quality-gates 소유)
### 제외 범위
> 의도적으로 제외 — sibling branch 소유 (CLAUDE.md §11 OUT_OF_BRANCH_SCOPE). 면접에서 "이건 본 branch 범위 밖" 답변 근거.
- **coverage threshold + gate 순서 + blocking/warning 정책 *값*** → [[raw/branch-notes/feature-ci-quality-gates-contract]]
- **ArchUnit 경계 룰 + SonarQube custom rule *구현*** → [[raw/branch-notes/feature-architecture-enforcement-rules]] (그 branch 의 명시적 OOS)
- **dependency CVE/license 스캔 + SBOM + Cosign + dependency-locking *메커니즘*** → [[raw/branch-notes/feature-build-release-supply-chain-contract]] / [[raw/branch-notes/feature-dependency-vulnerability-management-contract]]
- **JaCoCo coverage 도구** → coverage 영역(ci-quality-gates) — 본 branch 미결정
- **CI job 분리/실행 시점** → ci-quality-gates 에서 최종화 (`feature-architecture-enforcement-rules.md:54` 와 동일 위임 패턴)
## 근거 (필수, 최소 1개+)
> 이 branch의 구현·설계 결정의 **근거가 되는 외부 자료**. 공식 문서·대기업 기술 블로그·강의 등 raw 자료를 인용. 같은 자료가 여러 결정의 근거면 결정 표시와 함께 여러 번 등장 가능.
| Source | 정당화하는 결정 |
|---|---|
| [[raw/official-docs/errorprone-gradle-plugin-readme]] | D5 — `net.ltgt.errorprone` 채택, Java 21에서 JDK 16+ 자동 forking + JVM args 주입 근거 (C3, C4) |
| [[raw/official-docs/sonarqube-server-versus-cloud]] | D7 — SonarQube(Server든 Cloud든) 기본 미채택 근거. Server는 self-managed 서버 설치 필요, Cloud는 외부 SaaS — 둘 다 zero-external-service 원칙과 충돌. |
| [[raw/official-docs/checkstyle-google-style-reference]] | D2 — Checkstyle naming(TypeName/MethodName)/Javadoc(MissingJavadocType/MissingJavadocMethod)/formatting(Indentation/LineLength/Whitespace) 모듈 분류 + google_checks.xml 기준 config 확인 |
| [[raw/official-docs/spotless-gradle-plugin-readme]] | D1/D9 — Spotless Gradle plugin(`com.diffplug.spotless`) 채택 + `spotlessCheck`(CI 검증) vs `spotlessApply`(자동수정) task 분리 + `googleJavaFormat` step 사용 + Gradle 7.3 / JRE 17 최소 요건 확인 |
| [[raw/official-docs/spotbugs-gradle-plugin-docs]] | D3/D4/D9 — SpotBugs Gradle Plugin 채택, `spotbugsPlugins` 로 FindSecBugs 연동, `./gradlew check` 자동 집계 (C1~C5) |
| [[raw/official-docs/find-sec-bugs-official]] | D4 — FindSecBugs(SpotBugs 보안 플러그인) 채택. 144개 취약점 유형·826+ API 시그니처 탐지, OWASP Top 10/CWE 연계, Maven/IDE/CI 통합 공식 확인. |
| [[raw/official-docs/google-java-format-readme]] | D1 — google-java-format 공식 scope(formatting 전용, naming 등 미포함), Java 21 최소 버전 요건, zero-configurability 설계 결정, JDK 16+ --add-exports JVM flag 요건 원문 확인. |
근거 자료가 raw에 아직 없다면 먼저 `raw-source-template` 또는 `lecture-note-template` 으로 raw에 등록한 뒤 여기서 링크.
## TODO
각 항목 옆에 증거 등급 표기: `actually-implemented` | `locally-verified` | `prod-verified` | `documented-only` | `planned` | `needs-confirmation`
- [x] Spotless + google-java-format Gradle wiring (`subprojects {}` + `spotlessCheck`/`spotlessApply`) — 등급: `locally-verified` (8.6.0 + GJF 1.35.0, `spotlessApply` 로 654 파일 일괄 포맷, `spotlessCheck` green)
- [x] Checkstyle custom minimal ruleset (`config/checkstyle/checkstyle.xml` + suppressions) — 등급: `locally-verified` (13.5.0; naming/logical error-tier, formatting/import-order 모듈 제거, Javadoc warning-tier, `log`/`SELF` 관용구 보정)
- [x] SpotBugs + FindSecBugs wiring (`config/spotbugs/exclude.xml`) — 등급: `locally-verified` (6.5.6/core 4.10.2 + FSB 1.14.0; `reportLevel='high'`; commons-lang3 BOM 충돌 해소 후 분석 정상; CSRF false-positive exclude)
- [x] ErrorProne wiring (`net.ltgt.errorprone` + `error_prone_core`) — 등급: `locally-verified` (5.1.0 + core 2.49.0; main 무오류, test 4건 실수정 후 compileJava/compileTestJava green)
- [x] Gradle 9.0.0 + Java 21 에서 4개 도구 plugin 버전 호환 smoke 검증 (`./gradlew check`) — 등급: `locally-verified` (`./gradlew check` BUILD SUCCESSFUL, 10모듈 도구+테스트+Testcontainers; gate-bites 음성테스트 확인)
- [x] (선택) SonarQube opt-in 문서 (`docs/optional/sonarqube-integration.md`) — 등급: `documented-only` (작성 완료; 단 `/docs` 는 gitignore 라 로컬 전용 — 커밋 비포함)
- [ ] 머지 시 ci-quality-gates `format / lint` gate row `(toolchain)``feature-static-analysis-quality-contract` 충원 (역참조 전파) — 등급: `planned` (merge-time follow-up — 본 구현 범위 밖)
## 진행 중 메모
- ca-tmpl 은 정적 분석 도구가 **전무한 greenfield** (Explore 확인: spotless/checkstyle/spotbugs/errorprone/pmd/sonar/jacoco 0). 본 branch 는 신규 도입(마이그레이션 아님).
- 실제 stack: Java 21 / Gradle **9.0.0** / Spring Boot **3.5.15** (project §34 는 3.5.14 기재 — 경미한 drift, §Audit 참조). 모든 plugin 버전 선택이 Gradle 9.0.0 기준 → 호환은 §Claims To Verify 로 실측.
- 도구 선택 철학: §34 single-stack minimalism + §2 무외부의존 → **로컬·infra-free·비중복** 도구만. 중복 도구(PMD)·외부 서비스(Sonar)는 기본 배제.
## 결정 사항
> 추후 면접/회고에서 "왜 이렇게 했나" 답할 근거. 대안과 함께 기록. 상세 매핑은 ↓ Decision Evidence Map.
- 2026-06-15: **D1 formatter = Spotless 8.6.0 + google-java-format 1.35.0** / 이유: 결정론적 zero-config 포맷 + `spotlessApply` 자동수정 / 대안: palantir-java-format(Spotless API 호환 위험), Eclipse JDT(custom XML overhead), Checkstyle-only(자동수정 없음) / 근거: [[raw/official-docs/google-java-format-readme]], [[raw/official-docs/spotless-gradle-plugin-readme]]
- 2026-06-15: **D2 style linter = Checkstyle 13.5.0 (custom minimal ruleset)** / 이유: formatter 가 못 하는 naming·Javadoc·logical 강제, formatting 모듈은 formatter 와 중복이라 suppress / 대안: google_checks.xml 그대로(포맷 충돌 #6527), sun_checks.xml(obsolete) / 근거: [[raw/official-docs/checkstyle-google-style-reference]], [[raw/official-docs/google-java-format-readme]]
- 2026-06-15: **D3 bytecode bug finder = SpotBugs 6.5.6 (core 4.10.2)** / 이유: 바이트코드 데이터플로우 null/resource/equals 버그 탐지 / 대안: 미채택 시 ErrorProne 단독 / 근거: [[raw/official-docs/spotbugs-gradle-plugin-docs]]
- 2026-06-15: **D4 code-level security = FindSecBugs 1.14.0 (SpotBugs plugin)** / 이유: SQL injection·weak crypto 등 코드 수준 보안 anti-pattern 탐지(타 도구 미커버), CVE 스캔과 구분 / 대안: 전문 SAST 위임 / 근거: [[raw/official-docs/find-sec-bugs-official]]
- 2026-06-15: **D5 compile-time checker = ErrorProne (net.ltgt.errorprone 5.1.0 + core 2.49.0)** / 이유: 컴파일 타임 correctness/swapped-arg/MissingOverride 즉시 강제 / 대안: 빌드 속도 제약 시 생략 / 근거: [[raw/official-docs/errorprone-gradle-plugin-readme]]
- 2026-06-15: **D6 PMD 미채택** / 이유: SpotBugs+ErrorProne 과 중복 크고 domain-less skeleton 에서 복잡도/CPD 가치 낮음·보안 미커버 / 대안: 복잡도 계약 요구 시 재검토 / 근거: 비교 합성(외부 vendor "미사용" 권고 부재 — `UNSUPPORTED_DECISION`)
- 2026-06-15: **D7 SonarQube 미채택(기본 skip) + optional opt-in** / 이유: Server/Cloud 모두 외부 서비스 전제 → §2 무외부의존 위반; 로컬 plugin 으로 `./gradlew check` 완결 / 대안: 조직이 Sonar 서버 보유 시 opt-in profile / 근거: [[raw/official-docs/sonarqube-server-versus-cloud]]
- 2026-06-15: **D8 Gradle wiring = 기존 루트 `subprojects {}` 확장** / 이유: ca-tmpl 현행 build.gradle 이 이미 `subprojects {}` 사용 → 일관성 / 대안: build-logic convention plugin(모듈 급증 시) / 근거: ca-tmpl `src/build.gradle:15-52` ground truth (`UNSUPPORTED_IMPL_DECISION` — 메커니즘 선택은 임의 trade-off)
- 2026-06-15: **D9 `./gradlew check` 단일 집계 + blocking; ci-quality-gates `(toolchain)` 충원** / 이유: 각 plugin 이 check 에 자동 연결, lint gate 가 실제 도구 실행 / 대안: 별도 task 수동 호출 / 근거: [[raw/official-docs/spotless-gradle-plugin-readme]], [[raw/official-docs/spotbugs-gradle-plugin-docs]] + cross [[raw/branch-notes/feature-ci-quality-gates-contract]] D1/D4
## 결정-근거 매핑
> 각 결정이 어떤 raw source claim 으로 뒷받침되는지 명시한다.
> `Decision ID` 는 이 branch-note 안에서 안정적으로 유지한다. 예: `D1`, `D2`.
> `Supporting Claims` 는 `raw/<category>/<slug>.md#C1` 형식으로 연결한다.
> `선택 조건` 열(R2): "이 조건일 때 이 결정, 다른 조건이면 어떤 대안". 분기 없으면 `N/A`.
| Decision ID | Decision | 선택 조건 (언제 이 결정 / 언제 대안) | Supporting Claims | Evidence Strength | Open Risk |
|---|---|---|---|---|---|
| D1 | formatter = Spotless 8.6.0 + google-java-format 1.35.0 | 결정론적 포맷 + 100-char 수용 가능 → 이 결정 / 120-char 팀 표준이면 palantir(단 Spotless API 호환 확인 필수) | `raw/official-docs/google-java-format-readme.md#GJF-README-C2`, `raw/official-docs/google-java-format-readme.md#GJF-README-C3`, `raw/official-docs/spotless-gradle-plugin-readme.md#SPOTLESS-GRADLE-C4`, `raw/official-docs/spotless-gradle-plugin-readme.md#SPOTLESS-GRADLE-C5` | `official-vendor-doc` | google-java-format 1.35.0 + Spotless 8.6.0 의 Gradle 9.0.0 무결 동작 미검증(docs는 Gradle 7.3+/JRE17+ 최소만) → Claims To Verify |
| D2 | style linter = Checkstyle 13.5.0 (custom minimal ruleset: naming+Javadoc+logical, formatting 모듈 suppress) | naming/Javadoc 강제가 skeleton 계약 범위 → 이 결정 / pure formatting 만이면 Checkstyle 생략(D1 단독) | `raw/official-docs/checkstyle-google-style-reference.md#C2`, `raw/official-docs/checkstyle-google-style-reference.md#C3`, `raw/official-docs/checkstyle-google-style-reference.md#C4`, `raw/official-docs/checkstyle-google-style-reference.md#C1`, `raw/official-docs/google-java-format-readme.md#GJF-README-C1` | `official-vendor-doc` | google_checks.xml 직접 사용 시 formatter 충돌(#6527); importOrder↔CustomImportOrder 동기화 누락 시 CI 무한 reformat 루프 |
| D3 | bytecode bug finder = SpotBugs 6.5.6 (toolVersion core 4.10.2) | 항상(baseline) | `raw/official-docs/spotbugs-gradle-plugin-docs.md#SPOTBUGS-GRADLE-C1`, `raw/official-docs/spotbugs-gradle-plugin-docs.md#SPOTBUGS-GRADLE-C3`, `raw/official-docs/spotbugs-gradle-plugin-docs.md#SPOTBUGS-GRADLE-C5` | `official-vendor-doc` | docs는 "Gradle v7.0+"만 명시(Gradle 9.0.0 직접 미언급) → plugin 6.5.6 의 Gradle 9 동작 실측 필요 |
| D4 | code-level security = FindSecBugs 1.14.0 (spotbugsPlugins) | 코드 수준 OWASP 보안을 CI 에서 잡을 때 → 이 결정 / 전문 SAST 완전 위임 시 생략 가능 | `raw/official-docs/find-sec-bugs-official.md#C1`, `raw/official-docs/find-sec-bugs-official.md#C2`, `raw/official-docs/find-sec-bugs-official.md#C5`, `raw/official-docs/spotbugs-gradle-plugin-docs.md#SPOTBUGS-GRADLE-C4` | `official-vendor-doc` | FSB docs는 Gradle 통합 직접 미명시(C4=Maven/IDE만) → spotbugsPlugins 경유 Gradle 적용 실측 필요; CVE 스캔(sibling)과 경계 유지 |
| D5 | compile-time checker = ErrorProne (net.ltgt.errorprone 5.1.0 + error_prone_core 2.49.0) | correctness/null 즉시 강제 필요 → 이 결정 / 빌드 속도 절대 제약이면 생략(SpotBugs 단독) | `raw/official-docs/errorprone-gradle-plugin-readme.md#C3`, `raw/official-docs/errorprone-gradle-plugin-readme.md#C4`, `raw/official-docs/errorprone-gradle-plugin-readme.md#C2`, `raw/official-docs/errorprone-gradle-plugin-readme.md#C1` | `official-vendor-doc` | README는 min Gradle 6.8 만 명시; plugin 5.1.0 + core 2.49.0 의 Gradle 9.0.0 fork compiler 정상 동작 실측 필요 |
| D6 | PMD 미채택 | SpotBugs+ErrorProne 기채택 + domain-less skeleton → 제외 / 복잡도·CPD 가 계약 요구되면 재검토 | (없음 — 비교 합성, vendor "미사용" 권고 부재) | `research-synthesis`**UNSUPPORTED_DECISION** (외부 근거 없는 임의 trade-off: 중복성·skeleton 규모 판단) | PMD CPD/복잡도 메트릭이 나중에 필요해지면 재평가 |
| D7 | SonarQube 미채택(기본 skip) + optional opt-in 문서 | skeleton zero-external-service 원칙 고수 → skip / 조직이 Sonar 서버 보유 시 opt-in profile | `raw/official-docs/sonarqube-server-versus-cloud.md#C1`, `raw/official-docs/sonarqube-server-versus-cloud.md#C2` (+ project §2/§34 무외부의존 연결 논리) | `official-vendor-doc(배포모델) + project-ssot` | Sonar Gradle 9 + 9-module classpath drop 위험(opt-in 활성화 시); SonarJava 고유 dataflow 룰 일부 미커버 |
| D8 | Gradle wiring = 기존 루트 `subprojects {}` 블록 확장 (신규 build-logic convention plugin 미도입) | ca-tmpl 현행 build.gradle 이 이미 `subprojects {}` + `tasks.named('check')` 집계 사용 → 일관성 / 모듈 급증 시 convention plugin 재검토 | ca-tmpl ground truth `src/build.gradle:15-52` (`subprojects {}` apply 패턴 + `tasks.named('check')` 집계) | `ground-truth-code`**UNSUPPORTED_IMPL_DECISION** (메커니즘 선택은 임의 trade-off; convention plugin 이 Gradle 9 에선 더 idiomatic) | 빌드 복잡도 증가 시 convention plugin 마이그레이션 부담 |
| D9 | `./gradlew check` 단일 집계 + blocking; ci-quality-gates `(toolchain)` 공석 충원 | 항상 | `raw/official-docs/spotless-gradle-plugin-readme.md#SPOTLESS-GRADLE-C3`, `raw/official-docs/spotbugs-gradle-plugin-docs.md#SPOTBUGS-GRADLE-C1` (+ cross [[raw/branch-notes/feature-ci-quality-gates-contract]] D1/D4) | `official-vendor-doc + cross-contract` | 최종 blocking/warning *정책 값* 은 ci-quality-gates 소유 → 본 branch 는 채널 라우팅만, 정책 변경 시 재평가 |
## 구현 가이드
> *결정 (Decisions)* 이 "*무엇* 을 할 것인가" 라면, 본 §는 "*어디에 어떻게* 구현될 것인가" 의 사전 명세 — *문서가 모호해서 구현자가 임의로 정해야 했던 결정* 카탈로그. 작성 목표는 다음 구현자가 *되묻지 않아도 코드를 작성할 수 있는 수준*.
>
> **본 §는 일률적 anchor list 를 강제하지 않는다.** branch 마다 구현 내용·범위가 다르므로 sub-section 은 *이 branch 의 결정과 근거에서 도출되는 것만* 작성. 어떤 branch 는 error mapping 표 + 정적 강제 카탈로그, 어떤 branch 는 migration 단계 + wiring, 어떤 branch 는 sequence + state machine. 형식 예시는 [[raw/branch-notes/feature-boundary-validation-mapping-contract]] 의 §구현 가이드 참조.
>
> **3-rule meta principle (필수 준수)**:
>
> 1. **R1. Reference 필수** — 각 sub-section / row / cell 은 본 branch 의 `Decision ID` (예: D1, D2) + 그 결정의 `Supporting Claim ID` (예: `RAW-SLUG-C1`) 를 reference. *근거 없는 결정 금지* — 모든 구현 detail 은 결정 + 근거의 *도출* 이어야 함.
> 2. **R2. UNSUPPORTED_IMPL_DECISION 명시** — 근거 raw 가 *원칙* 만 권고하고 *detail* (메커니즘 선택 / 클래스/rule 명명 / glob 패턴 / algorithm / factory API 모양 등) 은 권고하지 않는 cell 은 `UNSUPPORTED_IMPL_DECISION` 라벨 + 사용자 trade-off 근거 한 줄. 이게 *근거 있는 결정 vs 사용자 임의 trade-off* 의 경계.
> 3. **R3. OUT_OF_BRANCH_SCOPE 정제** — 본 branch 결정 범위 밖 cell 은 §구현 가이드에 *남기지 않음*. 별도 branch 또는 canonical SSOT 로 이관 (이관 history 는 별도 § "Audit & Findings" 등에 보존). 도메인 특화 detail (ca-tmpl skeleton 범위 밖) 도 동일하게 정제.
>
> **각 sub-section 의 권장 헤더 패턴**:
>
> ```markdown
> ### N. <sub-section 제목>
>
> > **Trace**: <In-scope row 들 + Decision ID + Supporting Claim ID 의 매핑 (한 줄/한 단락)>
> >
> > - **UNSUPPORTED_IMPL_DECISION**: <근거 없는 사용자 임의 결정 항목들 + 각각의 trade-off 근거 한 줄>
>
> <표 또는 명확한 구조 — 자유 텍스트 = 모호함 = 되묻기 원인>
> ```
### 1. Gradle plugin 적용 (루트 build.gradle + 기존 subprojects 블록)
> **Trace**: D1(`GJF-README-C2/C3`,`SPOTLESS-GRADLE-C4`) · D3(`SPOTBUGS-GRADLE-C3`) · D4(`SPOTBUGS-GRADLE-C4`) · D5(`errorprone-...#C3/C4/C5`) · D8(ca-tmpl `src/build.gradle:15-52`). project §34 `apply false` 패턴 상속.
>
> - **UNSUPPORTED_IMPL_DECISION**: plugin 버전 핀(8.6.0 / 6.5.6 / 5.1.0) + `effort='max'` / `reportLevel='high'` 는 docs 가 원칙만 제시하고 skeleton-specific 값은 권고 없음 → 사용자 trade-off(엄격도↑ vs 빌드시간/false-positive↑). 기본값(`'default'`)도 기능상 유효.
```groovy
// 루트 build.gradle plugins 블록 (apply false — 기존 §34 패턴)
plugins {
id 'com.diffplug.spotless' version '8.6.0' apply false // D1
id 'com.github.spotbugs' version '6.5.6' apply false // D3
id 'net.ltgt.errorprone' version '5.1.0' apply false // D5
}
// 기존 subprojects {} (src/build.gradle:15-52)에 추가 — D8
subprojects {
apply plugin: 'com.diffplug.spotless'
apply plugin: 'checkstyle' // Gradle 내장 — plugins{} 선언 불요 (D2)
apply plugin: 'com.github.spotbugs'
apply plugin: 'net.ltgt.errorprone'
spotless { java { // D1
googleJavaFormat('1.35.0')
importOrder()
removeUnusedImports()
} }
checkstyle { // D2
toolVersion = '13.5.0' // ※ Gradle 기본 toolVersion 은 구버전 → 명시 필수
configFile = rootProject.file('config/checkstyle/checkstyle.xml')
configDirectory = rootProject.file('config/checkstyle')
ignoreFailures = false
maxWarnings = 0
}
spotbugs { // D3
toolVersion = '4.10.2'
excludeFilter = rootProject.file('config/spotbugs/exclude.xml')
// effort / reportLevel: UNSUPPORTED_IMPL_DECISION (위 참조)
}
dependencies {
spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.14.0' // D4
errorprone 'com.google.errorprone:error_prone_core:2.49.0' // D5
}
tasks.withType(JavaCompile).configureEach {
options.errorprone { disableWarningsInGeneratedCode = true } // D5 (errorprone-...#C5)
}
}
```
> **ErrorProne Java 21 JVM args 수동 설정 금지** (`errorprone-...#C4`): plugin 5.1.0 은 JDK 16+ 감지 시 forking compiler + 필요한 `--add-exports`/`--add-opens` 를 자동 주입한다. `org.gradle.jvmargs` 에 수동 추가 시 중복/충돌. 단 Gradle daemon 자체가 Java 21 toolchain 으로 컴파일하는지만 확인.
### 2. Config 파일 레이아웃
> **Trace**: D2(checkstyle config) · D3/D4(spotbugs exclude). 본 branch 의 결정 산출물 위치.
>
> - **UNSUPPORTED_IMPL_DECISION**: `config/<tool>/` 경로는 Gradle Checkstyle 관행 차용 — SpotBugs docs 는 자동탐색 없음. 다른 경로도 기능 동등(사용자 trade-off: 관행 일관성 vs 자유).
```text
config/
checkstyle/
checkstyle.xml ← KEEP: naming + Javadoc + logical 모듈만 (CS-C2/C3)
checkstyle-suppressions.xml ← SUPPRESS: formatter 소유 모듈 (CS-C4) — §3 카탈로그
spotbugs/
exclude.xml ← SpotBugs + FindSecBugs false-positive exclude filter
```
### 3. Checkstyle ruleset 카탈로그 (KEEP vs SUPPRESS)
> **Trace**: D2 + `checkstyle-...#C2/C3/C4/C5` + `GJF-README-C1`(formatter scope 는 formatting 한정, naming 미강제 → Checkstyle 잔존 이유). formatter(D1)와 중복 모듈을 suppress 해야 무한 reformat 루프(§엣지) 방지.
>
> - **UNSUPPORTED_IMPL_DECISION**: KEEP/SUPPRESS 각 모듈의 *세부 파라미터*(예: `LineLength` 100 vs `MissingJavadocMethod` 의 `scope`/test 예외)는 docs 가 모듈 존재만 확인하고 값은 미권고 → 사용자 trade-off. 아래는 권고 기본선.
| 분류 | 모듈(예) | 처리 | 근거 |
|---|---|---|---|
| naming | `TypeName`, `MethodName`, `ConstantName`, `ParameterName`, `LocalVariableName`, `LambdaParameterName` … | **KEEP** (blocking) | `CS-C2`, `CS-C5` |
| Javadoc | `MissingJavadocType`, `MissingJavadocMethod`, `NonEmptyAtclauseDescription` … | **KEEP** (main: blocking, test: warning) | `CS-C3` |
| logical/design | `NeedBraces`, `FallThrough`, `EmptyCatchBlock`, `OneStatementPerLine`, `MissingSwitchDefault` | **KEEP** | google_checks.xml(`CS-C1`) |
| formatting | `Indentation`, `LineLength`, `WhitespaceAround`, `LeftCurly`/`RightCurly`, `SeparatorWrap`, `OperatorWrap`, `EmptyLineSeparator` | **SUPPRESS** (formatter 소유) | `CS-C4` (#6527 충돌) |
| import order | `CustomImportOrder` | **SUPPRESS** (Spotless `importOrder()` 단독 소유) | D1 + `SPOTLESS-GRADLE-C1` (spotless{} 포매터 step 구성) |
### 4. 위반 → blocking
> **Trace**: D9 + cross [[raw/branch-notes/feature-ci-quality-gates-contract]] D1(contract violation=blocking)/D4(warning-only 경계). 본 branch 는 *라우팅*만; 최종 정책 *값* 은 ci-quality-gates 소유.
>
> - **UNSUPPORTED_IMPL_DECISION**: 아래 채널 배정은 ci-quality-gates 정책의 *예상 적용* — 그 branch 가 최종 확정. test source set warning 시작 여부는 사용자 trade-off.
| 도구 task | 채널 | 비고 |
|---|---|---|
| `spotlessCheck` (포맷 diff) | **blocking** | CI 는 `spotlessApply` 절대 실행 금지(파일 mutate) — `spotlessCheck` 만 |
| `checkstyleMain` | **blocking** (`ignoreFailures=false`, `maxWarnings=0`) | naming/Javadoc 위반 = skeleton 계약 위반 |
| `checkstyleTest` | **warning** 시작 → 추후 승급 | 테스트 헬퍼 Javadoc 예외 |
| `compileJava` (ErrorProne) | **blocking** (컴파일 오류) | 별도 설정 불요 |
| `spotbugsMain` (+ FindSecBugs) | **blocking** (high priority) | `reportLevel`/severity 정책은 ci-quality-gates |
### 5. SonarQube opt-in (기본 미적용)
> **Trace**: D7 + `sonarqube-...#C1/C2`(Server/Cloud 모두 외부 서비스). 기본 build.gradle 에 Sonar plugin **미포함**.
>
> - **UNSUPPORTED_IMPL_DECISION**: opt-in 제공 *형식*(주석 build.gradle vs 별도 `docs/optional/`)은 docs 무관 사용자 선택. 아래는 권고.
```text
docs/optional/sonarqube-integration.md ← Sonar 서버 보유 팀용 opt-in 가이드 (plugin id org.sonarqube + host.url/token)
```
기본 `./gradlew check` 는 Sonar 분석을 포함하지 않으며 외부 연결 없이 완결된다(D7).
## 엣지·실패·의존
> R4(깊이 게이트) 캡처용. 정상 경로 외에 *구현 중 부딪힐* 실패/엣지/다른 계약 의존을 미리 열거. 없으면 "해당 없음" 명시(공란 금지).
- **실패·엣지 경로**:
- *formatter ↔ linter 충돌*: google_checks.xml 직접 사용 시 `Indentation`/`LineLength` 등 formatter 가 고친 코드를 Checkstyle 이 reject → CI 무한 reformat. **기대 동작**: custom ruleset 이 formatter 소유 모듈 suppress(D2 / `CS-C4` / §구현 가이드 §3).
- *import order 동기화*: Spotless `importOrder()` ↔ Checkstyle `CustomImportOrder` 불일치 → 영구 CI 루프. **기대 동작**: Checkstyle 에서 import-order 검사 제거(Spotless 단독 소유).
- *생성 코드 false positive*: MapStruct/Lombok 생성물에 ErrorProne 경고 → `disableWarningsInGeneratedCode=true`(`errorprone-...#C5`).
- *Gradle 9 + Java 21 plugin 호환*: 4개 plugin 버전이 Gradle 9.0.0 에서 미검증 → 빌드 실패 가능. **기대 동작**: smoke 검증 후 버전 핀(§Claims To Verify).
- *FSB false positive*: taint 분석 inter-procedural 한계 → `config/spotbugs/exclude.xml` 로 관리.
- **다른 계약 의존**:
- [[raw/branch-notes/feature-ci-quality-gates-contract]] D1(contract violation=blocking) + D4(warning-only 경계)에 의존 — 본 branch 는 도구 위반을 그 정책 채널로 *라우팅*만. 그 정책 변경 시 본 branch 의 blocking 매핑 재평가. **역방향**: 그 branch 의 ownership 매트릭스(`:184`/§Coverage `:287`)는 이미 `(toolchain)`→본 branch 로 매핑됨; literal gate-list row `:194``(toolchain)` token 잔존(cosmetic) — 본 branch 머지 시 정합.
- [[raw/branch-notes/feature-build-release-supply-chain-contract]] D8(Gradle dependency-locking)에 의존 — 도구 JAR 버전이 `gradle/locks/*.lockfile` 에 포함되어야 함(`./gradlew dependencies --write-locks`). 본 branch 는 *버전 값*만 정하고 locking *메커니즘*은 그 branch 소유.
- [[raw/branch-notes/feature-architecture-enforcement-rules]] — ArchUnit suite 는 그 branch 소유. 본 branch 는 ArchUnit rule 추가 안 함(OOS).
## 검증해야 할 주장
> 공식 문서나 사례는 근거지만, 내 프로젝트에서의 동작을 자동으로 보장하지 않는다.
> 구현 전/중/후에 실제로 검증해야 하는 주장을 분리한다.
| Claim | Why uncertain | How to verify | Status |
|---|---|---|---|
| Spotless 8.6.0 + google-java-format 1.35.0 이 Gradle 9.0.0 + Java 21 에서 무결 동작 | docs는 Gradle 7.3+/JRE17+ 최소만 명시, Gradle 9 직접 미검증 | `./gradlew spotlessCheck` 실행 후 오류 0 | `locally-verified``spotlessApply` 654파일 포맷 후 `spotlessCheck` green (2026-06-20) |
| SpotBugs plugin 6.5.6(core 4.10.2) + FindSecBugs 1.14.0 이 Gradle 9.0.0 에서 분석 성공 | docs는 "Gradle v7.0+"만 명시(`SPOTBUGS-GRADLE-C5`); FSB는 Gradle 통합 직접 미언급(`find-sec-bugs-...#C4`) | `./gradlew check` → spotbugsMain 리포트 생성 + FSB 룰 동작 확인 | `locally-verified` — 단 Boot BOM 이 commons-lang3 를 3.17.0 으로 강등 → SpotBugs 4.10.2 가 `org.apache.commons.lang3.Strings`(3.18.0+) 부재로 crash. `ext['commons-lang3.version']='3.20.0'` override 로 해소(`force` 는 dependency-management 가 덮어써 무효). FSB 동작 확인(SPRING_CSRF_PROTECTION_DISABLED 탐지). 상세: [[raw/errors/spotbugs-commons-lang3-bom-downgrade-noclassdef-2026-06-20]] |
| ErrorProne plugin 5.1.0 + core 2.49.0 이 Gradle 9.0.0 + Java 21 fork compiler 정상 | README는 min Gradle 6.8 만 명시; 5.1.0 의 Gradle 9 호환 실측 필요 | `./gradlew compileJava` 오류 없이 통과 + ErrorProne 룰 적용 확인 | `locally-verified` — main 무오류; test 4건(CheckReturnValue×3, DoubleBraceInitialization×1) 실수정 후 compileJava/compileTestJava green |
| custom checkstyle.xml + suppressions 가 formatter 와 충돌 없이 동작(무한 reformat 루프 없음) | google_checks.xml 직접 사용은 #6527 충돌 — custom suppress 완전성 미검증 | `./gradlew spotlessApply && ./gradlew checkstyleMain` 연속 실행 시 위반 0 | `locally-verified` — formatting/import-order 모듈 제거; `spotlessApply``checkstyleMain` error 0(naming/logical), Javadoc 만 warning |
| `./gradlew check` 가 4개 도구 task 를 모두 집계 + 위반 시 non-zero exit | 각 plugin 이 check 에 자동 연결되나 조합 동작 미검증 | 의도적 위반 fixture 주입 후 `./gradlew check` exit code ≠ 0 확인 | `locally-verified``:module:check` dry-run 에 4개 도구 task 집계 확인; 의도적 위반(나쁜 포맷 + `Bad_Method_Name`) 주입 시 spotlessCheck/checkstyleMain BUILD FAILED 확인 후 원복 |
| Sonar opt-in 구성이 Gradle 9.0.0 + 9-module 에서 classpath drop 없이 분석 | sonar-scanner-gradle 7.0 공지가 "complex multi-module → major drop" 경고 | opt-in 활성화 후 `./gradlew sonar` 이슈 수 비교 | `planned` — 기본 미적용(opt-in 문서만), 본 branch 미검증 |
## Audit & Findings
> ground-truth(ca-tmpl 실제 코드/registry) 대조에서 발견한 drift. 본 branch 결정 영역 밖 항목은 *정합 권고만* (자동 rewrite 금지). 비차단.
- **GREENFIELD**: ca-tmpl 에 정적 분석 도구 전무(Explore 확인: settings.gradle 9-module 어디에도 spotless/checkstyle/spotbugs/errorprone/pmd/sonar/jacoco 없음). 본 branch = 신규 도입(마이그레이션 아님).
- **STACK_DRIFT (비차단)**: project §34 Stack Matrix = Spring Boot **3.5.14**, 실제 `ca-tmpl/src/build.gradle:2` = **3.5.15**. 정적 분석 도구는 Boot 버전 비의존이라 본 결정 무영향. project §34 갱신 권고.
- **GRADLE_VERSION (검증 대상화)**: ca-tmpl `gradle/wrapper/gradle-wrapper.properties` = Gradle **9.0.0**. project §34 는 "Gradle Groovy DSL"만 명시(버전 무기재). 본 branch 의 모든 plugin 버전이 9.0.0 기준 → §Claims To Verify 로 실측.
- **MODULE_LIST_STALE (비차단)**: ca-tmpl 실제 모듈 9개(`settings.gradle`): app-bootstrap · domain-core · application-core · adapter-web · adapter-persistence · adapter-outbound · **adapter-identifier** · shared-contract · sample-portfolio. project §25 Blocking Defaults 의 package layout 목록은 `adapter-identifier` 미포함(부분 stale). 본 branch 의 `subprojects {}` 는 9개 전체에 적용되므로 영향 없음.
- **CI_TOOLCHAIN_VACANCY (대부분 이미 정합)**: ci-quality-gates 노트는 ownership 매트릭스(`:184`) + Sources(`:254`) + Coverage(`:287`) + Audit(`:297`)에서 이미 `(toolchain)``feature-static-analysis-quality-contract` 를 owner 로 기재함. **잔존**: literal gate-list row `feature-ci-quality-gates-contract.md:194``| format / lint | true | (toolchain) |` token 만 미정합(cosmetic). 본 branch 머지 시 그 row 정합 권고(역참조 비차단 전파). → §TODO 에 항목화.
### AS-BUILT 편차 (2026-06-20 구현 실측 — §구현 가이드 대비)
> spec §구현 가이드 의 사전 명세 대비, 실제 ca-tmpl(Gradle 9.0.0 / Java 21 / Boot 3.5.15 / 10모듈)에서 green 을 위해 조정한 항목. 사용자 승인된 전략(Javadoc warning-tier)과 환경 강제(commons-lang3) 구분.
- **plugin 버전**: spec 핀(8.6.0 / 6.5.6 / 5.1.0) 그대로 사용 — Gradle Plugin Portal 에서 resolve 확인. config 파일은 `rootProject = src/` 이므로 `src/config/` 에 배치(spec §2 의 `config/` = rootProject 상대).
- **SpotBugs (env 강제)**: `ext['commons-lang3.version']='3.20.0'` 추가 — spec 미기재. Boot BOM 이 도구 classpath 의 commons-lang3 를 3.17.0 으로 강등시켜 4.10.2 가 crash(§마주친 문제). 또 `reportLevel='high'` 적용(§1 이 제시한 strictness lever) — medium tier 78건 중 38건이 EI_EXPOSE_REP/REP2(DI 협력자 방어복사 노이즈)라 high-confidence 만 blocking. SPRING_CSRF_PROTECTION_DISABLED(stateless JWT API 의 의도된 설정) 3건은 `*SecurityConfig` 한정 exclude.xml suppress.
- **Checkstyle (사용자 승인 전략 + 관용구 보정)**: §4 는 checkstyleMain Javadoc 을 blocking 으로 규정하나, 기존 코드 327건(Method 293 + Type 34) 누락 → 사용자 결정으로 **Javadoc 규칙을 warning-tier**(severity=warning, `maxWarnings=∞`)로 도입(추후 blocking 승급). naming/logical 은 error-tier 유지. 관용구 false-positive 보정: `ConstantName``log`/`logger` 허용(Logger 는 Google §5.2.4 상 비-상수), 타입파라미터 패턴 `^[A-Z][A-Z0-9]*$` 로 F-bounded `SELF` 허용. `checkstyleTest`·`spotbugsTest``ignoreFailures=true`(§4 test-source warning trade-off).
- **코드 실수정(behavior-preserving)**: NeedBraces 13(중괄호 추가) + MissingSwitchDefault 1(`UpdateWorkLogUseCase` 방어 default) + ErrorProne test 4(`catchThrowableOfType``assertThatThrownBy` ×3, double-brace init→static factory ×1). spotlessApply 로 654 파일 일괄 포맷(google-java-format 2-space).
- **SonarQube 문서**: `docs/optional/sonarqube-integration.md` 작성. 단 ca-tmpl `/docs``.gitignore` → 로컬 전용(registries/snapshot 과 동일 관행), 커밋에는 비포함.
## 관심사 커버리지 (coverage-auditor 자동 생성 — 있을 때)
> `/coverage` 가 채우는 **생성물** — 손으로 유지하지 않는다. governing 문서(frontmatter `governing_docs`)가 요구하는 관심사를 이 브랜치가 빠짐없이 덮는지의 결과. 기준: `rules/coverage-gate.md`.
> 상태: `covered-here`(이 브랜치 결정) / `delegated`(다른 owner 브랜치) / `missing`(아무도 안 맡음 → Blocking).
| 관심사 | 상태 | owner | 심각도 | 근거 |
|--------|------|-------|--------|------|
| <governing doc 의 관심사> | covered-here | — | — | D<n> |
| <관심사> | delegated | feature-<owner> | OK/Should-fix | §Audit 위임 링크 |
| <관심사> | missing | (없음) | 🔴 Blocking | governing doc §<x> 요구, 결정 없음 |
## 마주친 문제
> 짧은 메모만. 깊이 있는 트러블슈팅은 `raw/errors/` 로 분리하고 아래 Cluster에 연결.
- 2026-06-20 — **SpotBugs 4.10.2 분석 worker crash** (`NoClassDefFoundError: org.apache.commons.lang3.Strings`). Boot BOM 이 commons-lang3 를 모든 configuration(도구 `spotbugs` 포함)에서 3.17.0 으로 강등 → SpotBugs 가 요구하는 3.20.0 의 `Strings`(3.18.0+) 부재. `resolutionStrategy.force` 무효(dependency-management 가 우선), `ext['commons-lang3.version']='3.20.0'` 로 해소. 전말: [[raw/errors/spotbugs-commons-lang3-bom-downgrade-noclassdef-2026-06-20]].
- 2026-06-20 — spec 의 plugin 버전 핀은 Maven Central 구현-artifact 경로엔 404 였으나 **Gradle Plugin Portal 에는 전부 존재**(SpotBugs 6.5.6 / ErrorProne 5.1.0 marker 확인). `plugins{}` 는 portal 에서 resolve 하므로 spec 버전 그대로 사용.
## 묶음 (이 branch에서 파생된 자료)
<!-- GENERATED: sources:start -->
- [[raw/official-docs/checkstyle-google-style-reference]]
- [[raw/official-docs/errorprone-gradle-plugin-readme]]
- [[raw/official-docs/find-sec-bugs-official]]
- [[raw/official-docs/google-java-format-readme]]
- [[raw/official-docs/sonarqube-server-versus-cloud]]
- [[raw/official-docs/spotbugs-gradle-plugin-docs]]
- [[raw/official-docs/spotless-gradle-plugin-readme]]
<!-- GENERATED: sources:end -->
<!-- GENERATED: interviews:start -->
- [[raw/interviews/formatter-vs-style-linter-responsibility-split-2026-06-20]]
<!-- GENERATED: interviews:end -->
<!-- GENERATED: errors:start -->
- [[raw/errors/spotbugs-commons-lang3-bom-downgrade-noclassdef-2026-06-20]]
<!-- GENERATED: errors:end -->
<!-- GENERATED: blog-topics:start -->
- [[raw/blog-topics/gradle9-java21-static-analysis-baseline-2026-06-20]]
<!-- GENERATED: blog-topics:end -->
> 이 branch는 단일 노트가 아니라 **작업 묶음의 entry point**. 이 branch에서 파생된 모든 raw 노트를 카테고리별로 명시. 자식 노트가 forward link만 박아도 Obsidian backlink로 자동 발견되지만, 읽기 흐름과 분류를 위해 hub가 명시적으로 그룹화한다.
### Sub-branches (세부 작업)
- 해당 없음 (단일 branch — 세부 분해 없음).
### 근거 자료 (이 branch 결정 근거 — official-docs, 본 branch 가 hub)
- [[raw/official-docs/google-java-format-readme]] — D1 formatter scope
- [[raw/official-docs/spotless-gradle-plugin-readme]] — D1/D9 Spotless wiring
- [[raw/official-docs/checkstyle-google-style-reference]] — D2 ruleset 분류
- [[raw/official-docs/spotbugs-gradle-plugin-docs]] — D3/D4/D9 SpotBugs
- [[raw/official-docs/find-sec-bugs-official]] — D4 code-level security
- [[raw/official-docs/errorprone-gradle-plugin-readme]] — D5 ErrorProne
- [[raw/official-docs/sonarqube-server-versus-cloud]] — D7 Sonar skip 근거
### 오류 기록 (이 branch 작업 중 발생)
- [[raw/errors/spotbugs-commons-lang3-bom-downgrade-noclassdef-2026-06-20]] — Boot BOM 의 commons-lang3 강등으로 SpotBugs 분석 worker crash, `ext` property override 로 해소.
### 면접 준비 (이 작업에서 나올 수 있는 면접 질문)
- [[raw/interviews/formatter-vs-style-linter-responsibility-split-2026-06-20]] — 포매터와 스타일 린터의 책임 분리(중복 강제 시 무한 reformat 루프).
### 강의 (이 작업을 위해 학습한 강의)
- 해당 없음.
### job-posting tie-ins (이 작업에서 파생된 글감)
- [[raw/blog-topics/gradle9-java21-static-analysis-baseline-2026-06-20]] — Gradle 9 + Java 21 정적 분석 baseline 5종 도입의 함정(책임 중복 제거 / 기존 코드 마이그레이션 전략 / BOM↔도구 classpath 충돌 / reportLevel 보정).
## 관련 일일 노트
> 이 브랜치를 작업한 날짜들. 양방향 nav 유지.
- 해당 없음 (2026-06-15 daily 노트 미생성).
## 완료 후 정리
> 머지/종료 시점에 채움. `/ingest`가 이 섹션을 기준으로 wiki/projects/에 추출.
- PR 링크:
- 리뷰 메모:
- 머지 결과 / 배포 환경: (로컬/dev/staging/prod 어디까지 검증됐는지)
- **wiki 추출 대상** (verified만, `wiki/projects/`로만 추출):
- `actually-implemented` 항목:
- `locally-verified` 항목:
- `prod-verified` 항목:
- **추출하지 않을 항목** (planned / documented-only / abandoned):