Files
llm-wiki/raw/blog-topics/gradle9-java21-static-analysis-baseline-2026-06-20.md

7.5 KiB

title, source_type, status, related_branches, related_projects, tags, created, status_label, target_audience, inspiration_url, archive_url
title source_type status related_branches related_projects tags created status_label target_audience inspiration_url archive_url
blog-topic / gradle9-java21-static-analysis-baseline-2026-06-20 blog-topic raw
feature-static-analysis-quality-contract
ca-tmpl
blog-topic
ca-tmpl
static-analysis
gradle
spotless
checkstyle
spotbugs
errorprone
java21
2026-06-20 ready-for-canonical backend-engineer

blog-topic: gradle9-java21-static-analysis-baseline-2026-06-20

Layer: raw/blog-topics/ — 채용공고가 아닌 작업·학습에서 나온 블로그 글감 원석. canonical 정제 전 raw 후보이며, wiki/blog/ 직접 생성 근거가 아니다.

Parent / 부모

트리거 / 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".

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 / 근거 후보

미해결 / 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를 확인한다.