Spring 프로젝트를 생성해주는 사이트 또는 IDE를 이용하여 프로젝트를 생성하려고하면 Maven과 Gradle을 선택해야하는 순간이 온다
프로젝트를 진행하면서 Maven 또는 Gradle을 통하여 주로 의존성관리를 하게된다
이번에는 좀 더 자세하게 이것들을 살펴보려고 한다
Maven과 Gradle은 모두 빌드도구다
Apache Maven is a software project management and comprehension tool.
Gradle Build Tool is a fast, dependable, and adaptable open-source build automation tool with an elegant and extensible declarative build language.
여기서 빌드란 작성한 소스코드를 실행할 수 있는 상태로 만드는 작업을 의미한다
즉, Java의 경우 jar파일을 생성하는 작업이라고 볼 수 있다
이렇게 생성된 빌드파일은 서버에서 동작시는 과정 ( 배포 ) 에서 사용된다
Maven은 Apache 재단에서 만든 빌드도구로 프로젝트 객체 모델 ( POM )을 기반으로 한다
지식의 축적자라는 의미를 가지고있으며, 자카르타 프로젝트의 빌드 프로세스를 단순화하기 위해 만들어졌다
자카르타 프로젝트(Jakarta Project)는 자바 플랫폼을 위한 오픈 소스 소프트웨어를 만들고 정비하는 프로젝트이다, 위키백과
간단하게 말하자면 자바 기반 프로젝트를 쉽게 빌드하고 관리하기 위한 프로젝트다
프로젝트 실행 이전에 테스트 코드를 미리 실행시켜주거나
프로젝트에 필요한 의존성을 관리해주거나
로그를 출력해주거나
하는 등의 작업을 해준다
주요 특징으로는 아래의 것들이 있다
- 손쉬운 프로젝트 환경구성 ( 자주 사용되는 틀을 제공해준다 )
- 일관된 사용방법
- 자동업데이트와 같은 종속성 ( 의존성 ) 관리
- 여러 프로젝트를 동시에 쉽게 작업
- Ant task를 통한 의존성 관리나 배포
- 자바나 스크립트 언어를 통한 플러그인 작성
앞서 Maven은 POM을 기반으로 한다고 설명했다
실제로 Maven과 관련하여 설정을 할때 pom.xml
이라는 파일을 이용한다
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- 기본적인 내용 -->
<groupId>...</groupId>
<artifactId>...</artifactId>
<version>...</version>
<packaging>...</packaging>
<dependencies>...</dependencies>
<parent>...</parent>
<dependencyManagement>...</dependencyManagement>
<modules>...</modules>
<properties>...</properties>
<!-- 빌드 세팅 -->
<build>...</build>
<reporting>...</reporting>
<!-- 추가적인 프로젝트 정보 -->
<name>...</name>
<description>...</description>
<url>...</url>
<inceptionYear>...</inceptionYear>
<licenses>...</licenses>
<organization>...</organization>
<developers>...</developers>
<contributors>...</contributors>
<!-- 환경 설정 -->
<issueManagement>...</issueManagement>
<ciManagement>...</ciManagement>
<mailingLists>...</mailingLists>
<scm>...</scm>
<prerequisites>...</prerequisites>
<repositories>...</repositories>
<pluginRepositories>...</pluginRepositories>
<distributionManagement>...</distributionManagement>
<profiles>...</profiles>
</project>
pom.xml
의 전체적인 구성은 위와 같다
프로젝트의 필수적인 내용 ( 명칭 버전, 의존성 등 ) 부터 빌드와 관련된 세팅이나 추가적인 정보들이 모두 기입된다
내용이 많으므로 간략하게 기본적인 내용과 빌드 설정부분만 설명할 예정이다
자세한 내용은 공식문서를 확인하자
<groupId>...</groupId>
<artifactId>...</artifactId>
<version>...</version>
groupId:artifactId:version
의 꼴로 프로젝트 진행자, 프로젝트 명칭, 버전을 표기하는데 사용된다
이는 IntellJ 등을 통해 프로젝트를 생성할 때 입력할 수 있다
이렇게 생성된 프로젝트는 빌드 시 프로젝트 명칭과 버전이 표기된 jar파일을 생성한다
<packaging>...</packaging>
간단하게 jar
나 war
중 어떤 방식으로 패키징할 것인지를 선택하는 옵션이다
Jar => 자바 애플리케이션이 동작할 수 있도록 압축한 파일
War => Servlet/JSP와 같은 웹자원을 포함하여 압축한 파일
참고
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<type>jar</type>
<scope>test</scope>
<optional>true</optional>
</dependency>
...
</dependencies>
외부 의존성 목록을 작성하는 영역이다
주로 mvn repository 를 이용해서 의존성을 얻어온다
이곳에 작성한 의존성은 Maven이 다운로드해서 관리한다 ( users/{}/.m2/repository )
scope
는 해당 의존성의 동작범위를 지정하는데 사용된다
compile
,provided
,runtime
,test
,system
이 존재한다
compile
=> 기본 설정, 모든 클래스에서 사용가능
provided
=> 패키징시 제외
runtime
=> 컴파일시에는 불필요
test
=> 테스트 과정에서만 필요
system
=> 명시적으로 jar파일을 포함해야한다는 것을 의미, ex: 직접만든 의존성파일
optional
은 다른 의존성에서 이미 해당 의존성을 사용중이기 때문에 설치할 필요가 없음을 표기하는데 사용된다
<parent>...</parent>
<dependencyManagement>...</dependencyManagement>
parent
와 dependencyManagement
는 일종의 상속관계( 부모-자식 )에서 사용되는 옵션이다
두 프로젝트 관계를 이어줄 때 사용할 수 있으며 자식 프로젝트의 pom.xml
에 부모 프로젝트의 정보를 작성해야한다
부모-자식 관계에서는 의존성이 전이되는데 이때 dependencies
와 dependencyManagement
의 동작이 다르다
dependencies
의 경우 자식 프로젝트가 항상 해당 의존성을 가지게 된다
반면 dependencyManagement
의 경우 자식 프로젝트에서 dependencies
에 동일한 의존성을 작성한 경우에만 의존성을 가진다
또한 dependencyManagement
에 의존성과 관련된 정보( 버전 )을 명시함으로써 해당 의존성을 필요로하는 자신 또는 하위 프로젝트들이 dependencies
를 작성할때 버전정보를 작성하지 않아도 된다. 즉, 버전관리를 한곳에서 처리할 수 있다
<modules>
<module>my-project</module>
<module>another-project</module>
<module>third-project/pom-example.xml</module>
</modules>
modules
는 일종의 프로젝트 집합을 만드는 용도로 사용되며, 이 경우 의존성은 현재 pom.xml
을 기준으로 먼저 적용된다
모듈또한 상속과 유사하게 현재 프로젝트에서 관리할 수 있는 하위모듈을 지정하는 것을 의미한다
이를 통하여 하나의 프로젝트에서 여러개의 모듈을 관리하는 것이 가능하다. 즉, 하나의 pom.xml
을 이용하여 하위 프로젝트 또는 모듈의 의존성을 관리하는 것 또한 가능하다
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
마지막으로 properties
는 환경변수를 지정하는데 사용된다
${ name }
의 꼴로 사용이 가능하며, pom.xml
에서 사용할 전역변수를 설정한다고 보면된다
<build>
<defaultGoal>install</defaultGoal>
<directory>/home/jenkins/82467a7c/workspace/aven_maven-box_maven-site_master/target</directory>
<finalName>${artifactId}-${version}</finalName>
<filters>
<filter>filters/filter1.properties</filter>
</filters>
...
</build>
빌드과정에 필요한 정보들을 설정하는 영역이다
기본적인 구성은 위에 작성한 것과 동일하다
defaultGoal : 아무것도 주어지지 않았을때 실행될 작업을 설정한다 ( install, clean, build 등 )
directory : 빌드시 생성되는 파일의 위치를 의미한다
finalName : 빌드 시 생성되는 프로젝트 이름이다
filters :*.properties
로 정의된 파일에 설정된 자원들을 허락하는 용도로 사용된다
설정된 파일에 만약name=test
라고 작성되어 있다면,application.properties
에서${name}
을 이용하여 사용할 수 있다
<build>
...
<resources>
<resource>
<targetPath>META-INF/plexus</targetPath>
<filtering>false</filtering>
<directory>/home/jenkins/82467a7c/workspace/aven_maven-box_maven-site_master/src/main/plexus</directory>
<includes>
<include>configuration.xml</include>
</includes>
<excludes>
<exclude>**/*.properties</exclude>
</excludes>
</resource>
</resources>
<testResources>
...
</testResources>
...
</build>
앞서 소개한 것들에 이어서 resources
( 주로 *.propertis
또는 *.yml
) 관련 설정을 할 수 있다
resources: 프로젝트와 관련된
resource
들을 표시하는데 사용
targetPath: 빌드에서 생성될resource
파일들의 경로를 작성
filtering:filter
의 사용유무를 작성
directory:resource
를 찾는 위치를 작성
includes:resource
로 포함할 패턴
excludes:resource
로 제외할 패턴
testResources: 테스트시 사용될resource
설정
<build>
...
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.6</version>
<extensions>false</extensions>
<inherited>true</inherited>
<configuration>
<classifier>test</classifier>
</configuration>
<dependencies>...</dependencies>
<executions>...</executions>
</plugin>
</plugins>
</build>
마지막으로 plugin
은 maven 동작의 핵심이다
모든 작업은 plugin
을 통해 이루어지며 관련 정보는 여기를 확인해보자
extensions : 해당 플러그인의 extension의 사용유무
inherited : 해당 구성을 상속하는pom.xml
에서의 적용유무를 설정
configuration : 개인 플러그인과 관련된 설정,
dependencies : 플러그인과 관련된 의존성 설정
executions : 플러그인이 목표를 특정단계에 바인딩하는 것과 관련된 설정
<executions> <execution> <id>echodir</id> <goals> <goal>run</goal> </goals> <phase>verify</phase> <inherited>false</inherited> <configuration> <tasks> <echo>Build Dir: /home/jenkins/82467a7c/workspace/aven_maven-box_maven-site_master/target</echo> </tasks> </configuration> </execution> </executions>
verify
단계에run
이라는 목표를 바인딩하는 설정
<build>
<sourceDirectory>/home/jenkins/82467a7c/workspace/aven_maven-box_maven-site_master/src/main/java</sourceDirectory>
<scriptSourceDirectory>/home/jenkins/82467a7c/workspace/aven_maven-box_maven-site_master/src/main/scripts</scriptSourceDirectory>
<testSourceDirectory>/home/jenkins/82467a7c/workspace/aven_maven-box_maven-site_master/src/test/java</testSourceDirectory>
<outputDirectory>/home/jenkins/82467a7c/workspace/aven_maven-box_maven-site_master/target/classes</outputDirectory>
<testOutputDirectory>/home/jenkins/82467a7c/workspace/aven_maven-box_maven-site_master/target/test-classes</testOutputDirectory>
...
</build>
자바 소스파일의 위치나 컴파일시 생성될 파일의 위치를 표시하는데 사용되는 옵션이다
Gradle은 빌드 스크립트를 이용해서 빌드, 테스트, 배포등을 자동화한다
Gradle의 build.gradle
는 Maven의 pom.xml
과 유사한 동작을 한다
settings.gradle
과 build.gradle
을 이용하여 프로젝트와 관련된 의존성이나 Task를 설정하고 이를 통하여 손쉽게 실행할 수 있다
Gradle은 크게 User Home Directory와 Project Root Directory로 나뉜다
User Home Directory에서는 전역적인 propertie 설정이나 초기화 스크립트, 캐시나 로그파일 등이 저장된다
일반적으로 C:\Users\<USERNAME>\.gradle
에 위치하고 있으며 GRADLE_USER_HOME
환경변수로 저장해서 이용할 수도 있다
init.d
하위에 초기화 스크립트가 존재한다
Project Root Directory는 흔히 만드는 프로젝트 폴더를 의미한다
이곳에는 .gradle
과 build
폴더가 포함된다
.gradle
의 경우 버전관리와 관련된 정보
build
에는 주로 빌드 결과물이 저장된다
또한 Gradle Wrapper나 settings.gradle
, build.gradle
파일이 존재한다
settings.gradle
의 경우 루트프로젝트에 위치하며 build.gradle
은 서브프로젝트마다 존재한다
Gradle의 빌드는 크게 초기화 - 설정 - 실행
의 단계를 거친다
초기화에서는 settings.gradle
을 인식하고, Settings 인스턴스를 생성하며, 빌드를 구성할 프로젝트를 결정한다
또한 프로젝트마다 Project 인스턴스를 생성한다
설정에서는 초기화 단계에서 찾은 빌드에 참여하는 프로젝트들의 build.gradle
에 있는 빌드 스크립트들을 확인한다
실행에서는 선택된 task들의 실행 순서를 설정하고, 작업간의 의존성에 따라 실행순서를 결정짓는다
또한 병렬적으로 task들을 실행시킨다
settings.gradle
초기화단계에서 settings.gradle
를 찾게되면 빌드에 포함될 프로젝트를 설정하기 위하여 Settings
객체를 생성한다
이때 생성되는 객체는 해당 파일을 가지고 만들어진다
해당 객체에는 빌드 캐시설정, 관련 플러그인, 루트 디렉토리, 루트 프로젝트와 같은 기본적인 항목들이 존재하며, 하위 프로젝트에 대한 정보도 포함된다
이러한 설정들은 closure ( Groovy ) 또는 lambda ( Kotlin )라고 불리는 {}
블록을 통하여 작성한다
작성된 코드들은 top-bottom 의 순서대로 읽히여 실행된다
pluginManagement {
repositories {
gradlePluginPortal()
google()
}
}
플러그인들의 위치를 설정하는 코드를 작성하는 코드이다
이곳에는 Gradle Plugin Portal과 같은 바이너리 저장소가 포함된다
또한 플러그인이나 플러그인 의존성 전략등을 포함시킬 수 있다
plugins {
id("org.gradle.toolchains.fake") version "0.6.0"
}
또한 사용될 수도 있는 플러그인목록을 선언하는게 가능하다
이는 빌드에 포함되는 서브프로젝트에게도 공유된다
이를 통하여 하위프로젝트에서 동일한 버전의 플러그인을 사용하게 할 수 있다
dependencyResolutionManagement {
repositories {
mavenCentral()
}
}
Maven Central을 포함하여, 프로젝트에 사용되는 의존성들의 위치를 설정하는 것도 가능하다
include("app")
include("business-logic")
include("data-model")
마지막으로 빌드에 포함될 하위 프로젝트들을 설정할 수 있다
build.gradle
설정단계에서 해당 파일을 읽고 Project 객체를 생성한다
해당 위치에도 마찬가지로 Groovy 또는 Kotlin 문법을 이용한 코드작성이 가능하다
기본적으로 프로젝트 폴더의 이름, 경로, 의존성 핸들러 들을 설정하는게 가능하다
추가적으로 task의 생성도 가능하다
작성되는 양식은 앞서 설명한 settings.gradle
과 동일하게 {}
를 이용한다
plugins {
id("org.jetbrains.kotlin.jvm") version "1.9.0"
id("application")
}
먼저 해당 프로젝트 빌드 시 적용될 플러그인 목록을 설정할 수 있다
이러한 플러그인들은 프로젝트 설정을 모듈화하고 재사용하는데 사용된다
application {
mainClass = 'com.example.Main'
}
예를들어, applicaiton
플러그인을 통하여 프로젝트의 진입지점을 설정할 수 있다
repositories {
mavenCentral()
google()
}
settings.gradle
과 유사하게 의존성을 가져오기위한 저장소의 위치를 설정하는 예시다
tasks.register("zip-reports", Zip) {
from 'Reports/'
include '*'
archiveName 'Reports.zip'
destinationDir(file('/dir'))
}
또한 task를 등록하거나 설정하는게 가능하다
위 예시에서는 register를 이용하여 zip-reports
라는 task를 등록하고있다
tasks.named('test') {
useJUnitPlatform()
}
Spring Boot에서는 기본적으로 테스트를 위한 Task 설정을 제공한다
Version Catalog를 작성해서 하위 프로젝트간 의존성이나 버전구성을 공유할 수 있다
보통 libs.versions.toml
라는 명칭으로 작성한다
[versions]
androidGradlePlugin = "7.4.1"
mockito = "2.16.0"
[libraries]
google-material = { g**텍스트**roup = "com.google.android.material", name = "material", version = "1.1.0-alpha05" }
mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockito" }
[plugins]
android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }
4가지의 섹션 ( version
, libraries
, bundles
, plugins
)이 존재하며, 관련코드를 작성했다면 자동으로 IDE가 읽어서 설정해준다
dependencies {
// Dependency on a remote binary to compile and run the code
implementation(libs.google.material)
// Dependency on a remote binary to compile and run the test code
testImplementation(libs.mockito.core)
}
주로 사용하는 의존성의 관리는 dependencies
섹션에 작성할 수 있다
이곳에는 implementation
, compileOnly
, annotationProcessor
, testImplementation
등을 설정할 수 있다
implementation
의 경우, 컴파일 및 실행시에 필요한 의존성
compileOnly
의 경우, 컴파일 시에만 필요한 의존성 ( ex: lombok )
annotationProcessor
은 애노테이션 프로세서 설정
testImplementation
는 테스트 코드의 컴파일과 실행에 필요한 의존성
Task는 빌드시 수행될 작업단위를 의미한다
./gradlew {task 이름}
의 명령을 통해서 동작시키거나 IDE를 사용하여 동작시킬 수 있다
이러한 Task 간에는 의존성이 존재하기에 하나의 task를 실행시켰을때 다른 task들이 실행되게 할 수 있다
$ ./gradlew build
> Task :app:compileJava
> Task :app:processResources NO-SOURCE
> Task :app:classes
> Task :app:jar
> Task :app:startScripts
> Task :app:distTar
> Task :app:distZip
> Task :app:assemble
> Task :app:compileTestJava
> Task :app:processTestResources NO-SOURCE
> Task :app:testClasses
> Task :app:test
> Task :app:check
> Task :app:build
BUILD SUCCESSFUL in 764ms
7 actionable tasks: 7 executed
공식문서의 예시를 보면 이를 눈으로 확인할 수 있다
플러그인은 빌드 기능을 확장하는데 주로 사용된다
예를들어 Spring Boot를 지원하기 위한 org.springframework.boot
플러그인이나 Java와 관련한 java
플러그인이 있다
이러한 플러그인은 크게 3가지로 Core, Commnunity, Local로 나뉜다
Core은 Gradle에서 제공하며 짧고 유일한 명칭을 가지고,
Commnunity는 플러그인 개발자들이 만들어 놓은 플러그인,
Local은 개인적으로 만든 플러그인을 의미한다