Posts Gradle 알아보기
Post
Cancel

Gradle 알아보기

Gradle 프로젝트 구조


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
project
 ├── gradle/                     (1)
 │    ├── libs.versions.toml     (2)
 │    └── wrapper/
 │         ├── gradle-wrapper.jar
 │         └── gradle-wrapper.properties
 │
 ├── gradlew                     (3)
 ├── gradlew.bat                 (3)
 ├── settings.gradle(.kts)       (4)
 ├── subproject-a/
 │    ├── build.gradle(.kts)     (5)
 │    └── src/                   (6)
 └── subproject-b/
      ├── build.gradle(.kts)     (5)
      └── src/                   (6)

(1) : gradle/

  • Gradle 디렉터리, wrapper 파일과 기타 파일 저장소.

(2) : libs.versions.toml

  • Gradle version catalog 파일, 의존성 관리용.
  • 라이브러리 버전을 한 곳에서 관리하는 카탈로그.
  • 예시:
1
2
3
4
5
6
7
[versions]
spring-boot = "3.2.0"
junit = "5.10.0"

[libraries]
spring-boot-starter-web = { module = "org.springframework.boot:spring-boot-starter-web", version.ref = "spring-boot" }
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" }
  • 이렇게 정의해 두면 각 build.gradle에서 버전을 직접 쓰지 않고 이름으로 불러올 수 있음

(3) : gradlew, gradlew.bat

  • Gradle wrapper 스크립트
  • gradlew (리눅스/맥), gradlew.bat (윈도우)
  • 로컬에 Gradle이 없어도, 프로젝트가 지정한 Gradle 버전을 자동으로 다운로드해서 실행 가능.
  • 즉, 모든 개발자가 같은 버전의 Gradle을 쓰게 보장.

(4) : settings.gradle

  • 루트 프로젝트 이름과 서브프로젝트를 정의하는 Gradle 설정 파일.
  • 멀티 프로젝트의 구성도 같은 것.
  • 예시:
    1
    2
    
    rootProject.name = "my-multi-app"
    include("subproject-a", "subproject-b")
    
  • 이렇게 해야 Gradle이 subproject-a, subproject-b를 인식.
  • 멀티 모듈 프로젝트에서는 해당 파일이 반드시 필요함 (단일 모듈 프로젝트에서는 없어도됨)

(5) : build.gradle

  • 각 서브프로젝트의 빌드 스크립트
  • 각 모듈(예: subproject-a, subproject-b)이 어떤 언어, 어떤 의존성, 어떤 플러그인을 쓰는지 등을 정의
1
2
3
4
5
6
7
8
plugins {
    id("java")
}

dependencies {
    implementation(libs.spring.boot.starter.web)
    testImplementation(libs.junit.jupiter)
}

(6) : src/

  • 프로젝트의 소스 코드 및 기타 파일.
1
2
3
4
src/
 ├── main/java/...     # 실제 애플리케이션 코드
 ├── main/resources/   # 설정 파일, 정적 리소스
 └── test/java/...     # 테스트 코드

핵심 개념


Projects, Build Scripts, Dependencies, Tasks, Plugins

1. Projects

  • Gradle 프로젝트는 애플리케이션이나 라이브러리처럼 빌드할 수 있는 소프트웨어 조각이다.
  • 단일 프로젝트 빌드는 루트 프로젝트라 불리는 단일 프로젝트를 포함한다.
  • 멀티 프로젝트 빌드는 하나의 루트 프로젝트와 여러 개의 하위 프로젝트를 포함한다.
1
2
my-app/
 └── build.gradle
1
2
3
4
5
6
7
my-multi-app/
 ├── build.gradle      # 루트 프로젝트
 ├── settings.gradle   # 어떤 서브프로젝트 있는지 선언
 ├── app/              # 서브 프로젝트1
 │   └── build.gradle
 └── library/          # 서브 프로젝트2
     └── build.gradle
1
2
3
// settings.gradle
rootProject.name = "my-multi-app"
include("app", "library")

2. Build Scripts

  • 빌드 스크립트는 Gradle에게 프로젝트를 빌드하기 위해 어떤 단계를 수행해야 하는지를 알려준다.
  • 각 프로젝트는 하나 이상의 빌드 스크립트를 포함할 수 있다.
  • 예 : “이 프로젝트는 자바야, 이 라이브러리 필요해”라고 Gradle에게 알려주는 설명서.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// build.gradle
plugins {
    id 'java' // 자바 관련 Task들 자동 등록
}

group = 'com.example'
version = '1.0.0'

repositories {
    mavenCentral() // 외부 라이브러리를 여기서 가져옴
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web:3.2.0'
    testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0'
}

3. Dependencies and Dependency Management

프로젝트에 필요한 외부 리소스(=의존성)를 선언하고 해결하는 자동화된 기법

  • 의존성(Dependencies)에는 JAR, 라이브러리, 소스 코드 등이 포함되며, 이는 프로젝트 빌드를 지원한다.
  • Gradle은 이러한 의존성을 자동으로 다운로드하고, 캐싱하고, 해결해주기 때문에 개발자가 직접 관리할 필요가 없다.
  • 또한 Gradle은 버전 충돌 처리를 해주고, 유연한 버전 선언 방식도 지원한다.
1
2
3
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web:3.2.0'
}
  • Gradle에서는 의존성을 Configuration(구성) 별로 그룹화하여, 언제/어떻게 사용되는지를 정의한다.
    • implementation: 프로덕션 코드의 컴파일 및 실행에 필요한 의존성
    • api: 라이브러리 소비자에게도 공개되어야 하는 의존성 (즉, 라이브러리를 쓰는 쪽에서도 접근 가능해야 하는 경우)
  • 의존성 트리를 확인하려면 dependencies 작업을 실행할 수 있다.
    • 예: :app 프로젝트의 의존성을 보려면
    • $ ./gradlew :app:dependencies

버전 카탈로그

빌드 전체에서 의존성 좌표(group:name:version)와 버전을 중앙 집중적이고 일관되게 관리하는 방법을 제공한다.

build.gradle(.kts) 파일마다 버전을 직접 적는 대신, libs.versions.toml 파일에 한 번만 정의하고 공통으로 가져다 쓸 수 있다.

  • 이를 통해 다음이 쉬워진다:
    • 여러 서브프로젝트 간 공통 의존성 선언 공유
    • 중복 및 버전 불일치 방지
    • 대규모 프로젝트에서 의존성과 플러그인 버전 강제
  • 일반적으로 libs.versions.toml 파일에는 4개의 섹션이 있다:
    • [versions] → 플러그인 및 라이브러리가 참조할 버전 번호 선언
    • [libraries] → 빌드 파일에서 사용할 라이브러리 정의
    • [bundles] → 여러 의존성을 묶어서 세트로 정의
    • [plugins] → 플러그인 정의
  • 예시:
1
2
3
4
5
6
7
[versions]
guava = "32.1.2-jre"
juneau = "8.2.0"

[libraries]
guava = { group = "com.google.guava", name = "guava", version.ref = "guava" }
juneau-marshall = { group = "org.apache.juneau", name = "juneau-marshall", version.ref = "juneau" }
1
2
3
4
5
// build.gradle
dependencies {
    implementation(libs.guava)
    api(libs.juneau.marshall)
}

Dependency Configurations

Gradle 프로젝트에서 선언되는 모든 의존성은 특정 범위(scope)에 적용된다.
Dependency Configuration(의존성 구성)은 프로젝트 안에서 서로 다른 목적에 맞게 의존성을 분리하여 정의하는 방법이다.

  • 이것은 빌드 과정의 여러 단계(컴파일, 실행, 테스트 등)에서 의존성이 언제, 어떻게 사용될지를 결정한다.
  • Configurations는 Gradle의 의존성 해석(dependency resolution)에서 기본적인 역할을 한다.
  • Java Library Plugin을 적용하면 프로젝트가 Java 라이브러리를 생산할 수 있고, 이 플러그인은 여러 Configuration을 자동으로 추가한다.
    • 예: 소스 코드 컴파일, 테스트 실행 등 다양한 classpath에 필요한 의존성 그룹.
Configuration설명비고
api컴파일 & 실행 시 모두 필요한 의존성. 또한 배포된 API에 포함된다.java-library 플러그인 전용
implementation컴파일 & 실행 시 필요한 의존성. 하지만 API로 노출되지는 않는다.일반적인 경우 가장 많이 사용
compileOnly컴파일 시에만 필요한 의존성. 실행(runtime)이나 배포 시에는 포함되지 않는다.예: Lombok, Annotation Processor
compileOnlyApi컴파일 시에만 필요하지만, 배포된 API에는 포함된다. 
runtimeOnly실행 시에만 필요한 의존성. 컴파일 classpath에는 없음.예: JDBC Driver, 로깅 구현체
testImplementation테스트 코드 컴파일 & 실행에 필요한 의존성.예: JUnit
testCompileOnly테스트 코드 컴파일에만 필요한 의존성. 실행에는 불필요. 
testRuntimeOnly테스트 실행 시에만 필요한 의존성. 
  • annotationProcessor는 목록에 없을까?
    • 이유는, annotationProcessor는 Java 플러그인/Java Library 플러그인에서 추가적으로 정의한 특수 Configuration이기 때문
    • 원래 Gradle 기본 문서에서 “주요 Configuration”만 소개했을 때는 annotationProcessor가 빠져 있음
  • annotationProcessor는 다음을 위해 사용됨:
    • 컴파일 시에 애노테이션 프로세서를 실행해야 할 때 (예: Lombok, MapStruct)
    • 실행(runtime)이나 API 배포와는 무관하게, 오직 컴파일 단계에서만 필요

4. Tasks

작업(Task)은 코드를 컴파일하거나 테스트를 실행하는 것 같은 독립적인 작업 단위이다.

  • 일반적인 작업(Task)의 유형에는 다음이 포함된다:
    • 소스 코드 컴파일
    • 테스트 실행
    • 출력물 패키징 (예: JAR 또는 APK 생성)
    • 문서 생성 (예: Javadoc)
    • 빌드 산출물을 저장소에 배포
  • 각 작업은 독립적이지만, 다른 작업이 먼저 실행되어야만 수행될 수도 있다.
    • Gradle은 이 정보를 사용해서 가장 효율적인 순서로 작업들을 실행하며, 이미 최신 상태인 작업은 건너뛴다.
  • Gradle 플러그인과 빌드 스크립트는 프로젝트에서 사용할 수 있는 작업들을 정의한다.
    • $ ./gradlew tasks로 확인 가능

Task 실행

./gradlew <Task명>

  • 작업을 실행하려면 프로젝트 루트 디렉터리에서 Gradle Wrapper를 사용한다.
  • 예를 들어, build 작업을 실행하려면: ./gradlew build
    • 이 명령은 build 작업과 그에 필요한 모든 의존 작업들을 실행한다.
  • Gradle은 어떤 작업이 다른 어떤 작업에 의존하는지 알고 있으며, 올바른 순서로 자동으로 실행한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ ./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:check
> Task :app:build

BUILD SUCCESSFUL in 764ms
7 actionable tasks: 7 executed

5. Plugins

Gradle 빌드 시스템에 추가 기능을 제공하는 재사용 가능한 소프트웨어 조각

  • 추가 기능 예:
    • 새로운 작업(Task)을 빌드에 추가한다 (예: compileJava, test)
    • 새로운 설정(Configuration)을 추가한다 (예: implementation, runtimeOnly)
    • 새로운 DSL(Domain Specific Language, 빌드 스크립트에서 쓸 수 있는 전용 문법) 요소를 제공한다 (예: application {}, publishing {})
  • 즉, Gradle은 기본적으로 최소 기능만 있고 플러그인을 붙이면 기능이 늘어남.
  • 예시:
1
2
3
4
5
6
7
8
9
plugins {
    id("java") // java 플러그인을 적용
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web") // 코드 컴파일 시 필요한 라이브러리
    testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")       // 테스트 전용 라이브러리
    runtimeOnly("com.h2database:h2")                                   // 실행 시에만 필요한 라이브러리
}
  • java 플러그인을 적용하기 전에는 implementation, testImplementation, runtimeOnly 같은 Configuration이 없음.
  • 플러그인을 적용하면서 Gradle이 이런 “의존성 그룹”을 자동으로 만들어줌.
1
2
3
4
5
6
7
plugins {
    id("org.springframework.boot") version "3.2.0"
}

tasks.named<org.springframework.boot.gradle.tasks.run.BootRun>("bootRun") {
    args("--spring.profiles.active=dev")
}
  • 플러그인은 특정 도메인이나 워크플로우에 필요한 모든 로직을 가져온다.
    • 도메인 = “특정한 문제 영역” (예: Java 빌드, Spring Boot 실행, Android 앱 패키징 등)
    • 플러그인은 그 도메인에 필요한 Task, Configuration, DSL 블록을 한꺼번에 제공한다.
    • 즉, 어떤 워크플로우(흐름)를 실행하기 위해 필요한 것들을 전부 제공한다.
    • Spring Boot 플러그인을 적용하면:
      • bootRun, bootJar 같은 Task 자동 추가
      • developmentOnly, runtimeClasspath 같은 Configuration 추가
      • springBoot {} 같은 DSL 블록 제공
      • 결과적으로 Spring Boot 앱 빌드/실행/배포에 필요한 전 과정을 자동화할 수 있음.

플러그인 종류

Gradle은 세 가지 타입의 플러그인을 지원한다

1. 스크립트 플러그인 (Script plugins)

  • 다른 Gradle 스크립트 파일(.gradle / .gradle.kts)을 불러다 쓰는 방식.
  • apply from: 문법으로 적용.
  • 흔히 공통 설정을 common.gradle 같은 파일로 분리해서 여러 프로젝트에서 재사용할 때 사용.
1
2
3
4
5
6
// common.gradle
tasks.register("hello") {
    doLast {
        println("Hello from common.gradle")
    }
}
1
2
// build.gradle
apply from: "common.gradle"

2. 사전 컴파일된 플러그인 (Pre-compiled plugins)

  • Kotlin 또는 Groovy 코드로 패키징된 플러그인.
  • 직접 플러그인 클래스를 작성해서 프로젝트 내에서 재사용.
  • buildSrc/ 디렉터리에 넣으면 자동으로 인식됨.
1
2
3
4
project-root/
 ├── buildSrc/
 │   └── src/main/kotlin/MyConventionPlugin.kt
 └── build.gradle.kts
1
2
3
4
5
6
7
8
9
10
11
// buildSrc/src/main/kotlin/MyConventionPlugin.kt
import org.gradle.api.Plugin
import org.gradle.api.Project

class MyConventionPlugin : Plugin<Project> {
    override fun apply(project: Project) {
        project.tasks.register("greet") {
            doLast { println("Hello from MyConventionPlugin!") }
        }
    }
}
1
2
3
4
// build.gradle.kts
plugins {
    id("my-convention-plugin") // 클래스 이름 기반으로 자동 인식
}

3. 바이너리 플러그인 (Binary plugins)

  • 패키징·배포된 플러그인(Plugin Portal 또는 Maven에 올라온 것).
  • 대부분 우리가 쓰는 유명한 플러그인들 (예: java, application, spring-boot, spotless)
1
2
3
4
5
plugins {
    id("java-library") // Gradle 코어 플러그인 (버전 지정 필요 없음)
    id("org.springframework.boot") version "3.2.0" // 커뮤니티 플러그인
    id("com.diffplug.spotless") version "6.25.0"   // 코드 포맷팅 플러그인
}

플러그인 배포 경로

Gradle 플러그인은 여러 소스에서 제공되며, 상황에 맞게 선택할 수 있다.

1. 코어 플러그인 (Core Plugins)

  • Gradle 배포판에 포함된 플러그인. (목록)
  • Gradle 팀에서 관리한다.
  • 예: java-library
1
2
3
plugins {
    id("java-library")
}

2. 커뮤니티 플러그인 (Community Plugins, Plugin Portal)

  • Gradle 커뮤니티가 만든 플러그인.
  • Gradle Plugin Portal에 공개되어 있고, ID와 버전으로 적용 가능.
1
2
3
plugins {
    id("org.springframework.boot").version("3.1.5")
}

3. 로컬/커스텀 플러그인 (Local or Custom Plugins)

  • 직접 만든 플러그인. 단일 프로젝트에서만 쓰거나 여러 프로젝트에서 공유할 수 있다.
  • 가장 흔한 형태는 Convention Plugin.
    • buildSrc/ 디렉토리나 별도 build-logic 모듈 안에 위치.
    • Kotlin/Groovy로 작성.

Gradle Wrapper


Gradle Wrapper는 항상 신뢰할 수 있고, 제어되고, 표준화된 빌드 실행을 보장하기 때문에 권장되는 Gradle 빌드 방식이다.

  • Wrapper 스크립트는 선언된 Gradle 버전을 호출하며, 필요하다면 먼저 그것을 다운로드한다.
  • 프로젝트 루트 디렉터리에서 gradlew 또는 gradlew.bat 파일로 제공된다:
1
2
3
4
root
├── gradlew     // THE WRAPPER
├── gradlew.bat // THE WRAPPER
└── ...
  • 프로젝트에 이 파일들이 없다면, 그것은 아마도 Gradle 프로젝트가 아니거나, Wrapper가 아직 설정되지 않은 것일 수 있다.
  • Wrapper는 인터넷에서 직접 다운로드하는 것이 아니다. Gradle이 설치된 머신에서 gradle wrapper 명령을 실행하여 생성해야 한다.

image

출처 : https://docs.gradle.org/current/userguide/gradle_wrapper_basics.html

Wrapper가 제공하는 이점

  • 지정된 Gradle 버전을 자동으로 다운로드하고 사용한다.
  • 프로젝트를 특정 Gradle 버전에 표준화한다.
  • 서로 다른 사용자 및 환경(IDE, CI 서버 등)에 동일한 Gradle 버전을 제공한다.
  • Gradle을 직접 수동 설치하지 않고도 Gradle 빌드를 쉽게 실행할 수 있게 해준다.

만약 실행 환경별로 버전이 다르면 ?

1. 빌드 스크립트 문법 차이

  • Gradle은 계속 발전하면서 DSL(Groovy/Kotlin) 문법이 추가·변경·Deprecated 됨
    • 예: 예전 버전에서 쓰던 compile-> implementation으로 변경
  • Gradle 4.x를 쓰면 compile이 잘 동작하지만, Gradle 7.x에선 에러 발생

2. 플러그인 호환성 문제

  • Spring Boot, Kotlin, Android 등은 Gradle 버전에 맞춰 플러그인을 업데이트
  • 특정 버전의 Gradle에서는 플러그인이 아예 동작하지 않거나 경고/에러가 발생할 수 있음.
    • 예: 최신 Android Gradle Plugin은 최소 Gradle 7.x 이상을 요구. 누군가는 6.x를 쓰면 빌드 자체가 불가능.
  • CI/CD 서버나 다른 개발자 환경에서 빌드 깨짐.

3. 빌드 결과물 차이

  • 같은 코드를 빌드해도 Gradle 내부 동작(캐싱, 병렬 빌드 방식, dependency resolution 방식)이 버전에 따라 달라집니다.
    • 예: Gradle 6.x와 7.x는 의존성 중복 처리 방식이 달라서, 한쪽은 충돌 나고 다른 한쪽은 자동 해결될 수도 있음.
  • 로컬에서 테스트한 JAR/WAR는 잘 돌아가는데, 서버에서 빌드된 건 오류가 날 수 있음.

4. CI/CD 환경과 로컬 환경 불일치

  • 로컬 개발자는 Gradle 8.x, CI 서버는 Gradle 7.x → 서버에서만 빌드 에러 발생.
  • 빌드 실패 원인을 파악하기 어려워지고, 시간을 허비하게 됨.

Wrapper 구성 요소

1
2
3
4
5
6
7
.
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
└── gradlew.bat
  • gradle-wrapper.jar
    • 작은 JAR 파일로, Gradle Wrapper 코드를 담고 있다.
    • 프로젝트에 필요한 Gradle 버전이 설치되어 있지 않으면 이를 다운로드하고 설치하는 역할을 한다.
  • gradle-wrapper.properties
    • Gradle Wrapper에 대한 설정 속성을 담고 있는 파일이다.
    • 예: Gradle을 어디서 다운로드할지(배포 URL), 배포 유형(ZIP 또는 TARBALL).
  • gradlew
    • Unix 계열 시스템에서 사용하는 셸 스크립트.
    • gradle-wrapper.jar를 감싸는 역할을 하며, Gradle을 수동 설치하지 않고도 Gradle 작업을 실행할 수 있다.
  • gradlew.bat
    • Windows에서 사용하는 배치 스크립트.
    • 목적은 gradlew와 동일하다.

실행 흐름 예시

./gradlew build

  • gradlew (shell script) 동작:
    • CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 설정
    • Java 실행기로 gradle-wrapper.jar를 실행
    • 이때, gradle-wrapper.properties 파일도 읽어서 “Gradle 몇 버전, 어디서 받을지” 확인
  • gradle-wrapper.jar 동작:
    • 지정된 버전의 Gradle 설치 여부 확인
    • 없으면 다운로드 (ZIP이나 TAR)
    • 해당 Gradle 런처를 호출해서 실제 빌드 실행

주의 사항

  • 이 파일들은 절대 직접 수정해서는 안 된다.
  • Gradle 버전을 확인하거나 업데이트하려면 명령줄에서 다음 명령을 사용한다:
1
2
$ ./gradlew --version
$ ./gradlew wrapper --gradle-version 7.2
1
2
$ gradlew.bat --version
$ gradlew.bat wrapper --gradle-version 7.2

참고 자료


This post is licensed under CC BY 4.0 by the author.