Spring boot에서의 Gradle

appti·2021년 11월 3일
2

Gradle

목록 보기
5/5

Spring Initializr

Spring Initializr는 웹 기반 도구이며, 이를 통해 Spring boot Project의 구조를 쉽게 생성할 수 있다.

JVM 기반 프로젝트 생성을 위한 확장 가능한 API를 제공한다.

또한 메타 데이터 모델로 표현되는 프로젝트에 대한 다양한 옵션을 제공한다.
메티 데이터 모델을 사용하여 JVM 및 플랫폼 버전 등에서 지원하는 종속성 목록을 구성할 수 있다.

Project UI

Spring Initializr UI에는 다음과 같은 항목들이 존재한다.

  • Project

    • 프로젝트에서 필요한 라이브러리를 관리해주고, 빌드 라이프 사이클을 관리해주는 툴을 선택한다.
    • Maven/Gradle을 선택할 수 있으며, 레거시는 Maven이지만 최근에는 Gradle을 사용한다.
  • Language

    • 사용할 프로그래밍 언어를 선택한다.
    • Java, Kotlin, Groovy를 선택할 수 있다.
  • Spring Boot

    • 사용할 스프링 부트 버전을 선택한다.
    • 버전 뒤에 표기된 접미사는 소프트웨어 배포 생명 주기를 의미한다.
    • GA 또는 아무런 접미사가 없는 버전을 선택한다.
    • 스프링, 스프링 부트 버전 접미사 참고
  • Project Metadata

    • 프로젝트와 관련된 정보를 포함한 메타 데이터를 작성한다.
      • Group : 패키지 이름을 의미한다. 주로 기업명(기업 도메인)을 ㅈ거어준다.
      • Artifact : 애플리케이션 이름을 의미한다.
      • Name : Artifact와 동일하다.
      • Description : 해당 필드에 프로젝트에 대한 설명을 작성할 수 있다.
      • Package name : 생성될 프로젝트의 패키지 이름이다.
        Group + Name(혹은 Artifact)이 합쳐진 내용이다.
      • Packaging : 프로젝트를 생성할 때 어떤 확장자로 생성할 것인지를 선택한다.
      • Java : 자바 버전을 선택한다.
  • Dependencies

    • 스프링 부트 기반으로 프로젝트를 생성할 때 어떤 라이브러리를 사용할 지 선택한다.
    • 선택하지 않을 경우, spring-boot-starter만 의존한다.

프로젝트 import

Spring Initializr를 통해 생성된 프로젝트의 디렉토리는 다음과 같이 gradle(wrapper)와 java 패키지만 있는 상황이다.

프로젝트를 import 하기 위해 생성된 프로젝트를 압축 해제하고, build.gradle을 통해 프로젝트를 open하면 빌드가 진행된다.

정상적으로 빌드가 성공되었음을 확인할 수 있다.

다음과 같이 .gradle, build.gradle, gradlew, gradlew.bat, settings.gradle이 생성된 것을 확인할 수 있다.

Gradle

Gradle은 유연한 오픈 소스 빌드 자동화 도구이다.

Gradle 빌드 로그

Starting Gradle Daemon...
Gradle Daemon started in 2 s 362 ms
> Task :prepareKotlinBuildScriptModel UP-TO-DATE

BUILD SUCCESSFUL in 17s

프로젝트를 import 한 뒤 빌드를 실행했을 때 콘솔에 출력된 로그이다.

Gradle Daemon

Starting Gradle Daemon...
Gradle Daemon started in 2 s 362 ms

Gradle은 JVM에서 실행되고, 초기화 시간이 필요한 여러 지원 라이브러리를 사용하다 보니 속도가 느릴 수 있다.
이를 해결하기 위한 대안이 Gradle Daemon이다.

Gradle Daemon은 수명이 긴 프로세스로 JVM startup 비용을 줄이고 프로젝트 정보를 메모리에 캐싱하는 식으로 동작한다.

이전 빌드의 계산을 재사용하여 빌드 속도를 향상시키며, 빌드 시간을 15 ~ 75% 단축시킬 수 있다.

기본적으로 활성화 되어있는 상태이다.
gradle --status로 활성화되어있는 Gradle Daemon을 확인할 수 있으며, gradle --no-daemon로 옵션을 변경할 수 있다.

Task

태스크는 Gradle의 프로젝트를 구성하는 요소로 빌드가 실행하는 최소 단위의 작업(atomic piece of work)를 의미한다.

태스크에는 Action이라는 실행 가능한 코드를 포함한 블록을 통해 빌드를 진행한다.

> Task :prepareKotlinBuildScriptModel UP-TO-DATE

prepareKotlinBuildScriptModel라는 이름의 태스트로 인해 프로젝트를 빌드했을 때 gradle 관련 파일들을 생성한 것을 확인할 수 있다.

해당 태스크는 KotlinDslScriptsModel를 빌드하기 전에 실행해야 하는 사전 준비 태스크이다.


PREPARATION_TASK_NAME 필드에 의해 요구되는 것을 확인할 수 있다.

그러므로 prepareKotlinBuildScriptModel 태스크를 실행한 뒤 KotlinDslScriptsModel을 통해 Kotlin DSL Script를 빌드한다고 볼 수 있다.

UP-TO-DATE는 console UI와 Tooling API를 이용한 라벨로, 태스크로 인해 출력이 변경되지 않았다는 의미이다.


그 외의 자바에서 사용되는 태스크 목록은 ./gradlew task --all 명령어를 실행한 뒤 Other tasks 목록에서 확인할 수 있다.

Project root directory

Gradle 공식 홈페이지에서 명시한 Project root directory의 구성은 다음과 같다.

├── .gradle 
│   ├── 4.8 
│   ├── 4.9 
│   └── ⋮
├── build 
├── gradle
│   └── wrapper 
├── gradle.properties 
├── gradlew 
├── gradlew.bat 
├── settings.gradle 
├── subproject-one 
|   └── build.gradle  
├── subproject-two 
|   └── build.gradle 
└── ⋮
  • .gradle : Gradle에서 생성한 프로젝트별 캐시 디렉토리
    • 4.8, 4.9... : 버전별 캐시(증분 빌드 지원 등)
  • build : 해당 프로젝트의 빌드 디렉토리
  • gradle/wrapper : JAR 파일 및 Gradle Wrapper 구성
  • gradle.properties : 프로젝트별 Gradle 구성 속성
  • gradlew/gradlew.bat : Gradle Wrapper를 사용하여 빌드를 실행하기 위한 스크립트
  • settings.gradle : 하위 프로젝트 목록이 정의된 프로젝트의 설정 파일
  • subproject-one/build.gradle : 빌드 스크립트(하나의 서브 프로젝트마다 하나씩 존재)


현재 프로젝트 구조와는 차이점(gradle.properties)이 있는데, 그 이유는 프로젝트를 빌드할 때 Gradle Wrapper를 사용했기 때문이다.

Gradle Wrapper

Gradle Wrapper는 Gradle 빌드를 실행하는 권장 방법이며, 선언된 버전의 Gradle을 호출하여 필요하다면 Gradle의 설치 없이 프로젝트의 Gradle 작업을 수행할 수 있도록 하는 script를 의미한다.

Gradle Wrapper의 동작 과정은 위 그림과 같다.

Gradle Wrapper 추가

모든 바닐라 Gradle 빌드에는 wrapper 태스크가 존재하기 때문에, 커맨드 옵션으로 Gradler Wrapper를 추가할 수 있다.

gradle wrapper

해당 명령어로 간단하게 생성할 수 있으며, 다음과 같은 옵션을 부여할 수 있다.

  • --gradle-version
    • Gradle 버전을 의미한다.
  • --distribution-type
    • Wrapper에 사용되는 Gradle 배포 유형을 의미한다.
      사용 가능한 옵션은 binall이며, 기본값은 bin이다.
    • bin은 런타임만 포함하고 샘플 코드와 설명서는 포함하지 않는 배포이다.
    • all은 샘플 코드와 설명서까지 포함하는 배포이다.
  • --gradle-distribution-url
    • Gradle 배포 ZIP 파일을 가리키는 전체 URL을 의미한다.
    • 회사 네트워크 내에서 Gradle 배포를 호스팅하려는 경우 유용하다.
  • --gradle-distribution-sha256-sum
    • 다운로드한 Gradle 배포의 유효성을 확인하는 데 사용되는 SHA256 해시 합계이다.
gradle wrapper --gradle-version 7.2 --distribution-type all

위와 같은 식으로 명령어를 사용할 수 있다.

Gradle Wrapper directory

Gradle Wrapper의 디렉토리 구조는 다음과 같다.

├── a-subproject
│   └── build.gradle
├── settings.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
└── gradlew.bat

Gradle 프로젝트는 각 하위 프로젝트에 대해 settings.gradle파일과 하나의 build.gradle을 제공한다.
Wrapper 파일은 gradle 디렉토리와 루트 디렉토리에 나란히 존재한다.

다음은 wrapper 파일의 역할을 정리한 목록이다.

  • gradle-wrapper.jar
    • Gradle 배포를 다운로드하기 위한 코드가 포함된 Wrapper JAR 파일이다.
  • gradle-wrapper.properties
    • Wrapper 런타임 동작 구성을 담당하는 Properties 파일이다.
  • gradlew, gradlew.bat
    • Wrapper로 빌드를 실행하기 위한 쉘 스크립트나 윈도우 배치 스크립트이다.

Using the Gradle Wrapper

빌드를 안정적이고 표준화된 실행을 보장하기 위해 항상 Wrapper로 빌드를 실행하는 것을 권장한다.

./gradlew.bat

./gradlew

위와 같이 명령어를 입력하면 프로젝트 내에 gradle-wrapper.jar를 이용하여 빌드를 진행한다.

gradle/wrapper/gradle-wrapper.properties

Gradle Wrapper의 설정 파일이며, 데이터를 변경할 경우 task 실행 시 자동을 wrapper 파일을 로컬 캐시에 다운로드 받는다.

내용은 다음과 같다.

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

gradle/wrapper/gradle-wrapper.jar

gradlew/gradlew.bat 파일을 실행하면 프로젝트 내에 설치된 해당 파일을 사용하여 gradle task를 진행한다.
그렇기 때문에 로컬 환경의 영향을 받지 않는다.

실제로는 Wrapper 버전에 맞는 구성들을 로컬 캐시에 다운로드 받는다.
다운로드 받을 때 gradle-wrapper.properties에 설정한 영향을 받는다.
그 위치는 %GRADLE_USER_HOME%/.gradle/wrapper/dists/gradle-version-bin/shasum/gradle-version/lib/plugins이다.

build.gradle

실제 빌드를 할 때 실행되는 빌드 스크립트이다.

내용은 다음과 같다.

plugins {
	id 'org.springframework.boot' version '2.5.6'
	id 'io.spring.dependency-management' version '1.0.11.RELEASE'
	id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

test {
	useJUnitPlatform()
}

Plugin

Plugin은 특정 기능을 수행하는 유용한 태스크들의 집합이다.

Plugin의 종류는 바이너리 플러그인스크립트 플러그인으로 나뉘며, Spring Initializr를 통해 생성되는 build.gradle의 경우 바이너리 플러그인을 사용한다.

Binary plugin

바이너리 플러그인은 Plugin 인터페이스를 구현하여 프로그래밍 방식으로 작성되거나 Gradle의 DSL 언어 중 하나를 사용하여 선언적으로 작성된다.
바이너리 플러그인은 빌드 스크립트 내, 프로젝트 계층 내, 또는 플러그인 jar 외부에 있을 수 있다.

DSL

DSL은 Domain Specific Language의 약자로, 특정 도메민(산업, 분야 등 특정 영역)에 특화된 언어를 의미한다.
DSL은 특정 영역의 문제 해결에는 그 영역에 맞는 특화된 도구를 사용하자는 취지에서 등장했다.

장점으로는 반복이 제거되고 비슷한 처리 코드는 템플릿 처리가 가능하다는 점이 있다.
단점으로는 설계가 어렵고, 하위 호환성을 유지해야 한다는 점이 있다.

Using plugins

플러그인을 사용하기 위해서는 두 단계가 필요하다.

  1. 플러그인을 귀결(resolve)해야 한다.
  2. 플러그인을 대상(일반적으로는 Project)에 적용(apply)해야 한다.

플러그인을 귀결(Resolving a plugin)한다는 것은 주어진 플러그인을 포함하는 올바른 버전의 jar를 찾아 스크립트 클래스 경로에 추가하는 것을 의미한다.
플러그인이 귀결되면 해당 API를 빌드 스크립트에서 사용할 수 있다.
여기서 Gradle 배포의 일부로 제공되는 핵심 바이너리 플러그인(java 등)은 자동으로 귀결된다.

플러그인을 적용(Applying a plugin)한다는 것은 플러그인을 사용하고자 하는 프로젝트에서 플러그인의 Plugin.apply(T)를 실행하는 것을 의미한다.

DSL 플러그인을 사용하여 한 번에 플러그인을 귀결하고 적용하는 것을 권장한다.

plugins {
	id 'org.springframework.boot' version '2.5.6'
	id 'io.spring.dependency-management' version '1.0.11.RELEASE'
	id 'java'
}

build.gradle의 plugins 블록은 다음과 같다.

Binary plugins

바이너리 플러그인의 경우 플러그인의 전역 고유 식별자 또는 이름을 의미하는 플러그인 ID를 통해 적용한다.
Core Gradle 플러그인(핵심 바이너리 플러그인)은 JavaCompile을 java와 같이 짧은 이름으로 제공한다.
즉 일부 레거시 플러그인은 짧고 정규화되지 않은 형식을 사용할 수 있다는 것이다.
다른 모든 바이너리 플러그인은 정규화된 형식의 플러그인 ID(com.github.foo.bar)를 사용해야 한다.

Locations of binary plugins

플러그인은 단순히 Gradle의 플러그인 인터페이스를 구현하는 모든 클래스이다,
Gradle은 JavaPlugin 배포의 일부로 핵심 플러그인을 제공하기 때문에 귀결할 필요가 없다.

그러나 비핵심 바이너리 플러그인을 적용하려면 먼저 플러그인을 귀결해야 하며, 방법은 다음과 같다.

  • 플러그인 포털 또는 DSL 플러그인을 사용하는 사용자 지정 저장소의 플러그인 포함
  • buildscript 종속성으로 정의된 외부 jar의 플러그인 포함
  • 플러그인을 프로젝트의 buildSrc 디렉토리 아래에 소스 파일로 정의
  • 플러그인을 빌드 스크립트 내에서 인라인 클래스 선언으로 정의

Spring Initializr의 경우 DSL 플러그인을 사용했다.

Limitations of the plugins DSL

plugins {}블록의 형식은 다음과 같다.

plugins {
    id «plugin id»                                            
    id «plugin id» version «plugin version» [apply «false»]   
}

id «plugin id»의 경우 빌드 스크립트에서 이미 사용 가능한 플러그인이거나 핵심 Gradle 플러그인이어야 한다.
id «plugin id» version «plugin version» [apply «false»]의 경우 귀결이 필요한 바이너리 Gradle 플러그인의 경우에 사용한다.

apply 옵션을 통해 귀결 이후 즉시 적용되지 않도록 설정할 수 있다.

Java Plugin

Java 플러그인은 프로젝트에 테스트, 번들링 기능과 함께 Java 컴파일을 추가한다.
즉 현재 프로젝트에 Java 애플리케이션 빌드에 필요한 기능을 추가하는 역할을 한다고 볼 수 있다.

Project, JavaCompile

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

build.gradle에서의 처음 두 가지 정보는 Project에서 요구하는 Property이며, 나머지 하나는 JavaCompile에서 요구하는 Property를 의미한다.

  • group
    • 해당 프로젝트의 그룹을 의미한다.
    • Gradle은 항상 그룹의 toString() 값을 사용한다.
    • 그룹은 기본적으로 점이 구분자로 포함된 경로이다.
  • version
    • 해당 프로젝트의 버전을 의미한다.
    • Gradle은 항상 버전 toString() 값을 사용한다.
    • 기본값은 unspecified 이다.
  • sourceCompatibility
    • 소스 파일을 컴파일하는데 사용할 Java 언어 버전이다.

repositories

Gradle은 repositories를 사용하여 하나 이상의 리포지토리를 통해 종속성을 해결할 수 있다.

Declaring a publicly-available repository

공개 바이너리 리포지토리인 Maven Central 및 Google Android 리포지토리를 통해 종속성을 해결할 수 있다.

repositories {
	mavenCentral()
}

Spring Initializr에서는 Maven Central로 종속성을 해결하고 있음을 확인할 수 있다.

dependencies

Java 플러그인은 다음과 같이 여러 종속성 구성을 추가한다.

  • implementation

    • 구현 전용 종속성
  • compileOnly

    • 런타임에 사용되지 않는 컴파일 시간 전용 종속성
  • compileClasspath extends compileOnly, implementation

    • 소스를 컴파일할 때 사용되는 컴파일 클래스 경로
    • compileJava 태스크에서 사용
  • runtimeOnly

    • 런타임 전용 종속성
  • runtimeClasspath extends runtimeOnly, implementation

    • 런타임 클래스 경로에는 구현 요소와 런타임 전용 요소가 포함
  • testImplementation extends implementation

    • 테스트에 대한 구현 전용 종속성
  • testCompileOnly

    • 테스트 컴파일을 위한 추가 종속성
    • 런타임에는 사용되지 않음
  • testCompileClasspath extends testCompileOnly, testImplementation

    • 테스트 소스를 컴파일할 때 사용되는 테스트 컴파일 클래스 경로
    • compileTestJava 태스크에서 사용
  • testRuntimeOnly extends runtimeOnly

    • 테스트 실행을 위한 런타임 전용 종속성
  • testRuntimeClasspath extends testRuntimeOnly, testImplementation

    • 테스트 실행을 위한 런타임 클래스 경로
    • test 태스크에서 사용
  • archives

    • 해당 프로젝트에서 생성된 아티팩트
    • 빌드 실행 시 기본 태스크를 결정하기 위해 Gradle에서 사용
  • default extends runtimeElements

    • 해당 프로젝트에 대한 프로젝트 종속성이 사용하는 기본 구성
    • 런타임 시 해당 프로젝트에 필요한 아티팩트 및 종속성을 포함

이를 그림으로 표현하면 다음과 같다.

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

spring-boot-starter의 경우 implementation이므로 컴파일, 런타임 경로에 적용된다.
spring-boot-starter-test의 경우 testImplementation이므로 테스트 컴파일, 테스트 런타임 경로에 적용된다.

profile
안녕하세요

0개의 댓글