우리는 스프링을 사용하면서 Gradle이라는 추상화 도구를 통해 간편하게 로컬에서 프로젝트를 띄워볼 수 있었다.
본격적으로 배포하기에 앞서, Gradle의 기본 구조에 대해 이해해보도록 하자.
Gradle이란?
Gradle은 프로젝트 초기화 및 빌드 자동화 도구이다. 다시말해, 설정/외부 라이브러리 의존 관리를 간단하게 만들어주는 역할을 수행한다.
우리가 IDE, Spring Boot initializer로 흔히 생성하는 스프링 프로젝트는 gradle과 같은 빌드 도구를 기반으로 생성되는 프로젝트이다.
gradle init을 수행하면 gradle이 직접 만들어준 프로젝트를 실제로 확인해볼 수 있다.
Gradle의 자세한 목적
그렇다면, Gradle을 쓰는 이유는 뭘까?
- 설정/의존성 관리 통일
- 컴파일 방식 정의
- 테스트 실행
- 패키징
- 작업 설정
Gradle은 간편한 빌드를 도우면서, 동시에 빌드 시 코드 레벨에 수행할 수 있는 다양한 기능들을 추가할 수 있게 해주는 도구이기도 하다.
본격적으로 Gradle의 기본 개념에 대해 알아보자.
목차
- gradle의 핵심 개념
- gradle wrapper이란?
- settings.gradle의 기본 구조
- build.gradle의 기본 구조
- 종속성 및 종속성 관리란?
- Task란?
Gradle의 핵심 개념
Gradle의 아키텍처
Gradle에는 5가지 핵심 개념이 있다.
- 프로젝트
- 빌드 스크립트
- 종속성 및 종속성 관리
- Tasks(작업)
- Plugins(플러그인)
지금부터 Gradle의 핵심 개념 5가지를 알아보자.
프로젝트
Gradle에서 프로젝트는 애플리케이션이나 라이브러리와 같이 빌드 가능한 소프트웨어를 의미한다.
단일 프로젝트 빌드에는 "루트 프로젝트" 라는 단일 프로젝트가 포함된다.
- 일반적으로 intellij IDEA에서 프로젝트를 기본 옵션으로 생성하면, build.gradle과 settings.gradle이 루트 폴더 내에 같이 존재하는 것을 확인할 수 있다.
멀티 모듈 프로젝트 빌드에는 하나의 루트 프로젝트와 여러개의 하위 프로젝트가 있다.
- 루트 프로젝트는 보통 settings.gradle를 가지며, 서브 프로젝트는 각각 해당 모듈의 build.gradle를 가진다.
- 멀티모듈 개념을 들어봤다면, 이해하기 쉬울 것이다.
빌드 스크립트
- 프로젝트를 빌드하기 위해 어떠한 단계를 거쳐야 하는지를 gradle 로 설명한 파일이다.
- settings.gradle
- 루트 프로젝트를 위한 스크립트이다.
- build.gradle
- 서브 프로젝트를 위한 스크립트이다.
Dependency & Dependency Management
- 프로젝트에 필요한 외부 리소스를 선언하고 해결하기 위한 자동화된 기술이다.
- 우리가 일반적으로 외부 프레임워크를 가져올 때, 의존성 관리 도구를 이용해 가져오게 된다.
Tasks
- Gradle에선 코드 컴파일/테스트 실행과 같은 기본적인 작업 단위를 Task라고 부른다.
- 각 프로젝트에는 빌드 스크립트 혹은 플러그인 내부에 정의된 하나 이상의 Task가 존재하고, 해당 Task를 따라 빌드가 시작되는 것이다.
Plugins
- 코드 컴파일, 테스트 실행 또는 아티팩트 패키징과 같은 Task를 Gradle 내에서 실행하기 위해서, Gradle의 기본 기능을 확장하는 도구를 의미한다.
- Gradle의 기능을 확장하는 도구이다.
- 몇몇 플러그인은 프로젝트에 Task를 직접 추가하기도 한다.
gradle 프로젝트 구조
일반적으로 gradle init
을 수행했을 때 생성되는 Gradle 프로젝트의 구조에 대해 알아보자.
각 항목에 대한 자세한 설명은 뒤에 설명되니, 일단 큰 그림을 잡고 가보도록 하자.
- Wrapper 파일 등을 저장하는 Gradle 디렉토리
- 종속성 관리를 위한 Gradle 버전 카탈로그
- gradle wrapper 스크립트
- 일반적으로 Gradle은 프로젝트 구성요소로 특정 gradle 버전을 포함하고, wrapper 형태로 함께 형상관리가 수행된다.
- 왜인지는 조금 이따가 알아보도록 하자.
- settings.gradle
- 루트 프로젝트와 하위 프로젝트를 정의하는 gradle 설정 파일이다.
- 프로젝트 루트 폴더에 유일하게 존재한다.
- 각 서브 프로젝트의 Gradle 빌드 스크립트
- build.gradle의 형태로 작성된다.
- 일반적으로 단일 모듈 프로젝트인 경우, settings.gradle과 같은 폴더에 존재하게 된다.
- 소스 코드
gradle로 빌드하는 방법
gradle build
명령을 통해, 현재 프로젝트를 빌드할 수 있다.- 하지만 보통,
./gradlew build
(윈도우는gradlew.bat build
) 와 같이 래퍼를 이용하여 빌드를 수행하게 된다.
gradle wrapper
Gradle Wrapper에 대해 알아보자.
Gradle Wrapper란?
Gradle 프로젝트에는 gradle wrapper 가 존재하며, 래퍼 스크립트는 선언된 gradle 버전을 호출하고, 필요한 경우 미리 다운로드한다.
해당 래퍼 스크립트는 gradlew 혹은 gradlew.bat 파일로 사용할 수 있다.
만약 프로젝트에 위 파일이 포함되어 있지 않다면, gradle 프로젝트가 아니거나, 래퍼가 아직 설정되지 않은 것이다.
왜 직접 시스템에 설치된 gradle 버전을 사용하지 않는 걸까?
그 이유는 다음과 같다.
- 특정 Gradle 버전을 자동으로 다운로드하여 사용하고, 주어진 Gradle 버전에 따라 프로젝트를 표준화한다.
- 다양한 사용자 환경(IDE, CI 서버 등)에 동일한 gradle 버전을 제공한다.
- gradle을 수동으로 설치하지 않고도, Gradle 빌드를 쉽게 실행할 수 있게 된다.
그리하여, 다음 두가지를 반드시 분류할 수 있어야 한다.
gradle build
- 시스템에 설치된 Gradle 배포판을 사용하는 경우
./gradlew build
- Gradle Wrapper 를 사용하는 경우
gradle wrapper의 구조
gradle wrapper는 다음과 같은 구조로 이루어져 있다.
gradle-wrapper.jar
- Gradle wrapper 코드가 포함된 작은 JAR 파일이다.
- 프로젝트에 맞는 Gradle 버전이 아직 설치되어 있지 않은 경우, 해당 버전을 다운로드하고 설치하는 역할을 한다.
gradle-wrapper.properties
- Gradle Wrapper의 구성 속성이 포함되어 있는 파일이다.
- 배포 URL(Gradle을 다운로드할 위치) 및 배포 유형(ZIP 또는 TARBALL)이 들어있다.
3, 4. 래퍼 역할을 하는 셸 스크립트 및 배치 파일이다.
참고) 위 4가지 gradle wrapper 파일을 변경하지 말자.
- 필요하다면 직접 다운로드 받아서 사용하자.
settings.gradle
의 기본 구조
settings.gradle 에 대해 알아보자.
settings.gradle이란?
- 프로젝트의 구조를 정의하고, 빌드에 하위 프로젝트를 추가하는 역할을 수행하는 파일이다.
- 싱글 모듈 프로젝트 빌드의 경우, 설정 파일은 선택사항이다.
- 멀티 모듈 프로젝트 빌드의 경우, 설정 파일은 필수 사항이며, 모든 하위 프로젝트를 선언해야 한다.
참고) 멀티모듈을 쓰는 이유
- 주로 모듈 레벨의 책임 분리에서 사용한다.
- 대표적으로 여러 개의 데이터소스를 사용할 경우, 각 데이터소스에 대한 책임을 분리하기 위해 사용한다.
- 서로 다른 데이터소스 간에는 서로 다른 트랜잭션 컨텍스트를 가지기 때문에, 하나의 모듈로 가져가게 되면 결합도를 높이게 된다.
- 예를 들어, 하나의 논리적인 트랜잭션이 여러개의 독립적인 데이터 소스에 걸쳐 작업을 수행하게 되면, @Transactional로는 여러개의 데이터소스에 대한 트랜잭션을 손쉽게 관리하기 어렵게 된다.
- 글로벌 트랜잭션 매니저가 각 시스템 트랜잭션 매니저의 상태를 보고받아야 하고, 두 시스템 트랜잭션 매니저 모두에게 "커밋 준비됨" 상태를 보고받아야 그때 커밋을 수행하게 된다.
- 이는 2PC 모델의 방식이며, 동시 처리량을 매우 감소시키는 형태로 동작하게 된다.
- 이럴때는 보통 서로 다른 데이터소스를 별개의 모듈로 두어, 각자 독립적인 트랜잭션을 관리하고, 비동기 및 최종 일관성 처리를 사용하게 된다.
- 이에 대한 자세한 내용은 오프라인 동시성 문제를 참고하도록 하자.(작성예정)
예시로 알아보는 settings.gradle의 구조
settings.gradle은 크게 두가지 구성요소로 분류된다.
- 프로젝트 이름
- 빌드 당 루트 프로젝트는 하나만 존재해야 한다.
- 하위 프로젝트
- 해당 프로젝트에 포함된 하위 프로젝트를 선언한다.
build.gradle
의 기본 구조
build.gradle 에 대해 알아보자.
build.gradle에는 빌드 구성, task, plugin에 대한 세부 정보가 들어있다.
또한, 모든 gradle 빌드는 최소한 하나의 빌드 스크립트로 구성된다.
빌드 스크립트(build.gradle)는 groovy 혹은 kotlin으로 작성되며, 멀티 모듈 프로젝트는 각 하위 프로젝트는 자신의 루트 디렉토리에 자체 build.gradle
을 갖는다.
빌드 스크립트의 용도
- 플러그인 지정
- Gradle의 플러그인(코드 컴파일, 테스트 실행 또는 아티팩트 패키징과 같은 작업을 위해, Gradle의 기본 기능을 확장하는 도구)을 지정한다.
- 종속성 지정
- 프로젝트에서 사용하는 외부 라이브러리 및 도구를 지정한다.
빌드 스크립트에서 지정하는 종속성 유형
- Gradle 및 빌드 스크립트 종속성
- Gradle 자체 또는 빌드 스크립트 로직에 필요한 플러그인 및 라이브러리 종속성을 의미한다.
- 프로젝트 종속성
- 프로젝트의 소스 코드가 올바르게 컴파일되고 실행되기 위해 직접 필요한 라이브러리에 대한 종속성을 의미한다.
예제 - 빌드 스크립트의 구조
- 플러그인을 추가한다.
- 종속성을 추가한다.
- convention property를 사용한다.
플러그인 추가
- 플러그인은 Gradle의 기능을 확장하고, 프로젝트에 Task를 추가할 수 있다.
- 빌드에 플러그인을 추가하는 것을 "플러그인 적용"이라고 하며, 이를 통해 추가 기능을 사용할 수 있다.
- 여기서는 "application" 플러그인을 추가했다.
종속성 추가
- 프로젝트에 종속성을 추가하기 위해 사용한다.
- 여기서는 JUnit Jupiter(JUnit5)를 의존성에 추가했다.
convention properties 사용
- 사용하고자 한 플러그인에 대한 Convention Property를 지정한다.
- 여기선 "application" 플러그인에 필요한 mainClass 속성을 지정해주었다.
- 이를 통해 application 플러그인에 필요한 기본 속성을 지정할 수 있었다.
자세한 빌드 파일의 작성 방법은 내용이 길어지기에, 다른 글로 따로 작성해두었다. 해당 글을 참고하도록 하자.(작성예정)
Gradle의 종속성 및 종속성 관리란?
종속성 및 종속성 관리에 대한 기본 구조를 알아보자.
Gradle에서 종속성 관리란, 프로젝트에 필요한 외부 리소스를 선언하고 가져오기 위한 자동화된 기술을 의미한다.
Gradle에서 종속성이란, 프로젝트 빌드를 지원하는 JAR, 플러그인, 라이브러리 또는 소스코드를 의미한다.
- 이러한 종속성은 빌드 스크립트(build.gradle)에서 선언된다.
Gradle은 이러한 종속성을 다운로드, 캐싱 및 해결하는 작업을 자동으로 처리하여 사용자가 직접 관리할 필요가 없도록 한다.
또한 버전 충돌을 처리하고 유연한 버전 선언을 지원한다.
기본적인 종속성 선언 방법
사실 gradle을 알게 모르게 사용해왔다면, 기본적인 방법 정도는 알고 있을 것이라 생각한다.
- Java 라이브러리 빌드에 대한 지원을 추가하는 Java 라이브러리 플러그인을 적용한다.
- 프로덕션 코드에 사용되는 Google Guava 라이브러리에 대한 종속성을 추가한다.
- 라이브러리 코드에서 사용되는 Apache의 Juneau Marshall 라이브러리에 대한 종속성을 추가한다.
앞에 붙은 수식어에 대한 좀 더 자세하고 다양한 정보는 다음 글을 참고하자.(작성예정)
프로젝트 종속성 확인
- gradle은 이미 정의된
dependencies
Task를 사용하여 종속성 트리를 검사할 수 있다. - 예를 들어, app 프로젝트 내 종속성을 확인하고 싶다면, 다음과 같이 입력하면 된다.
./gradlew :app:dependecies
버전 카탈로그를 이용한 종속성 선언 및 버전 관리 방법
버전 정보를 build.gradle과 분리하여, 버전 정보를 특정 카탈로그에 모아 관리할 수도 있다.
gradle에서 버전 정보를 관리하는 카탈로그는 libs.versions.toml
에 정의한다.
- 해당 카탈로그는 루트 폴더의 gradle 폴더에 저장해야 한다.
해당 카탈로그는 4개의 섹션으로 구성된다.
- [versions]: 플러그인과 라이브러리가 참조할 버전 번호를 선언한다.
- [libraries]: 빌드 파일에서 사용되는 라이브러리를 정의한다.
- [bundles]: 종속성 집합을 정의한다.
- [plugins]: 플러그인을 정의한다.
예를 들면, 다음과 같이 관리할 수 있다.
Gradle의 Task란?
Gradle의 Task에 대해 알아보자.
작업(Task)은 빌드가 수행하는 "독립적인 실행 단위"를 의미한다.
여기에는 클래스 컴파일, JAR 생성, Javadoc 생성 또는 저장소에 아카이브 게시가 포함된다.
일반적인 작업 유형
- 소스 코드 컴파일
- 테스트 실행
- 패키징 생성(ex: JAR, APK 등등 생성)
- 문서 생성 (ex: Javadoc)
- 빌드 아티팩트를 저장소에 게시
이때, 각 작업은 독립적이지만, 다른 작업이 먼저 실행될 수 있다.
Gradle은 이 정보를 사용하여 가장 효율적인 작업 실행 순서를 파악하고, 이미 최신 상태인 작업은 건너뛴다.
작업의 수행
작업의 수행은 ./gradlew <task-name>
명령어를 통해 수행 가능하다.
- 예시
- Task(작업)의 실행
./gradlew build
- 사용 가능한 task 확인
./gradlew tasks
- 기존 output 제거
./gradlew clean
- Task(작업)의 실행
각 Task는 내부적으로 존재하는 의존관계를 파악하여, 가장 먼저 수행되어야 하는 Task 우선으로 순서대로 수행한다.
예를 들어, build
Task 를 수행하면, compileJava
, test
, jar
과 같은 작업도 함께 수행된다.
참고하면 좋을 자료
- Gradle 공식 문서
- Gradle CLI
- gradle 의 각종 명령어에 대해 확인할 수 있다.
- Build Cache
- Build Cache란, 변화가 발생한 부분에 대해서만 re-build를 수행하는 gradle의 핵심 기능이다.
- 해당 링크는 이에 대한 자세한 설명을 담고 있다.
해당 글에 대한 자세한 튜토리얼 예제는 다음 링크에서 수행해볼 수 있다.
https://docs.gradle.org/current/userguide/part1_gradle_init.html