RestDocs
설정을 하면서 Gradle
설정을 추가하거나 수정하는 일이 많이 생겼습니다.
Gradle
에 대한 이해가 부족하니, 어떤 동작을 하는 코드인지 알기가 어려웠습니다.
이 글을 통해 gradle
의 동작 방식과 구조에 대해 이해를 높이고자 합니다.
Gradle
은 빌드 자동화 툴입니다. 프로젝트를 배포 가능한 형태로 만들어 줍니다.
Groovy
와 Kotlin
을 지원하여 Java
를 사용하는 개발자들이 빠르게 적응하고 사용할 수 있습니다.
Gradle
은 간편한 설정, Groovy
와 Kotlin
을 사용한 스크립트 작성, 멀티 프로젝트 기능을 지원하여 소규모 프로젝트부터 대규모 프로젝트까지 사용할 수 있습니다.
실습하며 이해하기 전에 gradle
의 간단한 구조에 대해 이해하고 넘어가겠습니다.
세부적인 동작 방식은 알기 어렵지만, 대략적인 구조를 알고 실습을 진행한다면 실습을 통한 이해가 잘 될 것입니다.
plugins { // 추가적인 빌드 기능을 가져옵니다.
java
}
// build metadata
group = "org.example"
version = "1.0-SHANPSHOT"
repositories { // 의존성을 가지고 올 저장소
mavenCentral()
}
dependencies { // 의존성, 코드를 빌드할 때 필요한 것들
testImplementation("junit:junit:4.13.2")
}
build.gradle
은 Gradle
설정을 담당하고 있습니다.
4: 프로젝트에 특정 버전 Gradle
을 포함시켜, 개발자가 별도의 Gradle
설치 없이도 일관된 빌드 환경을 보장해줍니다. (Gradle Wrapper
라고 부릅니다)
5: build.gradle
에서 사용하는 속성들을 담고 있습니다.
6: Gradle Wrapper
를 사용하여 빌드 할 수 있는 스크립트가 작성되어 있습니다.(맥, 리눅스: gradlew
, 윈도우: gradlew.bat
)
실습을 진행하기 위해 Java 17버전
과 Gradle
이 설치되어 있어야 합니다. (Gradle
은 최소 Java 8버전
이상에서 동작합니다)
실습 운영체제는 Mac
환경이고, gradle 언어는 Kotlin
을 사용합니다.
mkdir get-going-with-gradle
cd get-going-with-gradle
gradle init
Gradle
프로젝트를 생성합니다.
ls -al
위와 같은 파일과 디렉토리들이 생성된 것을 볼 수 있습니다.
여기서 .gitignore
를 먼저 보게 되면
cat .gitignore
자동적으로 .gradle
과 build
디렉토리를 git
에서 무시하도록 설정하고 있습니다.
.gradle
: gradle
빌드할 때 생성되는 캐시 및 임시파일들을 저장하고 있습니다.
빌드 성능을 최적화 시키는 데에는 도움이 되지만, 프로젝트의 소스 코드나 설정과는 직접적인 관련이 없기 때문에 무시하는 것이 좋습니다.
build
: 컴파일된 자바 클래스, jar
파일, 생성된 문서, 테스트 보고서 등 빌드 결과물을 저장하고 있습니다. 빌드 프로세스의 최종 산출물로 소스 코드나 설정과 직접적인 관련이 없기 때문에 무시하는 것이 좋습니다.
./gradlew tasks
현재 gradle
이 사용할 수 있는 태스크들을 알 수 있습니다.
초기 상태에서는 사용할 수 있는 태스크들이 몇 개 없습니다.
gradle
을 만들었으니, 이 위에 자바 프로젝트를 만들어 실행해보겠습니다.
자바 프로젝트를 실행하기 위해서는 일반적으로 다음과 같은 요구사항이 필요합니다.
.java
파일을 .class
파일로 컴파일 해야 합니다..class
들과 리소스 파일들을 .jar
파일로 압축해야 합니다..jar
파일에 같이 패키징 되어선 안됩니다.Apache Commons
, Spring Boot
등)리소스 파일과 소스 파일이 존재하는 프로젝트를 만들고 gradle
로 실행 과정을 확인해보겠습니다.
프로젝트는 "en"을 입력할 경우 "Hello!", "es"를 입력할 경우 "Holla!" 를 출력하는 간단한 프로젝트입니다.
// src/main/java/org/example/languageapp/SayHello.java
package org.example.languageapp;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
public class SayHello {
public static void main(String[] args) throws IOException {
String language = args[0];
InputStream resourceStream = SayHello.class.getClassLoader().getResourceAsStream(language + ".txt");
assert resourceStream != null;
BufferedReader bufferedInputStream = new BufferedReader(
new InputStreamReader(resourceStream, StandardCharsets.UTF_8));
System.out.println(bufferedInputStream.readLine());
}
}
// src/main/resources/en.txt
Hello!
// src/main/resources/es.txt
Holla!
주석 경로에 .java
, txt
파일을 만듭니다.
// build.gradle.kts
plugins {
java
}
Gradle
에 자바 플러그인을 추가하고 ./gradlew tasks
로 수행할 수 있는 태스크들을 봅니다.
자바의 여러 태스크들이 추가된 것을 볼 수 있습니다.
추가된 자바 플러그인의 기능을 이용해 자바 프로젝트를 빌드해야 합니다.
하지만 태스크에는 compileJava
와 processResources
가 존재하지 않습니다.
실제 태스크 명령어로 보여지는 것들은 여러 개가 뭉쳐있는 높은 레벨에 태스크들만 표시 되어 있습니다.
./gradlew tasks --all
낮은 레벨까지의 모든 태스크들을 보고 싶다면 --all
옵션 붙이면 됩니다.
compileJava
와 processResources
를 순차적으로 명령하고 build
디렉토리에 어떤 변화가 생기는지 확인해보겠습니다.
./gradlew compileJava
./gradlew processResources
compileJava
시 .class
파일이 만들어지고,
processResources
시 리소스들이 빌드 디렉토리로 올라간 것을 볼 수 있습니다.
이제 jar
명령어로 패키징하여 실행해보겠습니다.
./gradlew jar
libs
디렉토리가 생기고 안에 .jar
이 생성되었습니다.
만들어진 .jar
파일을 실행해보겠습니다.
java -jar build/libs/get-going-with-gradle.jar en
"기본 Manifest 속성이 없다" 라는 것은 jar
파일에 실행 가능한 메인 클래스에 대한 정보가 포함되지 않았다는 것입니다.
// build.gradle.kts
tasks.named<Jar>("jar") {
manifest {
attributes["Main-Class"] = "org.example.languageapp.SayHello"
}
}
위와 같이 메인 클래스를 지정한 뒤
./gradlew jar
java -jar build/libs/get-going-with-gradle.jar en
다시 명령을 수행하게 되면 애플리케이션이 정상 동작하는 것을 볼 수 있습니다.
이제 test
를 통해 제대로 된 값들이 넘어가는지 확인해보겠습니다.
// src/test/java/org/example/languageapp/SayHelloTest.java
package com.org.example.languageapp;
import java.io.IOException;
import org.example.languageapp.SayHello;
public class SayHelloTest {
@Test
public void testSayHello() throws IOException {
SayHello.main(new String[]{"en"});
}
}
테스트 코드를 작성하고 테스트를 돌려보면 에러가 발생합니다.
./gradlew test
테스트 라이브러리 의존성을 추가하지 않고 수행해서 발생한 문제입니다.
테스트 라이브러리를 추가한 뒤 다시 수행해보겠습니다.
// build.gradle.kts
repositories {
mavenCentral() // 의존성을 가져올 저장소를 지정합니다.
}
dependencies {
// junit5를 테스트 코드 컴파일과 실행에 필요한 의존성으로 지정합니다.
testImplementation("org.junit.jupiter:junit-jupiter:5.8.1")
}
의존성을 추가하여 테스트 태스크도 정상 수행되었지만 정작 테스트 보고서에는 0개의 테스트가 수행되었습니다.
Gradle
에서 JUnit5
를 수행하기 위해서는 추가적인 설정이 필요합니다.(useJUnitPlatform()
설정을 사용하여 Junit5
테스트 엔진을 사용합니다)
다시 추가 설정을 하고 테스트를 진행하여 보고서를 확인해보겠습니다.
// build.gradle.kts
tasks.named<Test>("test") {
useJUnitPlatform()
}
이제 정상적으로 테스트를 실행합니다.
실제 태스크를 사용할 때는
build
를 수행하게 되면 지금까지 사용했던 모든 태스크들을 한번에 사용합니다.