93 lines
7.5 KiB
Markdown
93 lines
7.5 KiB
Markdown
---
|
|
title: blog-topic / gradle9-java21-static-analysis-baseline-2026-06-20
|
|
source_type: blog-topic
|
|
status: raw
|
|
related_branches: [feature-static-analysis-quality-contract]
|
|
related_projects: [ca-tmpl]
|
|
tags: [blog-topic, ca-tmpl, static-analysis, gradle, spotless, checkstyle, spotbugs, errorprone, java21]
|
|
created: 2026-06-20
|
|
status_label: ready-for-canonical
|
|
target_audience: backend-engineer
|
|
inspiration_url:
|
|
archive_url:
|
|
---
|
|
|
|
# blog-topic: gradle9-java21-static-analysis-baseline-2026-06-20
|
|
|
|
> Layer: `raw/blog-topics/` — 채용공고가 아닌 작업·학습에서 나온 블로그 글감 원석. canonical 정제 전 raw 후보이며, `wiki/blog/` 직접 생성 근거가 아니다.
|
|
|
|
## Parent / 부모
|
|
|
|
- [[raw/branch-notes/feature-static-analysis-quality-contract]] — Spotless + Checkstyle + SpotBugs + FindSecBugs + ErrorProne 5종을 Gradle 9.0.0 / Java 21 멀티모듈에 도입한 작업에서 추출.
|
|
|
|
## 트리거 / Trigger
|
|
|
|
- 트리거 유형: `branch-work`
|
|
- 정적 분석 도구가 전무한 greenfield 스켈레톤(10 모듈)에 "비중복·로컬·infra-free" 원칙으로 5종 도구를 한 번에 도입. 도구 선택은 문서에서 끝났지만, 실제 wiring 에서 (1) formatter↔linter 책임 중복, (2) 기존 코드 대량 위반, (3) BOM↔도구 classpath 충돌이 줄줄이 나왔다.
|
|
|
|
## 글감 코어 / Core idea
|
|
|
|
- **formatter 와 linter 의 책임 분리(중복 제거)**: google-java-format(Spotless) 가 *포맷·import order* 를 소유하면, Checkstyle 은 그 모듈(`Indentation`/`LineLength`/`WhitespaceAround`/`CustomImportOrder`)을 **반드시 빼야** 한다. 안 그러면 formatter 가 고친 걸 linter 가 reject → CI 무한 reformat 루프(checkstyle #6527). Checkstyle 은 formatter 가 못 하는 것(naming·Javadoc·logical)만 남긴다. "두 도구가 같은 규칙을 강제하지 않게 하는 게 도입의 핵심" 이라는 한 줄.
|
|
- **기존 코드에 blocking 게이트를 씌우는 3가지 전략**: ① 전부 컴플라이언스(reformat + Javadoc 327개 작성 — 비현실적·부정확 위험), ② 포맷은 전체 적용 + Javadoc 은 warning-tier 로 시작(추후 승급), ③ ratchet(변경 파일만). 이 스켈레톤은 ②를 택함 — `spotlessApply` 로 654 파일 일괄 포맷(포매터 도입의 표준 절차)하되, Checkstyle Javadoc 규칙은 `severity=warning` + `maxWarnings=∞` 로 reported-but-non-blocking. naming/logical 은 error-tier 유지.
|
|
- **idiom false-positive 는 rename 이 아니라 calibrate**: ConstantName 이 SLF4J `private static final Logger log` 를 25건 잡는다 — 하지만 Logger 는 mutable observable state 라 Google §5.2.4 상 *상수가 아님* → `log`/`logger` 를 패턴에 허용. InterfaceTypeParameterName 이 F-bounded self-type `ResourceId<SELF ...>` 의 `SELF` 를 잡는다 → 타입 파라미터 패턴을 `^[A-Z][A-Z0-9]*$` 로 완화. "규칙이 관용구를 잡으면 코드를 망치지 말고 규칙을 보정한다."
|
|
- **BOM 이 도구 classpath 를 오염시킨다**: `io.spring.dependency-management` 는 BOM managed version 을 **모든 configuration**(런타임뿐 아니라 `spotbugs` 도구 설정)에 적용. SpotBugs 4.10.2 가 요구하는 commons-lang3 3.20.0 이 Boot BOM 의 3.17.0 으로 강등 → `NoClassDefFoundError: org.apache.commons.lang3.Strings` 로 분석 worker crash. `resolutionStrategy.force` 는 안 먹히고 `ext['commons-lang3.version']='3.20.0'` 로 managed property 를 override 해야 함. (디버그 전말: [[raw/errors/spotbugs-commons-lang3-bom-downgrade-noclassdef-2026-06-20]])
|
|
- **SpotBugs 노이즈는 reportLevel 로 끊는다**: 기본(medium)에서 78건 중 38건이 EI_EXPOSE_REP/REP2 — 생성자가 주입받은 EntityManager/repository/Clock/ObjectMapper 를 "방어적 복사 안 했다" 고 잡는 노이즈(DI 협력자는 복사하면 안 됨). `reportLevel='high'` 로 high-confidence 만 blocking → 노이즈 제거. 남는 high 보안 finding(SPRING_CSRF_PROTECTION_DISABLED)은 stateless JWT API 에서 의도된 설정이라 exclude.xml 로 근거와 함께 suppress.
|
|
- **게이트 실효성 증명**: `./gradlew check` 가 green 한 번으로 끝내지 말고, 의도적 위반(나쁜 포맷 + `Bad_Method_Name`)을 주입해 spotlessCheck/checkstyleMain 이 실제로 BUILD FAILED 하는지(gate bites) 확인 후 원복.
|
|
|
|
## 글감 / Topic seed
|
|
|
|
- 한 문장 요지: Gradle 9 / Java 21 멀티모듈에 정적 분석 baseline을 넣을 때 핵심은 plugin 나열이 아니라 formatter-linter 책임 분리, 기존 코드 마이그레이션, 도구 classpath 충돌 처리다.
|
|
- 예상 제목 후보:
|
|
- Gradle 9와 Java 21에서 static analysis baseline을 잡는 법
|
|
- Spotless, Checkstyle, SpotBugs, ErrorProne을 한 번에 넣으며 배운 것
|
|
|
|
## 핵심 주장 후보 / Claim candidates
|
|
|
|
- 사실 후보:
|
|
- Spotless가 format/import order를 소유하면 Checkstyle의 중복 formatting rule은 제거해야 한다.
|
|
- Spring dependency management BOM은 SpotBugs tool configuration의 transitive dependency에도 영향을 줄 수 있다.
|
|
- Javadoc rule은 warning-tier로 시작하고 naming/logical rule은 blocking으로 둘 수 있다.
|
|
- 의견/해석 후보:
|
|
- static analysis baseline은 도구 도입보다 기존 코드와 CI가 감당할 수 있는 승급 경로 설계가 더 중요하다.
|
|
|
|
## Outline seed
|
|
|
|
1. formatter와 linter가 같은 규칙을 강제할 때 생기는 reformat loop를 설명한다.
|
|
2. 기존 코드 위반을 한 번에 blocking하지 않고 warning-tier/ratchet/전면 수정 중 선택하는 기준을 정리한다.
|
|
3. Spring BOM이 SpotBugs classpath를 오염시킨 사례와 해결 방향을 적는다.
|
|
4. 의도적 위반 주입으로 gate가 실제로 실패하는지 확인하는 절차를 남긴다.
|
|
|
|
## 왜 의미 있나 / Why it matters
|
|
|
|
- "정적 분석 도구 도입" 은 plugin 한 줄이 아니라, **책임 중복 제거 + 기존 코드 마이그레이션 전략 + 도구/BOM classpath 충돌** 의 묶음이다. 실무에서 그대로 부딪히는 함정들이라 이식성이 높다.
|
|
- Gradle 9 + Java 21(record/sealed bytecode) 환경에서 5종 도구의 버전 호환을 실측으로 확정한 사례. 문서가 "Gradle 7+/JRE 17+" 만 명시할 때 실제로 도는지는 별개라는 점.
|
|
- 한계(글에서 명시): Javadoc 은 아직 warning-tier(blocking 미승급), SonarQube 는 외부 서비스라 기본 배제(opt-in 문서만). 즉 "완성된 게이트" 가 아니라 "정직하게 단계적으로 조이는 baseline".
|
|
|
|
## 관련 / Related
|
|
|
|
- [[raw/errors/spotbugs-commons-lang3-bom-downgrade-noclassdef-2026-06-20]]
|
|
- [[raw/branch-notes/feature-static-analysis-quality-contract]]
|
|
|
|
## Canonical 전환 후보 / Canonical extraction candidates
|
|
|
|
- `wiki/projects/ca-tmpl/devops-ci-supply-chain-dx.md` 후보:
|
|
- Gradle 9 / Java 21 static analysis baseline 글감.
|
|
- 필요한 추가 검증:
|
|
- 현재 Spotless/Checkstyle/SpotBugs/ErrorProne wiring과 warning-tier 상태.
|
|
|
|
## Sources / 근거 후보
|
|
|
|
- [[raw/branch-notes/feature-static-analysis-quality-contract]]
|
|
- [[raw/errors/spotbugs-commons-lang3-bom-downgrade-noclassdef-2026-06-20]]
|
|
|
|
## 미해결 / Unknown
|
|
|
|
- 아직 확인해야 할 사실: Javadoc warning-tier가 blocking으로 승급됐는지.
|
|
- 과장하면 안 되는 부분: static analysis baseline을 운영 품질 보장처럼 쓰지 않는다.
|
|
|
|
## Decision / 처리 결정
|
|
|
|
- 액션: `promote-to-canonical`
|
|
- 이유: `wiki/projects/ca-tmpl/devops-ci-supply-chain-dx.md` 에 Gradle 9 / Java 21 static analysis baseline 글감으로 반영한다.
|
|
- 다음 단계: blogify 전 실제 current tool versions와 gate-bites evidence를 확인한다.
|