Post

빌드 도구와 의존성 관리 - Gradle/Maven과 Spring Boot 패키징

빌드 도구와 의존성 관리 - Gradle/Maven과 Spring Boot 패키징

매일 ./gradlew bootJarmvn package를 치고, 만들어진 app.jarjava -jar로 실행합니다. 그런데 그 사이에 무슨 일이 일어날까요. 의존성에 버전을 안 적었는데 어떻게 빌드가 되고, 하나의 jar 안에 라이브러리가 전부 들어가는 건 어떻게일까요. Series 5(빌드/보안)의 첫 글에서는 빌드 도구가 하는 일, 의존성 관리(BOM), 그리고 실행 가능 jar 패키징을 정리합니다.

TL;DR

  • 빌드 도구(Gradle/Maven)는 compile -> test -> package의존성 관리를 담당한다. Gradle은 태스크 DAG + DSL, Maven은 phase 생명주기 + XML.
  • Spring Boot 플러그인의 bootJar(Gradle) / repackage(Maven)는 앱 + 모든 의존성을 담은 실행 가능 fat jar를 만들어 java -jar로 실행하게 한다.
  • 의존성에 버전을 안 적어도 되는 이유spring-boot-dependencies BOM이 호환되는 버전 집합으로 맞춰 주기 때문이다.

1. 빌드 도구가 하는 일

빌드 도구의 역할은 크게 둘입니다. 하나는 빌드 단계 실행(소스 컴파일, 테스트, 패키징)이고, 다른 하나는 의존성 관리(필요한 라이브러리와 그 전이 의존성을 가져와 버전을 맞추는 것)입니다.

Gradle과 Maven은 접근이 다릅니다.

항목MavenGradle
설정pom.xml (XML, 선언적)build.gradle(.kts) (Groovy/Kotlin DSL)
실행 모델고정된 phase 생명주기태스크 그래프(DAG), 증분 빌드/캐시
빌드 명령mvn package./gradlew build

어느 쪽이든 “소스 + 의존성 -> 실행 가능한 산출물”을 만드는 목적은 같습니다.


2. 빌드 파이프라인과 실행 가능 jar

빌드의 흐름과 최종 산출물을 정리하면 다음과 같습니다.

빌드 파이프라인과 실행 가능 fat jar compile + test -> Spring Boot 플러그인 패키징 -> 앱과 모든 의존성을 담은 실행 가능 jar -> java -jar로 실행.

Spring Boot Gradle 플러그인은 bootJar 태스크로 실행 가능 jar를 만듭니다.

Executable jars can be built using the bootJar task.
- Spring Boot Gradle Plugin Reference

이 jar는 애플리케이션의 모든 의존성을 포함java -jar로 바로 실행됩니다.

The plugin can create executable archives (jar files and war files) that contain all of an application’s dependencies and can then be run with java -jar.
- Spring Boot Gradle Plugin Reference

내부 레이아웃은 Series 1의 fat jar 편에서 본 것과 같습니다.

By default, the bootJar task builds an archive that contains the application’s classes and dependencies in BOOT-INF/classes and BOOT-INF/lib respectively.
- Spring Boot Gradle Plugin Reference

BOOT-INF/classes(내 코드) + BOOT-INF/lib(의존성 jar들) 구조이고, 이걸 JarLauncher가 전용 클래스로더로 실행합니다. 빌드의 산출물이 곧 Series 1에서 본 실행 구조입니다.


3. 의존성 관리 - 버전을 안 적어도 되는 이유

Spring Boot 프로젝트의 build.gradle을 보면 의존성에 버전이 없습니다.

1
2
3
4
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
}

이게 가능한 이유는 dependency management(BOM) 때문입니다.

의존성 관리와 BOM 버전을 생략해 선언하면, spring-boot-dependencies BOM이 호환되는 버전 집합으로 채워 직접/전이 의존성을 정렬한다.

When you apply the io.spring.dependency-management plugin, Spring Boot’s plugin will automatically import the spring-boot-dependencies bom from the version of Spring Boot that you are using. … it allows you to omit version numbers when declaring dependencies that are managed in the bom.
- Spring Boot Gradle Plugin Reference

spring-boot-dependencies BOM은 수많은 라이브러리의 검증된 호환 버전 집합을 정의합니다. 그래서 버전을 직접 안 적어도 BOM이 채워 주고, 라이브러리 간 버전 충돌이 줄어듭니다. starter(예: spring-boot-starter-web)는 이 위에서 “이 기능에 필요한 의존성 묶음”을 한 번에 가져오는 역할을 합니다.


4. 함정과 팁

  • 버전 강제 override 주의. BOM이 관리하는 라이브러리의 버전을 임의로 올리면, BOM이 보장하던 호환 집합을 벗어나 미묘한 충돌이 날 수 있습니다. 꼭 필요할 때만, 영향 범위를 확인하고 바꿉니다.
  • 전이 의존성 충돌 확인. 같은 라이브러리를 서로 다른 버전이 끌고 오면 충돌합니다. ./gradlew dependencyInsight --dependency <name> 또는 mvn dependency:tree로 실제 해석된 버전을 확인합니다.
  • 무엇이 왜 들어왔는지 본다. fat jar의 BOOT-INF/lib가 비대해지면 의존성 트리를 점검해 불필요한 것을 줄입니다.

fat jar는 Spring Boot가 실행할 classpath를 고정한 산출물이고, BOM은 그 안의 dependency version을 함께 관리합니다. Spring Security의 filter chain은 Spring Security 기본에서, 버전 업그레이드 뒤 runtime 동작 변화는 JVM Spring capstone에서 이어집니다.

BOM과 version pinning은 재현 가능한 build의 기반이며, dependency tree는 SBOM과 취약점 scan이 실제로 어떤 library를 다루는지 보여 주는 출발점입니다.


5. 참고 자료

궁금하신 점이나 추가해야 할 부분은 댓글이나 아래의 링크를 통해 문의해주세요.
Written with KKamJi

This post is licensed under CC BY 4.0 by the author.