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 패턴
'Java & Spring' 카테고리의 다른 글
| Lombok 안전 사용 규칙(@Builder/@Value/@With) (0) | 2025.10.01 |
|---|---|
| JUnit5 + Mockito: Given-When-Then 패턴 (0) | 2025.10.01 |
| Pageable 응답 DTO 규격(정렬·페이지 표준화) (0) | 2025.09.30 |
| Spring Cache로 API 3배 빠르게(@Cacheable/무효화) (0) | 2025.09.30 |
| 멀티파트 업로드: 제한/보안/임시저장 설계 (0) | 2025.09.30 |