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.jargradle-wrapper.propertiesgradlew, 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를 의미한다.
groupversionunspecified 이다.sourceCompatibilityGradle은 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이므로 테스트 컴파일, 테스트 런타임 경로에 적용된다.