지난 주에 토스 페이먼츠 사전과제를 진행하며 처음으로 Kotlin dsl로 구성된 프로젝트를 보게 되었다. 이 때 나는 당황하지 않고 IntelliJ의 Reload All Gradle Projects를 눌렀지만 특정 build지점에서 3분이상씩 멈춰있어 시작부터 혼란스러웠다. 결국 그 환경에서는 30~40분을 보내며 해결해보려고 했지만, 그 때 시점의 내가 아는 수준의 지식으로는 어떻게 해결해야 할지 몰라 내가 익숙하게 빌드환경을 만들어왔던 start.spring.io 사이트를 이용해서 문제풀이를 급하게 시작했다.
이 때 느꼈던 것은
"아.. 내가 빌드 툴 조차도 어떻게 동작하는지 모르고 그냥 start.spring.io
이 사이트를 맨날 아무생각없이 이용했어서 결국 이런 상황이 나왔구나"
라는 생각을 하게 되었다.
따라서, 이미 망쳐버린 사전과제는 돌이킬 수 없지만 내가 부족했던 부분에 대해서 자세하게 공부하고자 Gradle의 공식문서를 읽어가며 학습하게 되었다.
https://docs.gradle.org/current/userguide/userguide.html
위 공식문서의 Learning The Basics와 Beginner Tutorial까지 학습하고 정리한 내용입니다. 마지막에는 학습한 내용을 바탕으로 사전과제에서 왜 그런 상황이 나왔는지 이유를 파악해보려고 합니다.
1. Gradle 프로젝트 구조

Gradle 에서 Project는 빌드할 수 있는 소프트웨어 단위를 의미한다. (ex. 애플리케이션, 라이브러리 등)
각 프로젝트마다 build.gradle 또는 build.gradle.kts와 같은 빌드 스크립트를 작성해서 Gradle이 어떤 작업을 해야 하는지 지시할 수 있다.
빌드 스크립트에 작성하는 요소로는 Dependencies, Tasks, Plugins이 있으며 공식 문서의 실습을 따라해보며 해당 내용들을 학습해보려고 한다.
2. Gradle 설치 및 Gradle Wrapper
먼저 Gradle을 설치해서 Gradle 프로젝트를 생성한 뒤 이후 부터는 Gradle Wrapper를 이용하여 gradle 프로젝트를 관리한다.
먼저 Gradle을 다운로드 하고 윈도우 기준으로 시스템 환경변수를 설정한다.


이후 저는 kotlin dsl로 gradle 디렉토리를 생성했다. (사전과제 원인분석을 진행해보기 위해)
gradle init --type java-application --dsl kotlin
├── gradle
├── libs.versions.toml
│ └── wrapper/
├── gradlew
├── gradlew.bat
├── settings.gradle.kts
└── app
├── build.gradle.kts
└── src
├── main
│ └── java
│ └── demo
│ └── App.java
└── test
└── java
└── demo
└── AppTest.java
이후 Gradle Wrapper를 사용해서 Gradle 없이도 프로젝트 빌드가 가능하다. Gradle Wrapper는 특정 Gradle 버전을 자동 다운로드하고 실행하게 해주는 스크립트로 github에 gradle/ 디렉토리를 넣어 팀 간 gradle 버전 일관성을 보장할 수 있다.
./gradlew 혹은 gradlew.bat을 사용해서 build하는 것이 바로 Gradle Wrapper를 사용해서 진행하는 방식이다.
따라서 앞으로 진행할 실습에서는 gradlew가 아니라 ./gradlew 혹은 gradlew.bat을 사용해 진행하려고 한다.
3. Settings 파일 및 Build 파일
settings.gradle (또는 settings.gradle.kts) 파일은 모든 Gradle 프로젝트의 진입점(entry point) 이다.
plugins {
// Apply the foojay-resolver plugin to allow automatic download of JDKs
id("org.gradle.toolchains.foojay-resolver-convention") version "0.10.0"
}
rootProject.name = "practice-gradle" //루트 프로젝트 이름 정의
include("app") //서브프로젝트 추가
다음과 같이 작성하며 Settings 파일은 빌드 스크립트(build.gradle)보다 먼저 실행된다. 그렇기 때문에 Settings 파일은 빌드 전체에 영향을 미치는 설정(예: 플러그인 관리, 버전 카탈로그 설정 등)을 하는 데 적합하다.
Gradle 빌드는 반드시 최소 하나 이상의 빌드 스크립트(build.gradle 또는 build.gradle.kts) 를 포함한다. 빌드 스크립트는 프로젝트의 빌드 설정, 태스크, 플러그인을 정의하는 파일이다.
plugins { //플러그인
// Apply the application plugin to add support for building a CLI application in Java.
application
id("com.diffplug.spotless") version "7.0.3"
}
repositories {
// Use Maven Central for resolving dependencies.
mavenCentral()
}
spotless{ //관습속성
java {
googleJavaFormat("1.20.0").aosp()
target("src/**/*.java")
}
}
dependencies { //의존성 추가
// Use JUnit Jupiter for testing.
testImplementation(libs.junit.jupiter)
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
// This dependency is used by the application.
implementation(libs.guava)
}
// Apply a specific Java toolchain to ease working on different environments.
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
application { //관습 속성
// Define the main class for the application.
mainClass = "org.example.App"
}
tasks.named<Test>("test") {
// Use JUnit Platform for unit tests.
useJUnitPlatform()
}
플러그인 추가
- 플러그인은 Gradle의 기능을 확장시킨다.
- application 플러그인을 적용하면 CLI 애플리케이션(실행 가능한 자바 프로그램) 을 빌드할 수 있다.
- application 플러그인을 적용하면 java 플러그인도 자동으로 같이 적용
관습 속성 사용
- 플러그인을 적용하면 추가적으로 "속성(properties)"이나 "메서드(methods)"를 사용할 수 있다.
- 여기서는 application 블록 안에서 mainClass를 설정하고 있다. (프로그램 실행의 시작점이 되는 메인 클래스를 지정하는 것.)
의존성 추가 (Add dependencies)
JUnit, Google Guava와 같은 라이브러리를 maven에서 가져와 프로젝트에서 사용할 때 dependencies{} 블록에 추가한다.
Task
tasks.register("hello") {
doLast { println("Hello!") }
}
tasks.register("greet") {
dependsOn("hello")
doLast { println("How are you?") }
}
다음과 같이 사용자 task를 정의할 수 있고 gradlew.bat greet 명령을 통해 커스텀 task를 실행할 수 있다.

tasks.named<Test>("test") {
// Use JUnit Platform for unit tests.
useJUnitPlatform()
}
다음과 같은 task의 경우 JUnit5 환경에서 테스트를 진행하는 것을 명시해주는 것이다. 직접 제거하고도 gradle build를 수행해보았는데 큰 차이는 없었다.
gradlew.bat tasks
다음 명령어를 통해 해당 프로젝트에서 사용할 수 있는 task 리스트를 확인할 수 있다.
4. 증분 빌드 및 빌드 캐시
Gradle은 다양한 방법으로 빌드를 최적화한다.
증분 빌드는 그 중 한 방법으로 이전 빌드 이후, 입력(input)이나 출력(output)이 변하지 않았다면 해당 태스크는 다시 실행하지 않고 건너뛴다.
증분 빌드가 동작하는지 확인하기 위해 다음 설정을 진행하자
루트 폴더에 gradle.properties 파일에 다음 코드 추가
org.gradle.console=verbose
이제 빌드시 task 상태가 더 자세하게 출력된다.

이렇게 보면 어떤 task가 동작했는지와 Cache가 잘 동작했는지를 파악할 수 있다.
여기서 main 코드를 수정해서 보면 UP-TO-DATE가 사라지는걸 볼 수 있다. (증분 빌드 테스트)

증분 빌드는 브랜치를 바꾸거나 이전 결과물이 사라지면 다시 전체 빌드가 발생한다.
이럴 때 Build Cache가 유용하다. 이전 빌드 결과물을 디스크에 캐시해두고 동일한 작업이면 다시 실행하지 않고 결과만 복사해서 사용한다. 아래 설정을 gradle.properties 파일에 추가해서 사용한다.
org.gradle.caching=true
처음 gradlew.bat build를 실행하면 입출력 정보를 local cache에 저장한다. 원격 빌드 캐시를 통해 다수의 개발자가 함께 공유 가능한 캐시를 서버에 저장하고 사용할 수 있다.
But, 이 옵션은 default 옵션은 아니다. 이유는 잘못 사용되면 문제의 원인이 되기 쉬운기능이기 때문에 주의해서 사용해야한다. ( 디스크 사용량 급증, 캐시 일관성 보장 문제)
5. 사전과제 환경 원인 분석
기존 환경인 Gradle Groovy의 경우 gradle 8.5버전으로 빌드한 것이 캐시되어있어 빠르게 빌드가 진행되었다.
→ 그럼 Gradle Kotlin의 경우 빌드하면 오래걸렸던 것을 재현하려면 노트북 ~./gradle 내의 폴더를 다 삭제하고 진행해보면 되겠다는 생각으로 테스트 환경을 구성하였습니다.
먼저 데몬으로 돌아가는 gradle 종료후 kotlin 프로젝트를 만들어 IntelliJ 상에서 Reload All Gradle Projects를 실행했다.
gradle init --type java-application --dsl kotlin


IDE 상에서 초기 설정 시간이 groovy의 경우10분이 걸렸다.
테스트 당시에 특히 3분 경과 후에는 로그 출력도 없이 멈춘 것처럼 보여 당황했는데, 그 이유를 나중에서야 정확히 알 수 있었습니다.
알고 보니 이 시점에서는 다음과 같은 작업들이 IDE 내부에서 조용히 진행 중이었습니다:
- Gradle 배포본의 압축 해제
- 내부 파일들의 해시 검증
- Kotlin 스크립트(Groovy도 마찬가지)의 컴파일 및 Classpath 생성
- 프로젝트 전체에 대한 IDE 인덱싱 작업
문제는 이 과정들이 IntelliJ의 Gradle 연동 기능을 위한 작업이지, 우리가 일반적으로 말하는 “빌드”가 아니었다는 점입니다. 즉, IDE가 사용할 수 있는 개발 환경을 준비하는 데 시간이 오래 걸렸던 것이며, 실제로는 아무 문제도 없었습니다.
그리고 당시에는 IDE에 의존적으로 개발만 했던 경험 때문에 직접 gradlew.bat run을 통해 실행시키는 방법도 몰랐다는 점에서 많이 아쉽다는 생각을 하게 되었습니다.
실제로 프로젝트를 실행시키는데 필요했던 것은 다음 명령어 한줄이었습니다.
gradlew.bat run
6. 마치며
취업 준비를 하며 부족한 부분이 많다는 사실도 깨닫는거 같습니다 특히 지금까지 Spring Boot 프로젝트를 생성할 때 주로 https://start.spring.io/에만 의존해왔다는 점을 이번 기회를 통해 돌아보게 되어 의미가 있었습니다.
다음 블로그글에서는 멀티 모듈 프로젝트를 실습해보며 여러가지 플러그인을 사용해보려고 합니다. 또한, 직접 스프링부트 플러그인을 추가해서 실제로 제가 자주 사용했던 환경을 직접 세팅해보겠습니다.
'Gradle' 카테고리의 다른 글
| Gradle Plugin로 Github Actions CI에 적용하기 (0) | 2025.05.02 |
|---|