Skip to content

Instantly share code, notes, and snippets.

@ihoneymon
Created October 15, 2018 01:07
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ihoneymon/7f4ac057986ebea9c3334671214aac67 to your computer and use it in GitHub Desktop.
Save ihoneymon/7f4ac057986ebea9c3334671214aac67 to your computer and use it in GitHub Desktop.

[springboot] 스프링 부트 플러그인: spring-boot-gradle-plugin

Note
Boot Spring Boot

Boot Spring Boot! 출간 이후 발표할 기회가 몇 번 있었고 그 때마다 개발자 인생은

B(uild) - C(oding) - D(eploy)

이라고 이야기 하고 있다. 개발자는 빌드(Build)와 배포(Deploy) 사이에 코드를 작성한다.

스프링 부트를 기반으로 한 개발자는 지속적으로 자신이 작성한 코드를 빌드하여 배포하는데, 빌드동작을 제대로 이해하지 못하는 경우가 많다. 책 홍보도 할겸(!!!) 스프링 부트 빌드 플러그인의 동작방식에 대한 이야기를 풀어보고자 한다(이걸 작성하면서도 내게 공부가 되었다).


스프링 부트는 자바에서 사용하는 범용적인 빌드 도구로 그레이들과 메이븐을 지원한다(ANT 등을 지원하지만 그건 거론하지 않는다). '지원한다’는 이야기는 각 도구별로 스프링 부트에 빌드 및 배포에 필요한 플러그인을 제공한다는 뜻이다. 스프링 부트는 컨테이너를 내장하여 실행가능한 JAR로 배포하기 위한 리패키징 과정을 거치게 되는데 스프링 부트 빌드도구 플러그인(그레이들, 메이븐)이 담당한다.

Note

스프링 부트 그레이들 플러그인을 기준으로 설명한다.

  • bootRepackage: 1.5 까지 사용된 스프링 부트 태스크

  • bootJar(or bootWar): 자바 압축방식 jar와 war 를 지원하는 태스크가 분리되었다.

스프링 부트 2.0 부터 스프링 부트 그레이들 플러그인에서는 bootRepackage 가 각각 jar 태스크를 확장한 bootJarwar 태스크를 확장한 bootWar로 분리되었다.

bootJarjar 태스크를, bootWarwar 태스크를 비활성화한다.

Note

jar 혹은 war 태스크를 사용하기 위해서는 다음과 같이 명시적으로 태스크를 활성화시켜야 한다.

jar {
  enabled = true
}

멀티 모듈 프로젝트에서 다른 모듈에서 참조하는 참조 모듈인 경우에는 굳이 스프링 부트 리패키징 과정을 거칠 필요가 없기 때문에 다음과 같이 선언한다.

bootJar {
  enabled = false
}

jar {
  enabled = true
}

스프링 부트 배포파일 구조

스프링 부트 리패키징을 통해 생성된 실행가능한 JAR는 크게 3개 부분으로 나눠 압축처리한다.

  • META-INF: 자바 애플리케이션 메타 정보 제공

  • org.springframework.boot.classloader: 스프링 부트 클래스 로더

  • BOOT-INF 애플리케이션 실행에 필요자원 제공(컴파일된 바이트코드, 리소스, 의존 라이브러리)

실제 생성되는 애플리케이션 배포파일을 풀어보면 대략 다음과 같은 구조를 가진다.

boot-spring-boot.jar
 +-META-INF // (1)
 |  +-MANIFEST.MF
 +-org
 |  +-springframework
 |     +-boot
 |        +-loader // (2)
 |           +-<spring boot loader classes>
 +-BOOT-INF // (3)
    +-classes
    |  +-io.honeymon.boot.springboot
    |     +-BootSpringBoot.class
    +-lib
       +-dependency1.jar
       +-dependency2.jar

스프링 부트 빌드 플러그인이 생성하는 실행가능한 JAR는 배포파일에 대한 사양(Jar File Specification)을 따른다.

META-INF 디렉터리에는 자바 플랫폼에서 실행되는 애플리케이션에 대한 구성, 확장, 클래스 로더 및 서비스 등을 등록하는 MANIFEST.MF 파일이 생성된다. 이 파일을 생성할 때 추가할 수 있는 속성은 다음과 같이 스프링 부트 그레이들 플러그인을 통해 제공하는 bootJar.manifest 를 추가 정의가능하다.

bootJar {
    manifest {
        attributes("Implementation-Title": "${project.name}", // (1)
                "Implementation-Version": "${project.version}") // (2)
    }
}

이렇게 정의된 속성은 빌드를 통해 다음과 같이 MANIFEST.MF에 추가된다.

Manifest-Version: 1.0
Implementation-Title: boot-spring-boot
Implementation-Version: 1.0.0.RELEASE
Start-Class: io.honeymon.boot.springboot.BootSpringBootApplication // (1)
Main-Class: org.springframework.boot.loader.JarLauncher //(2)
  1. 실행되어야 할 애플리케이션 지정

  2. 자바 메인 클래스로 선언된 것은 JarLauncher다. 이 속성이 실행되어 BootSpringBootApplication를 적재하고 실행한다.

이렇게 추가된 정보는 애플리케이션이 실행될때 활용되며 다음과 같이 배너(banner.txt)에 정의하여 사용할 수 있다.

   ___            __    ____         _             ___            __
  / _ )___  ___  / /_  / __/__  ____(_)__  ___ _  / _ )___  ___  / /_
 / _  / _ \/ _ \/ __/ _\ \/ _ \/ __/ / _ \/ _ `/ / _  / _ \/ _ \/ __/
/____/\___/\___/\__/ /___/ .__/_/ /_/_//_/\_, / /____/\___/\___/\__/
                        /_/              /___/

* Github Repository: https://github.com/ihoneymon/boot-spring-boot
* Application: ${application.title}${application.formatted-version}, Spring Boot Version:${spring-boot.formatted-version} // (1)
  1. application.titleapplication.formatted-version 외에 다른 속성을 활용하고 싶은 경우는 스프링 부트 배너 사용자 정의 사용부분을 읽어보기 바란다.

  • application.titleMANIFEST.MF파일에서 Implementation-Title: boot-spring-boot를 읽어온다.

  • application.formatted-versionMANIFEST.MF파일에서 Implementation-Version: 1.0.0.RELEASE를 읽어오며 접두어 v를 추가한다.

이렇게 작성된 배너가 리패키징된 jar를 실행하면 다음과 같이 출력되는 것을 확인할 수 있다.

   ___            __    ____         _             ___            __
  / _ )___  ___  / /_  / __/__  ____(_)__  ___ _  / _ )___  ___  / /_
 / _  / _ \/ _ \/ __/ _\ \/ _ \/ __/ / _ \/ _ `/ / _  / _ \/ _ \/ __/
/____/\___/\___/\__/ /___/ .__/_/ /_/_//_/\_, / /____/\___/\___/\__/
                        /_/              /___/

* Github Repository: https://github.com/ihoneymon/boot-spring-boot
* Application: boot-spring-boot (v1.0.0.RELEASE), Spring Boot Version: (v2.0.4.RELEASE)

스프링 부트에서는 3가지 유형의 런처(JarLauncher, WarLauncher 그리고 PropertiesLauncher)를 제공한다. 이 런처의 목적은 실행가능한 JAR 파일에 포함되어 의존 라이브러리로부터 있는 자원(.class 및 기타 파일)을 적재하는 것이다. JarLauncherBOOT-INF/lib를 찾아보고 WarLauncherWEB-INF/lib를 찾아본다. PropertiesLauncher의 경우 BOOT-INF/lib를 탐색하며 환경변수 LOADER_PATH 혹은 loader.properties 파일에서 loader.path 속성을 통해 정의할 수 있다.

jar 로 배포하는 경우에는 JarLauncher가 기본구성된다. war 로 배포하는 경우에는 WarLauncher가 기본구성된다.

실행할 런처를 사용자 정의하는 방식은 다음과 같다.

build.gradle
bootWar {
	manifest {
		attributes 'Main-Class': 'org.springframework.boot.loader.PropertiesLauncher'
	}
}

BOOT-INF

애플리케이션 실행에 필요한 메인 애플리케이션 클래스(ex: BootSpringBoot)를 비롯 실행하는데 필요한 애플리케이션 속성 파일 및 의존 라이브러리, 자원을 포함하고 있다. 개발자가 작성한 코드가 담겨있는 영역이라고 보면 된다.

각 디렉터리별 구성은 다음과 같다.

  • classes:: 개발자 작성 코드

    • application.yml

    • banner.txt

    • git.properties

    • META-INF:: 애플리케이션 메타정보를 가진 파일들이 위치한다.

      • build-info.properties:: 빌드정보 파일

      • spring-configuration-metadata.json:: spring-boot-configuration-processor가 생성한 사용자 정의 애프리케이션 속성으로 @ConfigurationProperties 애너테이션을 선언하고 작성한 클래스 정보를 추출하여 생성

      • spring.factories:: 스프링 부트 기동시 실행되어야 하는 리스너나 자동구성 등 정의

    • 템플릿 및 정적 자원: templates, static 등 포함

  • lib: 애플리케이션 실행에 필요한 의존성 라이브러리 위치

정리

스프링 부트 빌드 플러그인은 실행가능한 jar 혹은 war로 배포하는 경우 리패키징이라는 과정을 통해 패키징된 애플리케이션 배포파일을 실행할 내장 컨테이너(톰캣, 언더토우 등)와 함께 애플리케이션 및 애플리케이션이 가지는 의존 라이브러리를 하나의 묵직한 jar(Fat jar 라고도 부르는)로 재압축하는 과정을 거치게 된다. 이 과정에서 실행가능한 jar 형식에 맞춰서 파일을 재구성한다.

이 재구성되는 방식을 이해하면 애플리케이션 실행시 메타정보를 정의하고 이를 활용하는 것도 가능해진다. 스프링 부트 애플리케이션이 실행될 때 메인 클래스 및 의존 라이브러리들이 적재되는 과정도 이해할 수 있다.

굳이 암기할 필요는 없다. 아~ 이렇게 되는구나 하고 이해만 해도 충분하다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment