--- title: error / spring-boot-four-jackson-three-migration-2026-06-30 source_type: error-note status: raw related_branches: [] related_projects: [ca-skeleton-operational-contract] tags: [error, ca-tmpl, runtime, spring-boot, json, testing, distributed-lock] created: 2026-06-30 status_label: resolved --- # error: spring-boot-four-jackson-three-migration-2026-06-30 > Layer: `raw/errors/` — Spring Boot 4 / Jackson 3 / Testcontainers 2 / Spring Integration 7 migration 중 발생한 실패 묶음의 트러블슈팅 기록. ## Parent / 부모 - [[raw/project-notes/ca-skeleton-operational-contract]] - 작업 식별자: `chore-spring-boot-four-migration` (별도 branch-note 없음) ## 증상 / Symptom - 에러 메시지 (원문 그대로): ```text Property 'server.error.include-stacktrace' is Deprecated: Use 'spring.web.error.include-stacktrace' instead.vscode-spring-boot(YAML_DEPRECATED_ERROR) ``` - 에러 메시지 (원문 그대로): ```text server.error.include-stacktrace -> spring.web.error.include-stacktrace Default: never Deprecated! When to include the "trace" attribute. ``` - 에러 메시지 (원문 그대로): ```text MethodName must match pattern '^[a-z][a-zA-Z0-9]*$' ``` - 에러 메시지 (원문 그대로): ```text ConstantName must match pattern NeedBraces StaticVariableName ``` - 에러 메시지 (원문 그대로): ```text The method asText() from the type JsonNode is deprecatedJava(67108967) String tools.jackson.databind.JsonNode.asText() Deprecated. Use asString() instead. Source: jackson-databind-3.0.2.jar ``` - 에러 메시지 (원문 그대로): ```text Execution failed for task ':adapter-web:spotlessJavaCheck'. The following files had format violations: Run './gradlew spotlessApply' to fix all violations. ``` - 에러 메시지 (원문 그대로): ```text Execution failed for task ':app-bootstrap:compileTestJava'. bad class file: /home/donghyeon/workspace/ca-tmpl/src/app-bootstrap/build/classes/java/test/dev/caskeleton/bootstrap/integration/outbox/OutboxContainerTestSupport.class unable to access file: java.nio.file.NoSuchFileException Please remove or make sure it appears in the correct subdirectory of the classpath. ``` - 에러 메시지 (원문 그대로): ```text H S SECUJDES: Unsafe Jackson deserialization configuration used in dev.caskeleton.bootstrap.architecture.violations.boundary.DefaultTypingFixture.unsafe() SpotBugs ended with exit code 1 ``` - 에러 메시지 (원문 그대로): ```text java.lang.Error: Failed Approval Approved: .../EnvelopeContractTest.successEnvelopeShape.approved.txt Received: .../EnvelopeContractTest.successEnvelopeShape.received.txt ``` - 경고 메시지 (원문 그대로): ```text Assigning String value 'high' to property of enum type 'com.github.spotbugs.snom.Confidence'. This behavior has been deprecated. This will fail with an error in Gradle 10. ``` - 경고 메시지 (원문 그대로): ```text Invocation of Task.project at execution time has been deprecated. This will fail with an error in Gradle 10. ``` - 경고 메시지 (요약): ```text MissingJavadocMethodCheck 310 MissingJavadocTypeCheck 34 ``` - CI 실패 메시지 후보 (workflow 원문): ```text A governed contract baseline changed but the PR lacks the 'intent:breaking-change-approved' label. ``` - 서버 시작 실패 메시지 (원문 발췌): ```text Caused by: org.flywaydb.core.api.exception.FlywayValidateException: Validate failed: Migrations have failed validation Migration checksum mismatch for migration version 4 -> Applied to database : 1718831886 -> Resolved locally : -37693890 Either revert the changes to the migration, or run repair to update the schema history. ``` - 서버 시작 실패 메시지 (local `bootRun`): ```text Failed to bind properties under 'spring.profiles.active' to java.util.Set Value: "${SPRING_PROFILES_ACTIVE}" Profile '${SPRING_PROFILES_ACTIVE}' must contain a letter, digit or allowed char ``` - 서버 시작 실패 메시지 (local `bootRun`): ```text Could not initialize Logback logging from classpath:logback-spring.xml Could not resolve placeholder 'APP_NAME' in value "${APP_NAME}" ``` - 서버 시작 실패 메시지 (사용자 신규 로그): ```text org.flywaydb.core.api.FlywayException: Found more than one migration with version 4 Offenders: -> .../adapter-persistence-postgresql-0.0.1+3a300d89ec30.jar!/db/migration/postgresql/V4__int_lock.sql -> .../adapter-persistence-postgresql/build/resources/main/db/migration/postgresql/V4__int_lock.sql ``` - 발생 컨텍스트: Spring Boot 4 dependency set에서 Gradle compile/test/check 및 VSCode Spring Boot YAML validation 실행. - 발생 시점: 2026-06-30 - 발생 환경: local - 재현 가능 여부: `always` ## 재현 절차 / Reproduction 1. Spring Boot 4 / Spring Framework 7 / Jackson 3 dependency set으로 프로젝트를 refresh한다. 2. `src/app-bootstrap/src/test/resources/application-test.yml` 또는 application yml에서 deprecated `server.error.include-stacktrace` key를 유지한다. 3. VSCode Spring Boot YAML validation 또는 Gradle test/check를 실행한다. 4. 기대 결과: 설정 key, compile surface, tests가 현 dependency set과 일치한다. 5. 실제 결과: deprecated YAML warning/error, moved package compile errors, Jackson runtime test failures, JDBC lock schema/API mismatch, Checkstyle method naming failures가 발생한다. ## 조사 단계 / Investigation log - 2026-06-30 — `rg -n "server\\.error\\.include"` 실행 → repository config에 deprecated key가 남아 있음을 확인. - 2026-06-30 — Spring Boot 4.0.0 jar metadata에서 replacement 확인 → `spring.web.error.include-stacktrace`와 `spring.web.error.include-message`로 이동. - 2026-06-30 — focused adapter-web/app-bootstrap/sample tests 실행 → Jackson 3 `tools.jackson.*` API와 local JsonNullable module 필요 확인. - 2026-06-30 — Spring Integration 7 source/API 확인 → `JdbcLockRegistry` TTL constructor와 `DistributedLock.tryLock(wait, ttl)` path 필요 확인. - 2026-06-30 — `./gradlew test --continue` 실행 → 전체 test suite 통과 확인. - 2026-06-30 — `./gradlew check` 실행 → architecture/env checks는 통과했으나 Checkstyle/Spotless 단계에서 test method naming 위반으로 실패 확인. - 2026-07-01 — `rg -n "\\.asText\\(" src` 실행 → adapter-web auth 테스트 2개 파일에서 Jackson 3 deprecated call 확인. - 2026-07-01 — adapter-web auth focused tests 실행 → `asString()` 전환 후 테스트 통과 확인. - 2026-07-01 — `./gradlew check --continue` 실행 → blocking failures가 `adapter-web`, `app-bootstrap`, `sample-portfolio` Spotless format drift였음을 확인. - 2026-07-01 — `./gradlew spotlessApply` 실행 → formatter-owned import order / google-java-format drift 정리. - 2026-07-01 — `./gradlew check` 재실행 → `app-bootstrap:compileTestJava`가 stale `OutboxContainerTestSupport.class` 경로를 참조하며 실패. - 2026-07-01 — `./gradlew :app-bootstrap:cleanTest :app-bootstrap:compileTestJava` 실행 → stale test output 제거 후 재컴파일 성공. - 2026-07-01 — Checkstyle XML report를 집계 → `MethodName`, `ConstantName`, `NeedBraces`, `StaticVariableName` error entries가 test source에 남아 있음을 확인. - 2026-07-01 — test method/constant bulk rename 및 one-line `if` brace cleanup 실행 → `./gradlew checkstyleTest checkstyleSampleOffTest --continue` 후 XML parser `total_errors=0` 확인. - 2026-07-01 — ApprovalTests failed path 확인 → `PackageSettings.UseApprovalSubdirectory` exact field convention을 lowerCamelCase로 바꾸면 approved snapshot directory lookup이 깨짐을 확인. - 2026-07-01 — SpotBugs `SECUJDES` output 확인 → architecture negative fixture가 의도적으로 unsafe Jackson call을 포함해 false-positive처럼 출력됨을 확인. - 2026-07-01 — ApprovalTests snapshot filename을 lowerCamelCase method name과 맞추고, exact field/negative fixture만 targeted suppression/filter 적용. - 2026-07-01 — `./gradlew build --warning-mode all` 실행 → Gradle 10 deprecation 후보가 SpotBugs enum coercion과 task action project lookup 2건임을 확인. - 2026-07-01 — Checkstyle XML report 집계 → 남은 warning이 `MissingJavadocMethodCheck` 310건, `MissingJavadocTypeCheck` 34건뿐임을 확인. - 2026-07-01 — SpotBugs `reportLevel`을 enum value로 넘기고 `verifyCleanArchitectureDependencies` lookup을 `rootProject.project(...)`로 변경. - 2026-07-01 — default Checkstyle에서 Javadoc warning-tier modules를 제거 → Checkstyle XML warning/error total 0 확인. - 2026-07-01 — CI quality-gates workflow 검토 → PR 전용 breaking-change-approval gate가 `.approved.txt` filename-only rename도 label-required로 볼 수 있음을 확인. - 2026-07-01 — old/new ApprovalTests approved snapshot SHA 비교 → 세 snapshot 모두 내용 동일, 파일명만 변경됨을 확인. - 2026-07-01 — `.github/scripts/verify-breaking-change-approval.sh` 추가 → `R100` identical-content rename은 통과, content change는 label 없으면 실패하도록 분리. - 2026-07-01 — temp git repo에서 no-change / identical rename / content change without label / content change with label path 검증. - 2026-07-01 — `./gradlew check`, `./gradlew build` 실행 → 둘 다 success. - 2026-07-01 — 사용자 서버 startup log 확인 → 실제 root cause는 후속 `BeanCreationException`이 아니라 마지막 `Caused by`의 Flyway V4 checksum mismatch임을 확인. - 2026-07-01 — `git show b3bd7fa:.../V4__int_lock.sql` 확인 → 기존 V4에는 `EXPIRED_AFTER`가 없고 최근 커밋에서 V4에 컬럼을 직접 추가했음을 확인. - 2026-07-01 — `FlywayMigrationCompatibilityContractTest` 추가 → old V4가 이미 적용된 PostgreSQL DB에 current V4/V5 migration set을 적용하는 시나리오를 재현. - 2026-07-01 — `V4__int_lock.sql`에서 `EXPIRED_AFTER`를 제거하고 `V5__int_lock_expired_after.sql`을 추가 → focused migration compatibility test 통과. - 2026-07-01 — 순수 `bootRun` 실행 → profile placeholder literal binding failure 확인. - 2026-07-01 — `spring.profiles.active`에 `local` fallback과 `EnvProfileMatrixContractTest` 회귀 테스트 추가 → focused test 통과. - 2026-07-01 — 순수 `bootRun` 재실행 → logback early placeholder failure 확인. - 2026-07-01 — `logback-spring.xml` springProperty source를 direct env key로 변경하고 `StructuredLogFieldContractTest` 회귀 테스트 추가 → focused test 통과. - 2026-07-01 — `.env`를 process env로 명시 주입한 `bootRun` 실행 → 서버가 `Started CaSkeletonApplication`까지 도달하고 기존 DB에 V5 migration이 적용됨을 확인. - 2026-07-01 — Gradle `bootRun`이 `src/.env`를 Java process env로 주입하도록 변경 → 순수 `timeout 60s ./gradlew :app-bootstrap:bootRun --no-daemon --stacktrace`도 `Started CaSkeletonApplication`까지 도달. - 2026-07-01 — `./gradlew check verifyPublicPathSnapshot --no-daemon --stacktrace` 재실행 → success. - 2026-07-01 — 사용자 신규 startup log 확인 → root cause가 이전 checksum mismatch가 아니라 stale `adapter-persistence-postgresql` JAR와 current resources의 Flyway migration duplicate version임을 확인. - 2026-07-01 — `find src/adapter-persistence-postgresql/build/libs -name 'adapter-persistence-postgresql-*.jar'` 실행 → `0.0.1+3a300d89ec30.jar` 포함 다수의 old git-revision JAR가 남아 있음을 확인. - 2026-07-01 — `verifyNoStaleTraceableJars`를 먼저 추가하고 실행 → 10개 module의 stale traceable JAR를 감지하며 실패해 검증이 실제 문제를 잡는 것을 확인. - 2026-07-01 — `cleanStaleTraceableJars`, `verifyNoStaleTraceableJars`, `Jar`/`BootJar` 실행 전 stale archive cleanup을 추가하고 `check`에 연결. - 2026-07-01 — `./gradlew verifyNoStaleTraceableJars --no-daemon --stacktrace` 재실행 → stale archive 105개 삭제 후 success. - 2026-07-01 — `find src -path '*/build/libs/*.jar'` 실행 → 각 module에 current git-revision archive만 남은 것을 확인. - 2026-07-01 — `timeout 45s ./gradlew :app-bootstrap:bootRun --no-daemon --stacktrace` 실행 → Flyway duplicate 오류 없이 `Started CaSkeletonApplication`까지 도달. - 2026-07-01 — `./gradlew check verifyPublicPathSnapshot --no-daemon --stacktrace` 재실행 → new stale-jar gate 포함 success. ## 근본 원인 / Root cause - 직접 원인: Spring Boot 4 / Spring Framework 7 / Jackson 3 / Testcontainers 2 / Spring Integration 7에서 package, module, configuration key, serializer API, lock schema/API가 변경되었는데 기존 코드와 설정이 Boot 3/Jackson 2 계열 surface에 남아 있었다. - 근본 원인: patch upgrade와 달리 major upgrade는 compiler output뿐 아니라 runtime auto-configuration metadata, test-slice module split, third-party module compatibility까지 함께 바뀐다. - 추가 원인: warning-only static-analysis task라도 rule severity가 `error`면 Gradle 출력에 error처럼 보이는 로그가 남는다. 이 경우 ignoreFailures 정책은 exit code만 완화하고 리포트의 severity를 바꾸지 않는다. - 추가 원인: Gradle warning은 default mode에서 요약만 보이고 실제 제거 지점은 `--warning-mode all`에서만 드러난다. Checkstyle Javadoc warning은 meaningful documentation 없이 대량 주석을 강제하는 baseline이라 기본 build signal로 적합하지 않았다. - 추가 원인: CI breaking-change approval gate가 content diff가 아니라 path glob 중심으로 governed snapshot 변경을 판단해, 동일 내용 rename도 breaking change로 오탐할 수 있었다. - 추가 원인: 적용된 Flyway versioned migration인 `V4__int_lock.sql`에 Spring Integration 7용 `EXPIRED_AFTER` 컬럼을 직접 추가해 기존 DB의 `flyway_schema_history` checksum과 소스 checksum이 달라졌다. - 추가 원인: Boot 4 early profile/logging initialization은 `spring-dotenv`가 `.env`를 Spring Environment에 넣기 전에 실행될 수 있어, required placeholder가 literal 또는 unresolved 상태로 실패했다. - 추가 원인: Gradle `bootRun`은 working directory만 `src`로 바꿨고, `.env` 값을 Java process environment로 직접 주입하지는 않았다. - 추가 원인: traceable artifact 이름에 git revision이 포함되는데 `build/libs`에 old revision JAR가 누적되었다. IDE/runtime classpath가 stale JAR와 current `build/resources/main`을 함께 잡으면 Flyway가 동일 versioned migration을 두 번 발견한다. - 트리거 조건: Boot 4 dependency set refresh 후 Gradle compile/test/check 및 IDE YAML validation 실행. ## Sources / 근거 (해결 근거가 된 자료, 최소 1개+ 권장) - [[raw/official-docs/spring-boot-test-slices-webmvctest-datajpatest-official]] — Boot test slice/Jackson component scan semantics 확인. - [[raw/official-docs/lock-spring-integration-lock-registry]] — LockRegistry/JdbcLockRegistry와 TTL 만료 위험 확인. - [[raw/official-docs/config-spring-boot-externalized-configuration]] — Spring Boot 설정 검증 관점 확인. - [[raw/official-docs/tracing-spring-boot-3-actuator-tracing-reference]] — Micrometer tracing/OpenTelemetry starter 선택지 확인. - local Spring Boot 4.0.0 configuration metadata — `server.error.include-*` replacement 확인. ## 해결 / Resolution - 적용한 조치: - Boot 4 moved package imports와 split module dependencies를 갱신했다. - Jackson 3 `tools.jackson.*` API로 production/test code를 전환했다. - Jackson 2 기반 nullable module 대신 local Jackson 3 `JsonNullable` serializer/deserializer module을 구성했다. - Testcontainers PostgreSQL package를 Testcontainers 2 module namespace로 갱신했다. - Spring Integration 7 JDBC lock schema에 `EXPIRED_AFTER`를 추가하고 TTL-aware lock API를 사용했다. - `server.error.include-stacktrace/message`를 `spring.web.error.include-stacktrace/message`로 이전하고 env registry를 갱신했다. - Jackson 3.0.2에서 deprecated 된 `JsonNode.asText()` 테스트 assertion을 `asString()`으로 변경했다. - `spotlessApply`로 formatter-owned drift를 정리했다. - `:app-bootstrap:cleanTest :app-bootstrap:compileTestJava`로 stale test compile output을 재생성했다. - test method `snake_case`를 lowerCamelCase로, static constant를 `UPPER_SNAKE_CASE`로 정리했다. - Checkstyle `NeedBraces` 위반이 남은 single-line `if`에 braces를 추가했다. - ApprovalTests approved snapshot 파일명을 새 lowerCamelCase method name과 맞췄다. - ApprovalTests reflection convention인 `UseApprovalSubdirectory`만 file-scoped Checkstyle exception으로 문서화했다. - architecture negative fixture의 `SECUJDES`는 `DefaultTypingFixture.java` source에 한정해 SpotBugs exclude했다. - SpotBugs Gradle `reportLevel`은 문자열 coercion 대신 `com.github.spotbugs.snom.Confidence.valueOf('HIGH')`를 사용했다. - `verifyCleanArchitectureDependencies` task action 내부 project lookup은 `rootProject.project(...)`로 변경했다. - Checkstyle default ruleset에서 `MissingJavadocType`, `MissingJavadocMethod`, `NonEmptyAtclauseDescription` warning-tier modules를 제거했다. - breaking-change approval workflow inline shell을 `.github/scripts/verify-breaking-change-approval.sh`로 분리하고, `git diff --name-status --find-renames=100%` 기반으로 identical-content rename을 통과시켰다. - 이미 적용된 `V4__int_lock.sql`을 원래 checksum으로 되돌리고 `V5__int_lock_expired_after.sql` 전진 migration으로 `EXPIRED_AFTER`를 추가했다. - 기존 V4가 적용된 DB도 repair 없이 V5로 올라가는 Testcontainers 회귀 테스트를 추가했다. - `spring.profiles.active`에 `local` fallback을 추가하고 early profile binding 계약 테스트를 추가했다. - `logback-spring.xml`의 early properties를 `application.yml` 경유가 아니라 direct env key + default로 읽게 바꿨다. - Gradle `bootRun`이 `src/.env`를 Java process env로 주입하되 이미 export된 env를 덮어쓰지 않게 했다. - `cleanStaleTraceableJars`와 `verifyNoStaleTraceableJars`를 추가해 old git-revision JAR를 삭제하고 남아 있으면 검증 실패하도록 했다. - `verifyNoStaleTraceableJars`를 기본 `check`에 연결했다. - 모든 `Jar`/`BootJar` 계열 archive task 실행 전에 같은 archive base/classifier의 old git-revision JAR를 삭제하게 했다. - 검증 방법: - `./gradlew test --continue` success. - `./gradlew verifyCleanArchitectureDependencies verifyEnvKeys verifyPublicPathSnapshot` success. - `./gradlew checkstyleTest checkstyleSampleOffTest --continue` success and Checkstyle XML `total_errors=0`. - `./gradlew checkstyleMain checkstyleTest checkstyleSampleOffTest --continue` success and Checkstyle XML total warning/error `0`. - `./gradlew :app-bootstrap:spotbugsTest :app-bootstrap:spotbugsSampleOffTest` success without `SECUJDES` output. - `./gradlew build --warning-mode all` success without Gradle deprecation output. - `./gradlew check verifyPublicPathSnapshot --no-daemon --stacktrace` success. - `./gradlew :app-bootstrap:sampleOffTest verifyCleanArchitectureDependencies --no-daemon --stacktrace` success. - `bash .github/scripts/verify-gate-matrix.sh && bash .github/scripts/verify-supply-chain-contract.sh && bash .github/scripts/test-supply-chain-scripts.sh` success. - temp git repo script test success for breaking-change approval paths. - `./gradlew check` success. - `./gradlew build` success. - `./gradlew :app-bootstrap:test --tests dev.caskeleton.bootstrap.integration.FlywayMigrationCompatibilityContractTest --no-daemon --stacktrace` success. - `./gradlew :app-bootstrap:test --tests dev.caskeleton.bootstrap.contract.EnvProfileMatrixContractTest --no-daemon --stacktrace` success. - `./gradlew :app-bootstrap:test --tests dev.caskeleton.bootstrap.contract.StructuredLogFieldContractTest --no-daemon --stacktrace` success. - `timeout 60s ./gradlew :app-bootstrap:bootRun --no-daemon --stacktrace` → process는 timeout 124로 종료했지만 로그에서 `Started CaSkeletonApplication` 및 Readiness `ACCEPTING_TRAFFIC` 확인. - `./gradlew check verifyPublicPathSnapshot --no-daemon --stacktrace` success. - `./gradlew verifyNoStaleTraceableJars --no-daemon --stacktrace` → stale traceable JAR 삭제 후 success. - `find src -path '*/build/libs/*.jar'` → old git-revision JAR 제거 확인. - `timeout 45s ./gradlew :app-bootstrap:bootRun --no-daemon --stacktrace` → process는 timeout 124로 종료했지만 로그에서 Flyway validate/migrate와 `Started CaSkeletonApplication` 확인. - `./gradlew check verifyPublicPathSnapshot --no-daemon --stacktrace` success, `verifyNoStaleTraceableJars` 포함. - `rg -n "server\\.error\\.include|spring\\.jackson\\.generator|SPRING_JACKSON_GEN_WRITE_BIGDECIMAL_AS_PLAIN" .`로 deprecated/removed keys 제거 확인. - `rg -n "\\.asText\\(" src` no matches. - `./gradlew :adapter-web:test --tests 'dev.caskeleton.adapter.web.auth.EnvelopeAccessDeniedHandlerTest' --tests 'dev.caskeleton.adapter.web.auth.EnvelopeAuthenticationEntryPointTest'` success. - 잔여 위험 / 후속 작업: - 기본 빌드의 Javadoc warning은 제거했지만, public API documentation 자체가 완료된 것은 아니다. - tracing fallback은 local test contract를 유지하기 위한 최소 bean 구성이다. 운영 exporter 구성은 별도 작업으로 분리해야 한다. - 대규모 test method rename으로 외부 IDE run configuration이나 문서가 old snake_case method name을 직접 참조하면 갱신이 필요하다. - remote Gitea CI 로그는 로컬에 `gh`가 없고 GitHub remote가 아니어서 직접 조회하지 못했다. remote runner 재실행으로 최종 확인 필요. - 누군가 잘못된 V4 내용으로 `flyway repair`를 이미 실행한 DB는 이번 V4 원복 후 반대 방향 checksum mismatch가 날 수 있다. 해당 경우에는 DB별 schema history 확인 후 별도 repair/backout 절차가 필요하다. - `.env` parser는 단순 `KEY=value` 형식만 처리한다. quoted value, escaped newline, `export KEY=value`가 필요하면 확장해야 한다. - IDE run configuration이 삭제된 old JAR absolute path를 직접 고정하고 있다면 IDE classpath refresh가 필요하다. Gradle `check`와 archive task는 stale JAR를 다시 만들지 않도록 막지만 IDE 설정 자체의 old path 참조까지 수정하지는 않는다. ## 회고 / Lessons - 빨리 감지하는 신호: - Boot major upgrade 후 `YAML_DEPRECATED_ERROR`, `tools.jackson`/`com.fasterxml` 혼재, `JsonNode.asText()` deprecation, `NoSuchMethod`/schema mismatch, Testcontainers package missing이 함께 보이면 단순 import fix가 아니라 migration surface 전체를 점검해야 한다. - 예방 체크리스트 항목 후보: - Boot metadata replacement grep. - Jackson 2 module compatibility audit. - Testcontainers module namespace audit. - Spring Integration schema/API diff audit. - `test --continue`와 `check`를 분리해 test pass와 style gate failure를 별도 보고. - Spotless failure가 보이면 수동 import/order patch보다 `spotlessApply`로 formatter-owned 영역을 정규화. - `bad class file` + `NoSuchFileException` 조합은 stale Gradle test output 가능성이 크므로 해당 source set clean 후 재컴파일. - ApprovalTests `PackageSettings`처럼 reflection convention을 쓰는 도구 설정은 일반 naming cleanup 전에 exact symbol contract인지 먼저 확인한다. - warning-only 정적분석 task라도 developer-facing error log를 줄이려면 XML severity entry를 0으로 만드는 별도 검증이 필요하다. - Gradle deprecation은 `--warning-mode all`을 정기적으로 돌려 실제 Gradle 10 failure 후보를 조기에 제거한다. - Javadoc은 대량 기계 주석으로 해결하지 말고, 공개 API 문서화 정책과 범위를 별도 작업으로 잡는 편이 낫다. - Contract snapshot 게이트는 파일 경로 변경과 내용 변경을 분리해야 한다. ApprovalTests method rename은 filename drift를 만들지만 wire contract drift를 뜻하지 않는다. - Flyway versioned migration은 한 번 적용되면 코드 리뷰에서도 immutable artifact로 취급해야 한다. schema drift는 새 version migration으로만 전진시킨다. - Boot major upgrade 후 local `bootRun` 검증은 compiler/test와 별개로 필요하다. profile/logging은 application context보다 먼저 실패할 수 있다. - `.env`를 working directory에 두는 것과 process env에 주입하는 것은 다르다. early initialization 경로는 process env 또는 inline default가 더 안전하다. - git revision을 archive name에 포함하는 build에서는 `build/libs` 누적 산출물도 runtime 위험이 될 수 있다. IDE classpath가 Gradle classpath와 다르게 움직일 수 있으므로 stale artifact cleanup과 검증을 빌드에 포함해야 한다. - wiki로 끌어올릴 가치가 있는 일반화된 교훈: - Major framework migration은 "compile surface", "runtime metadata", "test slice auto-config", "third-party module compatibility", "registry/env contract"를 별도 축으로 검증한다. - Static-analysis noise cleanup은 "진짜 코드 스타일 위반", "도구 convention", "negative fixture"를 분리해야 한다. ## Related / 관련 - 관련 작업 식별자: `chore-spring-boot-four-migration` (별도 branch-note 없음) - 선행 patch upgrade 작업 식별자: `chore-spring-boot-patch-upgrade` (별도 branch-note 없음)