카테고리 없음

[TIL] Gradle-1

bluealice 2025. 7. 13. 17:15

1. gradle 이란

gradle 이 컴파일, 라이브러리 추가, jar 파일 생성 등의 '어플리케이션 생성' 이라는 일련의 과정을 수행하도록 설정해줄 수 있다. 설정을 해준다면 Gradle 내에서 해당 스크립트를 읽고 실행해준다.

Gradle 을 사용하여 build 를 할 때는 2가지 방식이 있는데, 직접 Gradle CLI 를 입력하거나, Gradle Wrapper 를 사용하는 방식이 있다.

$gradle build -> os 에 설치된 gradle 버전 사용

$./gradlew build -> Gradle Wrapper 를 사용하여, build.gradle 에 지정된 버전을 사용, 공식문서에서 권장

 

Gradle 공식문서에서 권장하는 것은 Gradle Wrapper를 사용하는 방식이며, gradle 버전을 설정한 대로 자동으로 다운로드해주기 때문에 팀원이 로컬에 gradle 을 설치하지 않아도 된다. Gradle Wrapper 를 사용하여 자동으로 다운받고, build.gradle 을 실행하는 명령어는 gradlew 파일이며, windows 용/unix 용 2가지가 같이 있다. UNIX 계열 OS 에서는 gradlew, Windows OS 에서는 gradlew.bat 파일이 있어야 gradlew 라는 명령어를 사용해서 script를 실행할 수 있다.

보통 한 (서브)프로젝트 당 'build.gradle'(build.gradle.kts) 이라는 파일에 DSL(도메인 특화 언어)로, 어떻게 빌드 과정을 수행할 것인지 간단하게 작성만 하면 script 를 작성할 수 있다. 

공식문서에서는 Gradle 을 사용했을 때 프로젝트 구조를 다음과 같이 보여주고 있다.

project
├── gradle                          
│   ├── libs.versions.toml              
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew                         
├── gradlew.bat                         
├── settings.gradle(.kts)           
├── subproject-a
│   ├── build.gradle(.kts)              
│   └── src                         
└── subproject-b
    ├── build.gradle(.kts)              
    └── src

wrapper 파일이 있고, gradlew 가 존재한다. settings.gradle 의 경우 여러 subproject 가 있을 때는 루트 프로젝트와 서브 프로젝트의 구성을 잡기 위해 반드시 작성해야 하지만, 단일 프로젝트의 경우 쓰지 않아도 된다. 각 (서브)프로젝트 내부에는 build.gradle(.kts) 가 존재하여, 각 프로젝트를 어떻게 빌드해야 하는지에 대해 dsl 로 작성되어 있다.

 

2. build.gradle 과 Task

 

1) Task 작성 방식 : gradle 이 어떤 과정과 순서로 컴파일과 패키징을 수행하며 세부적인 설정은 어떻게 할 것인지 정할 수 있도록 build.gradle 에서 작성한다. 각각의 과정들을 task 라고 부르는데, 해당 task 를 작성하는 방법은 크게 class 파일로 만들어서 컴파일해서 binary 로 생성하거나, dsl 로 script 를 작성해서 사용하는 방식이 있다. (출처: Gradle 공식문서)

- class 파일로 작성

// plugin/src/main/kotlin/plugin/MyPlugin.kt
class MyPlugin : Plugin<Project> { // MyPlugin 생성
    override fun apply(project: Project) {
        project.run {
            tasks {
                register("myCopyTask", Copy::class) { // gradle task 등록
                    group = "sample" 
                    from("build.gradle.kts") //이 파일을
                    into("build/copy") // 여기로 복사
                }
            }
        }
    }
}

// build.gradle.kts
plugins {
    id("com.example.myplugin") // id 내 파일 경로를 문자열로 작성하여 실행
}

 

- Script 파일로 작성

// build.gradle
class HelloWorldPlugin : Plugin<Project> { // 플러그인 클래스 상속
    override fun apply(project: Project) { // apply 함수 오버라이딩
        // Gradle Task 등록
        project.tasks.register("helloWorld") {
            group = "Example"
            description = "Prints 'Hello, World!' to the console" // task 에 대한 간단한 설명
            doLast {
		// hello world 를 출력하는 것.
                println("Hello, World!") // "Hello, World!" 를 출력하기
            }
        }
    }
}

// Apply the plugin
apply<HelloWorldPlugin>() //바로 적용

 

그러나 대부분의 경우 그렇게 직접 작성하지 않아도, Gradle 이 제공하거나 제 3자가 올린 안정적인 plugin 을 사용하면 된다. 이것은 Gradle Plugin Portal 에 저장되어 있으며(Docker hub 같은 것과 같다) gradle 이 처음 시작할 때 다운받아 cache 에 저장하여 이후부터는 cache 에서 꺼내 사용한다.

plugins {
	id 'java' // java 소스 컴파일, jar 패키징 등
	id 'org.springframework.boot' version '3.3.4'등 // SpringBoot 실행 파일 fat jar (bootJar) 생성 등
	id 'io.spring.dependency-management' version '1.1.6' // dependency 에서 Spring Boot 가 정의한 대로 의존성 버전을 관리해줌
}

 

프로젝트에서는 이러한 플러그인을 실제로 사용했다. java 를 컴파일하고, SpringBoot 전용 패키징, 버전 설정 자동화 등의 task 를 수행하는 plugin 들을 사용하여, 따로 task 를 작성해주지 않아도, id 를 통해 plugin을 지정하면 해당 task 가 생성 및 dependsOn(순서 결정) 순서까지 실행할 수 있었다. 

 

2) 'java' plugin

 

그중에서 Gradle 공식문서에 작성된 'java' 플러그인에 있는 task 들은 다음과 같은 게 있는데,

1) compileJava : JDK 를 사용하여 소스코드를 바이트코드로 컴파일

2) processResources : resource 에 있는 파일들을(ex. application.yml 등) 복사해서 출력 디렉토리로 옮김

3) classes : compileJava + processResources 

4) test 에 대해 위 1~3 의 task 가 따로 마련되어있음(compileTestJava...)

5) jar : jar 파일 생성

6) javadoc : javadoc으로 소스코드에 대한 API DOC 생성

7) clean : 빌드 디렉토리 지움

 

특히 sourceSet, 즉 파일 단위로 1~3 task 가 자동으로 생성된다.

sourceSet은 보통  src/main/java, src/main/resources 아래 파일들을 관리하는 main,  src/test/java, src/test/resources 아래 파일들을 관리하는 test 가 있는데, 

sourceSets { // 'integrationTest' 라는 sourceSet 생성
	integrationTest{
		java.srcDir("src/integrationTest/java")
		resources.srcDir("src/integrationTest/resources")
	}
}

sourceSets { // main sourceSet 내 java 소스코드 디렉토리에 querydslDir 을 추가한다.
	main.java.srcDirs += [ querydslDir ]
}

 

이런 식으로 sourceSet 을 새로 만들어 따로 task 를 생성 및 관리할 수도 있고, 기존 sourceSet 에 새로운 디렉토리를 포함시키는 것도 가능하다.

 

3) SpringBoot plugin

그러나 SpringBoot를 사용하는 입장에서 큰 의미는 없었는데, 왜냐하면

plugins {
	...
	id 'org.springframework.boot' version '3.3.4'
	...
}

 

이러한 plugin 을 설치하여 일반 jar 파일이 아닌 SpringBoot 프로젝트를 생성할 수 있게 bootJar 를 만들어줘야 했기 때문이다.

jar {
	enabled = false
}

그래서 다음과 같이 java plugin 의 jar 이란 task 를 'enabled=false' 로 지정하면서, 일반 java jar 파일을 생상하지 않고, SpringBoot plugin 에서만 jar 파일을 생성하도록 설정했다.

 

 

* jar 파일

jar 파일은 java archive 의 약자로, zip 파일 같은 것이라고 쉽게 생각할 수 있다. 그러나 zip 파일과는 다르게, JVM 에서 실행할 수 있고, class 파일/라이브러리/MANIFEST.MF 등 Java Application 용 압축 파일이다. MANIFEST.MF 는 JAR 파일에 대한 메타데이터를 저장한 파일인데, 진입 main class 지정, class path 지정, MANIFEST 버전, 작성자 등을 지정할 수 있다. 다음과 같이 jar task 에서 manifest 관련 설정을 직접 지정할 수 있다. 그러나 SpringBoot 를 사용한다면 자동으로 지정해주기 때문에 작성하지 않아도 된다.

// 출처 : https://codingdreamtree.tistory.com/108
jar {
    manifest {
        attributes(
                'Manifest-Version': '1.0', // 버전 지정
                'Main-Class': 'org.example.Main', // main class 경로 지정
                'Create-By': '17.0.2 (OpenJdk)' // 생성자
        )
    }
}