본문 바로가기
Java & Spring

Maven→Gradle 마이그레이션 함정 7가지

by yamoojin83 2025. 10. 1.

Maven→Gradle 마이그레이션 함정 7가지

Maven에서 Gradle로 넘어가면 빌드 속도, 설정 유연성, 캐시 활용에서 큰 이점을 얻습니다.

하지만 단순히 pom.xml → build.gradle로 옮기는 수준을 넘어서, 개념 차이를 정확히 이해해야

“동작은 하지만 어딘가 불안한 빌드”를 피할 수 있습니다.

아래 7가지는 실무에서 가장 자주 만나는 함정과 해결책을 사례 중심으로 정리했습니다.

1) 라이프 사이클 vs 작업 그래프: 개념 전환

Maven은 validate → compile → test → package → verify → install → deploy 같은

정해진 라이프사이클이 강합니다. Gradle은 “작업(Task) 그래프” 중심이며, 실행 시 실제 필요한 작업만 고른 뒤

의존 관계를 따라 수행합니다. 즉, Maven의 phase에 해당하는 걸 Gradle에 그대로 찾으려 하지 말고,

원하는 결과물(예: jar, test)을 기준으로 태스크를 호출하는 식으로 사고를 바꾸세요.

2) 의존성 스코프 매핑 오류

Maven의 compile/provided/runtime/test 스코프는 Gradle의 구성(configuration)으로 옮겨 가야 합니다. Kotlin DSL 기준 예시는 다음과 같습니다.


// build.gradle.kts
dependencies {
  implementation("org.springframework.boot:spring-boot-starter-web") // maven: compile
  compileOnly("org.projectlombok:lombok")                              // maven: provided
  annotationProcessor("org.projectlombok:lombok")
  runtimeOnly("org.postgresql:postgresql")                             // maven: runtime
  testImplementation("org.springframework.boot:spring-boot-starter-test") // maven: test
}

특히 annotationProcessor 구성을 빼먹으면 Lombok, MapStruct 같은 APT가 조용히 실패해 “IDE에선 되는데 CI에서 깨지는” 상황이 나옵니다.

3) BOM, 버전 정합성: platform 사용

Maven의 dependencyManagement는 Gradle에서 platform() 또는 enforcedPlatform()으로 대체합니다. Spring BOM을 예로 들면 다음과 같습니다.


dependencies {
  implementation(platform("org.springframework.boot:spring-boot-dependencies:3.3.0"))
  implementation("org.springframework.boot:spring-boot-starter-web")
  // 버전 생략 가능
}

다모듈 프로젝트라면 루트에서 BOM을 한 번만 import해 하위 모듈의 버전 정합성을 유지하세요.

4) 멀티모듈 설정: settings.gradle(.kts)와 버전 카탈로그

Maven의 <modules>는 Gradle의 settings.gradle.kts에서 include(":core", ":api")로 정의합니다. 공통 버전/라이브러리는 gradle/libs.versions.toml로 관리하면 수정/검토가 쉬워집니다.


# gradle/libs.versions.toml
[versions]
spring-boot = "3.3.0"
[libraries]
boot-web = { group="org.springframework.boot", name="spring-boot-starter-web", version.ref="spring-boot" }

dependencies {
  implementation(libs.boot.web)
}

5) Resource filtering과 profile 오해

Maven의 리소스 필터링프로파일을 Gradle로 옮길 때, 동일한 메커니즘을 기대하면 좌초됩니다. Gradle은 보통 application-*.yml과 환경변수/CI 변수(--args, -P)를 조합합니다. 리소스 복사 시 토큰 대체가 필요하다면 processResources 태스크에서 파일을 가공하세요.


tasks.processResources {
  filesMatching("**/app.properties") {
    expand("buildNumber" to (System.getenv("BUILD_NUMBER") ?: "dev"))
  }
}

6) Shade vs Shadow, Surefire vs Test

Maven의 maven-shade-plugin은 Gradle에서 shadow 플러그인으로 대체합니다. 또 Maven Surefire/Failsafe 설정은 Gradle의 test 태스크로 통합되어 있으며, 통합테스트를 분리하려면 커스텀 소스셋과 테스트 태스크를 만드세요.


// settings/plugins
plugins {
  id("com.github.johnrengelman.shadow") version "8.1.1"
}
tasks.shadowJar {
  archiveClassifier.set("all")
}

7) 캐시·병렬·CI: Gradle의 진가

Gradle은 증분 빌드원격 빌드 캐시가 강력합니다. CI에서 --build-cache --parallel --scan을 적극 활용하고, Wrapper(gradlew)를 저장소에 포함해 도구 버전을 고정하세요.


./gradlew clean build --scan --build-cache --parallel

마이그레이션 체크리스트

  • annotationProcessor 구성, Lombok/MapStruct 확인
  • BOM → platform, 버전 카탈로그 도입
  • 멀티모듈: settings 포함/모듈 경로 점검
  • Shadow로 fat-jar 대체, 통합테스트 분리
  • 리소스 가공은 processResources에서
  • Wrapper/캐시/병렬/스캔 활성화

 

 

👉 1편: Maven→Gradle 마이그레이션 함정 7가지

👉 2편: JUnit5 + Mockito: Given-When-Then 패턴

👉 3편: Lombok 안전 사용 규칙(@Builder/@Value/@With)

👉 4편: IntelliJ 생산성: 라이브 템플릿 10개