본문으로 건너뛰기
김신건의 로그

[Spring Boot] Build Plugins: Maven, Gradle, Executable JAR, OCI Image

· 수정 · 📖 약 2분 · 620자/단어 #spring-boot #maven #gradle #build #packaging #oci #buildpacks
Spring Boot Maven Plugin, Spring Boot Gradle Plugin, Executable JAR, Layered JAR, OCI image, bootBuildImage, 스프링 부트 빌드

정의

Spring Boot 의 빌드 플러그인은 표준 jar 가 아닌 executable jar (fat jar / uber jar) 생성. 추가로 OCI 이미지, native image, layered jar 등 production 패키징 지원.

핵심 goal:

  • Executable JAR: java -jar app.jar 로 실행 가능 (내장 Tomcat 포함)
  • Layered JAR: Docker layer 최적화
  • OCI Image (bootBuildImage): Dockerfile 없이 Cloud Native Buildpacks 로 이미지 생성
  • Native Image: GraalVM AOT 컴파일

Maven Plugin 기본

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <version>${spring-boot.version}</version>
            <configuration>
                <mainClass>com.example.MyApplication</mainClass>
                <executable>true</executable>
                <layers>
                    <enabled>true</enabled>
                </layers>
            </configuration>
        </plugin>
    </plugins>
</build>

주요 goal:

  • mvn spring-boot:run : 개발 실행
  • mvn package : executable jar 빌드 (target/app-1.0.0.jar)
  • mvn spring-boot:build-image : OCI 이미지 생성
  • mvn spring-boot:start / :stop : background 실행 (통합 테스트용)

Gradle Plugin 기본

plugins {
    id("org.springframework.boot") version "3.4.0"
    id("io.spring.dependency-management") version "1.1.7"
    java
}

springBoot {
    mainClass.set("com.example.MyApplication")
    buildInfo()    // META-INF/build-info.properties 생성
}

주요 task:

  • ./gradlew bootRun : 개발 실행
  • ./gradlew bootJar : executable jar 빌드 (build/libs/app-1.0.0.jar)
  • ./gradlew bootBuildImage : OCI 이미지 생성
  • ./gradlew bootBuildInfo : build info 생성

Executable JAR 구조

app.jar (zip)
├── META-INF/
│   ├── MANIFEST.MF              ← Main-Class: org.springframework.boot.loader.JarLauncher
│   └── build-info.properties
├── BOOT-INF/
│   ├── classes/                 ← 내 코드 + 리소스
│   ├── lib/                     ← 모든 의존성 jar
│   └── classpath.idx
└── org/springframework/boot/loader/   ← JarLauncher

JarLauncherBOOT-INF/classes + BOOT-INF/lib/*.jar 를 classpath 에 추가 후 MyApplication.main() 호출. nested jar 지원.

Layered JAR (Docker 최적화)

기본 executable jar 는 하나의 큰 layer = 의존성 한 줄 바뀌어도 전체 layer 재빌드.

Layered 활성화:

tasks.bootJar {
    layered {
        enabled.set(true)
    }
}

생성된 jar 안에 BOOT-INF/layers.idx:

- "dependencies":
  - "BOOT-INF/lib/spring-core-6.x.jar"
  - ...
- "spring-boot-loader":
  - "org/springframework/boot/loader/"
- "snapshot-dependencies":
  - "BOOT-INF/lib/my-snapshot.jar"
- "application":
  - "BOOT-INF/classes/"
  - "META-INF/"

추출:

java -Djarmode=layertools -jar app.jar extract --destination extracted

Dockerfile:

FROM eclipse-temurin:21-jre AS builder
WORKDIR /app
COPY app.jar app.jar
RUN java -Djarmode=layertools -jar app.jar extract --destination extracted

FROM eclipse-temurin:21-jre
WORKDIR /app
COPY --from=builder /app/extracted/dependencies/ ./
COPY --from=builder /app/extracted/spring-boot-loader/ ./
COPY --from=builder /app/extracted/snapshot-dependencies/ ./
COPY --from=builder /app/extracted/application/ ./
ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]

dependency layer 가 자주 안 바뀌어 cache hit. application layer 만 매 빌드 변경 → push 시간 단축.

OCI Image (Buildpacks, Dockerfile 없이)

mvn spring-boot:build-image
# 또는
./gradlew bootBuildImage

자동 생성:

  • JDK 선택 (Paketo Buildpack)
  • 멀티 stage 최적화
  • non-root user
  • CA cert / timezone
  • entrypoint

설정:

tasks.bootBuildImage {
    imageName.set("my-app:${version}")
    builder.set("paketobuildpacks/builder-jammy-base")
    environment.set(mapOf(
        "BP_JVM_VERSION" to "21",
        "BPE_DELIM_JAVA_TOOL_OPTIONS" to " ",
        "BPE_APPEND_JAVA_TOOL_OPTIONS" to "-XX:+UseG1GC"
    ))
    publish.set(false)
}

docker build 보다 훨씬 빠르고 보안 강화. Spring 권장.

Native Image (GraalVM AOT)

plugins {
    id("org.graalvm.buildtools.native") version "0.10.6"
}

graalvmNative {
    binaries {
        named("main") {
            imageName.set("my-app")
            mainClass.set("com.example.MyApplication")
            buildArgs.add("--no-fallback")
        }
    }
}
./gradlew nativeCompile      # 실행 binary
./gradlew nativeBuildImage   # native image OCI

장점: 시작 30ms, 메모리 절감 (~100MB → ~50MB). 단점:

  • 빌드 오래 (수 분~10분)
  • reflection 메타데이터 명시 (Spring 6.x 가 자동 hint 생성)
  • 일부 라이브러리 호환성

서버리스 (AWS Lambda) / 빠른 스케일 환경에 적합. spring-native 참고.

Build Info (Actuator info endpoint 노출)

springBoot {
    buildInfo {
        properties {
            artifact.set("my-app")
            version.set(project.version.toString())
            additional.set(mapOf(
                "commit" to gitCommit(),
                "buildTime" to Instant.now().toString()
            ))
        }
    }
}
management:
  info:
    git:
      mode: full
    build:
      enabled: true
  endpoints:
    web:
      exposure:
        include: info

/actuator/info 에 build/git 정보 노출. 운영 환경에서 버전 추적.

의존성 BOM

dependencies {
    implementation(platform("org.springframework.boot:spring-boot-dependencies:3.4.0"))
    implementation("org.springframework.boot:spring-boot-starter-web")
    // 버전 명시 안 함, BOM 이 관리
}

Spring Boot BOM 이 호환되는 라이브러리 버전 결정. 충돌 회피. Maven 은 dependencyManagement 로 동일.

CI / CD 최적화

# .github/workflows/build.yml
- uses: gradle/actions/setup-gradle@v4
  with:
    cache-disabled: false
- run: ./gradlew bootJar
- run: ./gradlew bootBuildImage --publishImage \
    -Pjib.to.image=ghcr.io/${{ github.repository }}:${{ github.sha }}

bootBuildImage 가 Docker daemon 또는 docker-less (Kaniko) 환경 모두 지원.

함정과 베스트 프랙티스

  • executable jar 권장: WAR 는 legacy. embedded 서버 활용
  • layered jar + Dockerfile 또는 bootBuildImage: 둘 중 하나. Buildpacks 가 권장
  • native image 는 빌드 시간 비용: 모든 빌드 X, release 만
  • BOM 의존성 관리: 직접 버전 명시는 충돌 위험
  • spring-boot-configuration-processor: IDE 자동완성
  • build-info + git-commit-id-plugin: 운영 환경 버전 추적
  • bootRun--args=...: 개발 중 profile 전환
  • Gradle parallel + build cache: 빌드 속도 개선
  • dev tools : production 에서 제외 (developmentOnly)

관련 위키

이 글의 용어 (6개)
[Spring Boot] 자동 구성과 @SpringBootApplicationspring
정의 Spring Boot의 핵심 가치 = 자동 구성(autoconfiguration). classpath의 라이브러리를 보고 합리적 기본 설정으로 Bean 등록. Tomcat이…
[Spring Boot] Deployment: 전통 WAR, Cloud, Systemdspring
정의 컨테이너 / K8s 외의 Spring Boot 배포 옵션: - Traditional WAR: 외부 Tomcat / Jetty (legacy 환경) - systemd serv…
[Spring Boot] Docker / Kubernetes 배포spring
정의 Spring Boot 앱을 컨테이너로 배포. 두 가지 주류: - Docker / Docker Compose: 단일 호스트, 개발 + 소규모 운영 - Kubernetes: 멀…
[Spring Boot] Properties 외부화: PropertySource, Profile, @ConfigurationPropertiesspring
정의 Spring Boot 는 설정값을 15가지 소스에서 우선순위로 병합. 같은 키가 여러 곳에 있으면 우선순위 높은 게 이김. 코드 변경 없이 환경별 설정 가능. 핵심 소스 (…
[Spring] Actuator: health, metrics, infospring
정의 Spring Boot Actuator는 프로덕션용 monitoring/management endpoint 자동 제공. health check, metrics, env, be…
[Spring] Native Image: GraalVM, 빠른 시작spring
정의 Spring Native (3.0+)는 Spring Boot 앱을 GraalVM Native Image로 컴파일해 즉시 시작·낮은 메모리·작은 바이너리를 얻는다. JIT 없…

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기