Spring Initializr는 웹 기반 도구이며, 이를 통해 Spring boot Project의 구조를 쉽게 생성할 수 있다.
JVM 기반 프로젝트 생성을 위한 확장 가능한 API를 제공한다.
또한 메타 데이터 모델로 표현되는 프로젝트에 대한 다양한 옵션을 제공한다.
메티 데이터 모델을 사용하여 JVM 및 플랫폼 버전 등에서 지원하는 종속성 목록을 구성할 수 있다.
Spring Initializr UI에는 다음과 같은 항목들이 존재한다.
Project
Language
Spring Boot
Project Metadata
Dependencies
Spring Initializr를 통해 생성된 프로젝트의 디렉토리는 다음과 같이 gradle(wrapper)와 java 패키지만 있는 상황이다.
프로젝트를 import 하기 위해 생성된 프로젝트를 압축 해제하고, build.gradle을 통해 프로젝트를 open하면 빌드가 진행된다.
정상적으로 빌드가 성공되었음을 확인할 수 있다.
다음과 같이 .gradle, build.gradle, gradlew, gradlew.bat, settings.gradle이 생성된 것을 확인할 수 있다.
Gradle은 유연한 오픈 소스 빌드 자동화 도구이다.
Starting Gradle Daemon...
Gradle Daemon started in 2 s 362 ms
> Task :prepareKotlinBuildScriptModel UP-TO-DATE
BUILD SUCCESSFUL in 17s
프로젝트를 import 한 뒤 빌드를 실행했을 때 콘솔에 출력된 로그이다.
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
로 옵션을 변경할 수 있다.
태스크는 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
목록에서 확인할 수 있다.
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.properties)이 있는데, 그 이유는 프로젝트를 빌드할 때 Gradle Wrapper를 사용했기 때문이다.
Gradle Wrapper는 Gradle 빌드를 실행하는 권장 방법이며, 선언된 버전의 Gradle을 호출하여 필요하다면 Gradle의 설치 없이 프로젝트의 Gradle 작업을 수행할 수 있도록 하는 script를 의미한다.
Gradle Wrapper의 동작 과정은 위 그림과 같다.
모든 바닐라 Gradle 빌드에는 wrapper
태스크가 존재하기 때문에, 커맨드 옵션으로 Gradler Wrapper를 추가할 수 있다.
gradle wrapper
해당 명령어로 간단하게 생성할 수 있으며, 다음과 같은 옵션을 부여할 수 있다.
bin
과 all
이며, 기본값은 bin
이다.bin
은 런타임만 포함하고 샘플 코드와 설명서는 포함하지 않는 배포이다.all
은 샘플 코드와 설명서까지 포함하는 배포이다.gradle wrapper --gradle-version 7.2 --distribution-type all
위와 같은 식으로 명령어를 사용할 수 있다.
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.properties
gradlew
, gradlew.bat
빌드를 안정적이고 표준화된 실행을 보장하기 위해 항상 Wrapper로 빌드를 실행하는 것을 권장한다.
./gradlew.bat
./gradlew
위와 같이 명령어를 입력하면 프로젝트 내에 gradle-wrapper.jar
를 이용하여 빌드를 진행한다.
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
gradlew/gradlew.bat 파일을 실행하면 프로젝트 내에 설치된 해당 파일을 사용하여 gradle task를 진행한다.
그렇기 때문에 로컬 환경의 영향을 받지 않는다.
실제로는 Wrapper 버전에 맞는 구성들을 로컬 캐시에 다운로드 받는다.
다운로드 받을 때 gradle-wrapper.properties에 설정한 영향을 받는다.
그 위치는 %GRADLE_USER_HOME%/.gradle/wrapper/dists/gradle-version-bin/shasum/gradle-version/lib/plugins이다.
실제 빌드를 할 때 실행되는 빌드 스크립트이다.
내용은 다음과 같다.
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의 종류는 바이너리 플러그인
과 스크립트 플러그인
으로 나뉘며, Spring Initializr를 통해 생성되는 build.gradle
의 경우 바이너리 플러그인
을 사용한다.
바이너리 플러그인은 Plugin 인터페이스를 구현하여 프로그래밍 방식으로 작성되거나 Gradle의 DSL 언어 중 하나를 사용하여 선언적으로 작성된다.
바이너리 플러그인은 빌드 스크립트 내, 프로젝트 계층 내, 또는 플러그인 jar 외부에 있을 수 있다.
DSL은 Domain Specific Language의 약자로, 특정 도메민(산업, 분야 등 특정 영역)에 특화된 언어를 의미한다.
DSL은 특정 영역의 문제 해결에는 그 영역에 맞는 특화된 도구를 사용하자는 취지에서 등장했다.
장점으로는 반복이 제거되고 비슷한 처리 코드는 템플릿 처리가 가능하다는 점이 있다.
단점으로는 설계가 어렵고, 하위 호환성을 유지해야 한다는 점이 있다.
플러그인을 사용하기 위해서는 두 단계가 필요하다.
플러그인을 귀결(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 블록은 다음과 같다.
바이너리 플러그인의 경우 플러그인의 전역 고유 식별자 또는 이름을 의미하는 플러그인 ID를 통해 적용한다.
Core Gradle 플러그인(핵심 바이너리 플러그인)은 JavaCompile을 java와 같이 짧은 이름으로 제공한다.
즉 일부 레거시 플러그인은 짧고 정규화되지 않은 형식을 사용할 수 있다는 것이다.
다른 모든 바이너리 플러그인은 정규화된 형식의 플러그인 ID(com.github.foo.bar
)를 사용해야 한다.
플러그인은 단순히 Gradle의 플러그인 인터페이스를 구현하는 모든 클래스이다,
Gradle은 JavaPlugin 배포의 일부로 핵심 플러그인을 제공하기 때문에 귀결할 필요가 없다.
그러나 비핵심 바이너리 플러그인을 적용하려면 먼저 플러그인을 귀결해야 하며, 방법은 다음과 같다.
Spring Initializr의 경우 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 플러그인은 프로젝트에 테스트, 번들링 기능과 함께 Java 컴파일을 추가한다.
즉 현재 프로젝트에 Java 애플리케이션 빌드에 필요한 기능을 추가하는 역할을 한다고 볼 수 있다.
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
build.gradle에서의 처음 두 가지 정보는 Project에서 요구하는 Property이며, 나머지 하나는 JavaCompile에서 요구하는 Property를 의미한다.
group
version
unspecified
이다.sourceCompatibility
Gradle은 repositories를 사용하여 하나 이상의 리포지토리를 통해 종속성을 해결할 수 있다.
공개 바이너리 리포지토리인 Maven Central 및 Google Android 리포지토리를 통해 종속성을 해결할 수 있다.
repositories {
mavenCentral()
}
Spring Initializr에서는 Maven Central로 종속성을 해결하고 있음을 확인할 수 있다.
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
archives
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
이므로 테스트 컴파일, 테스트 런타임 경로에 적용된다.