Gradle 이해하기: 초보자를 위한 완벽한 가이드
Gradle 이해하기: 초보자를 위한 완벽한 가이드

Gradle 이해하기: 초보자를 위한 완벽한 가이드

설명
빌드 도구인 Gradle의 기본 개념을 설명하고, 설치부터 프로젝트 설정, 의존성 관리, 태스크 이해, 플러그인 사용까지 Gradle을 효과적으로 사용하는 방법을 초보자를 위해 상세히 안내합니다.
Last Updated
Last updated May 29, 2023
태그
Gradle
빌드도구
의존성관리

1. 소개: Gradle이란 무엇인가?

Gradle은 Java, C/C++, Python 등과 같은 여러 언어를 지원하는 강력한 빌드 도구입니다. 자동화 빌드, 테스트, 배포, 출시 등의 프로세스를 관리하는 데 있어 도움을 줍니다.

Gradle 필요성

  1. 복잡한 빌드 과정: 대규모 프로젝트에서는 종종 여러 라이브러리가 필요하고, 이들 라이브러리는 서로 다른 버전을 요구하는 경우도 있습니다. 이렇게 복잡한 의존성을 수동으로 관리하는 것은 매우 힘들고 오류가 발생하기 쉽습니다. Gradle을 사용하면 이런 의존성을 쉽게 관리할 수 있습니다.
  1. 여러 개발 환경: 개발, 테스트, 스테이징, 프로덕션 등 다양한 환경에서 코드를 실행하려면, 각 환경에 맞는 설정을 해야 합니다. 이런 설정을 수동으로 관리하면, 실수로 잘못된 환경에서 코드를 실행하는 등의 문제가 발생할 수 있습니다. Gradle을 사용하면 이런 환경 설정을 자동화할 수 있습니다.
  1. 빌드 시간: 대규모 프로젝트에서는 전체 빌드를 실행하는 데 많은 시간이 소요됩니다. 그러나 매번 전체 빌드를 실행할 필요는 없습니다. 변경된 부분만 빌드하면 빌드 시간을 크게 단축할 수 있습니다. Gradle은 이런 인크리멘탈 빌드를 지원합니다.
  1. 테스트 자동화: 코드를 변경할 때마다 모든 테스트를 수동으로 실행하는 것은 매우 번거롭습니다. Gradle을 사용하면 테스트를 자동화하고, 실패한 테스트를 즉시 알 수 있습니다.
이처럼 Gradle은 개발 프로세스를 자동화하고 효율화하는 데 큰 도움을 줍니다. 프로젝트의 규모가 커지고 복잡성이 증가함에 따라, Gradle과 같은 빌드 도구의 중요성은 더욱 높아집니다.

1.1. Gradle의 정의

Gradle은 Apache Ant와 Maven의 가장 좋은 기능을 결합하면서도, 유연성과 성능을 향상시킨 빌드 자동화 시스템입니다.
빌드 도구는 Ant -> Maven -> Gradle 순으로 발전되어 왔다.

Ant

  • XML 기반으로 빌드 스크립트를 작성한다.
  • 자유롭게 빌드 단위를 지정할 수 있다.
  • 간단하고 사용하기 쉽다.
  • 유연하지만 프로젝트가 방대해지는 경우 스크립트 관리나 빌드 과정이 복잡해진다.
  • 생명주기(Lifecycle)을 갖지 않아 각각의 결과물에 대한 의존관계 등을 정의해야 한다.

Maven

  • XML 기반으로 작성한다.
  • 생명주기(Lifecycle)와 프로젝트 객체 모델(POM, Project Object Model)이란 개념이 도입됐다.
  • Ant의 장황한 빌드 스크립트를 개선했다.
  • pom.xml에 필요한 라이브러리를 선언하면 자동으로 해당 프로젝트로 불러와 편리하다.
  • 상대적으로 학습 장벽이 높다.
  • 라이브러리가 서로 의존하는 경우 복잡해질 수 있다.

1.2. 왜 Gradle을 사용해야 하는가?

Gradle은 성능적으로 뛰어납니다. 인크리멘탈 빌드를 지원하여 변경된 부분만 빌드할 수 있고, 병렬 처리를 통해 빌드 시간을 단축시킵니다. 또한, 코드 대신 선언적 언어를 사용하여 프로젝트를 설정하므로, 빌드 프로세스를 이해하고 관리하기 쉽습니다.

Gradle의 특징

 
 
Gradle은 앞서 살펴본 Ant와 Maven이 가진 장점을 모아 만들었다. 의존성 관리를 위한 다양한 방법을 제공하고 빌드 스크립트를 XML 언어가 아닌 JVM에서 동작하는 스크립트 언어 ‘그루비’ 기반의 DSL(Domain Specific Language)를 사용한다.
그루비(Groovy)는 자바 문법과 유사하여 자바 개발자가 쉽게 익힐 수 있는 장점이 있으며 Gradle Wrapper를 이용하면 Gradle이 설치되지 않은 시스템에서도 프로젝트를 빌드할 수 있다.
심지어 메이븐(Maven)의 pom.xml을 Gradle 용으로 변환할 수도 있으며 Maven의 중앙 저장소도 지원하기 때문에 라이브러리를 모두 그대로 가져다 사용할 수 있다.
 

2. Gradle 설치하기

Gradle 설치는 간단합니다. 필요한 것은 Java Development Kit(JDK)와 인터넷 연결입니다.

2.1. 필요한 소프트웨어

Gradle을 실행하려면 JDK 또는 JRE 8 이상이 필요합니다. JDK가 아직 설치되지 않았다면, 먼저 설치해야 합니다.

2.2. 설치 과정

Gradle 웹사이트에서 Gradle 바이너리를 다운로드 받아 설치하거나, 패키지 관리자를 사용하여 설치할 수 있습니다.
MacOS 기준으로 간단하게 brew를 이용하면 간편하다.
$ brew install gradle

3. Gradle 프로젝트 시작하기

Gradle 프로젝트를 시작하는 것은 간단합니다. gradle init 명령어를 사용하여 새 프로젝트를 생성할 수 있습니다.
$ gradle init # gradle로 프로젝트 생성. 몇가지 설정들을 선택해주면 된다 $ tree # 기본 gradle 생성 자바 프로젝트는 다음과 같은 구조를 가진다 ├── app # 기본 프로젝트 명 │ ├── build.gradle # 빌드 스크립트를 작성하는 곳 │ └── src # 소스 코드 │ ├── main # 프로그램 코드 │ │ ├── java │ │ │ └── com │ │ │ └── programmers │ │ │ └── java │ │ │ └── App.java │ │ └── resources │ └── test # 테스트 코드 │ ├── java │ │ └── com │ │ └── programmers │ │ └── java │ │ └── AppTest.java │ └── resources ├── gradle # gradle 실행을 위한 런타임 │ └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle # gradle 설정 파일

3.1. 프로젝트 구조 이해하기

Gradle 프로젝트의 기본 구조는 build.gradle 파일, src 디렉토리, gradle 디렉토리 등으로 구성됩니다.

3.2. 기본적인 Gradle 스크립트

build.gradle 파일은 프로젝트의 빌드 설정을 정의합니다. 이 파일에서 의존성, 플러그인, 태스크 등을 설정할 수 있습니다.

4. Gradle 의존성 관리

Gradle의 의존성 관리 시스템은 강력합니다. 프로젝트의 라이브러리 의존성을 선언적으로 관리하고, 필요한 라이브러리를 자동으로 다운로드 받아줍니다.

4.1. 의존성이란 무엇인가?

의존성은 프로젝트가 필요로 하는 외부 라이브러리를 의미합니다. 예를 들어, 웹 애플리케이션을 개발할 때는 종종 Spring, Hibernate 등의 라이브러리가 필요합니다.

4.2. Gradle에서 의존성 관리하기

Gradle에서 의존성은 build.gradle 파일의 dependencies 블록 내에 선언합니다. Gradle은 선언된 의존성을 자동으로 다운로드 받고, 빌드와 테스트에 사용합니다.
파이썬에서 pip로 외부 라이브러리를 설치하는 것처럼, Gradle에서는 build.gradle의 repositories와 dependencies 설정을 통해 설치할 수 있다.

repositories 메소드

  • repositories 메소드는 저장소 설정을 담당한다. 클로저 내용은 RepositoryHandler를 통해 실행된다. RepositoryHandler는 메이븐 중앙 저장소 추가를 위한 mavenCentral 메소드와 Bintray의 jCenter 저장소 추가를 위한 jcenter 메소드를 제공한다.
  • 예를 들어 사내 maven repository에서 라이브러리를 가져올 때 아래와 같이 명시한다.
repositories { mavenCentral() maven { allowInsecureProtocol(true) url 'https://repo.company.com/repository/maven-repository/' } }

ext 메소드

ext 메소드는 그 인자를 buildScript 에서 전역변수로 사용하기 위해 사용된다.
ext { queryDslVersion = '4.4.0' }

dependencies 메소드

의존성 라이브러리를 추가할 때 사용한다. 가장 많이 봤을 것이다.
대표적으로 아래의 옵션들을 제공한다.
  • implementation
    • 의존 라이브러리 수정시 본 모듈까지만 재빌드한다.
  • api
    • 의존 라이브러리 수정시 본 모듈을 의존하는 모듈들을 전부 재빌드한다.
  • compileOnly
    • compile 시에만 빌드하고 빌드 결과물에는 포함하지 않는다.
  • testImplementation
    • 테스트 코드를 수행할 때만 적용
  • annotationProcessor
    • annotation processor 명시 (ex. annotationProcessor 'org.projectlombok:lombok')
repositories { mavenCentral() } dependencies { testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0' implementation 'com.github.javafaker:javafaker:1.0.2' }

5. Gradle 태스크 이해하기

Gradle의 핵심 기능 중 하나는 태스크입니다. 태스크는 빌드, 테스트, 배포 등의 단계를 수행하는 작업 단위입니다.

5.1. 태스크 정의하기

Gradle 태스크는 build.gradle 파일의 task 블록 내에서 정의됩니다. 각 태스크는 이름, 설명, 그리고 실행할 동작을 포함합니다.

task 작성

그럼 지금부터 Gradle의 실행 작업 단위인 태스크(task)를 사용해보자. Gradle은 기본적으로 태스크를 구성하여 실행하며, 태스크를 구성하고 작성하는 것이 빌드 스크립트를 작성하는 과정이다.
task는 아래와 같은 구조를 작성하면 된다.
task 태스크이름 { ... 작업들 }
문자열을 출력하는 간단한 태스크를 작성해보자. build.gradle 파일에 아래와 같이 작성해주면 된다.
task sayHello { println 'Hello world' }

5.2. 태스크 실행하기

Gradle 태스크는 명령 줄에서 gradle <taskName> 형식으로 실행할 수 있습니다. 여러 태스크를 동시에 실행하려면, 태스크 이름을 공백으로 구분하여 지정할 수 있습니다.

task 실행

실행은 터미널에서 gradle 태스크이름으로 형태로 입력하면 된다. gradle 명령어를 사용하면 현재 위치한 경로에서 build.gradle 파일을 찾는다. 실행할 때 -q(quiet) 옵션을 주면 오류에 대한 로그만 출력한다.
$ gradle sayHello gradle sayHello > Configure project : Hello world BUILD SUCCESSFUL in 512ms # -q 옵션을 준 경우 $ gradle -q sayHello Hello world

doFirst, doLast

태스크 내부에서 순서를 지정하고 싶을 때 사용한다. doFirst는 가장 처음 수행하는 액션이고 doLast는 가장 마지막에 수행하는 액션이다. 태스크는 구성된 액션을 순서대로 실행한다.
task greeting { doFirst { println 'hello' } doLast { println 'bye' } }

실행결과

$ gradle greeting > Task :greeting hello bye BUILD SUCCESSFUL in 598ms 1 actionable task: 1 executed

task 축약형

leftshift 연산자는 Gradle 5.0 에서 제거되었다.
태스크는 아래와 같이 축약한 형태로 작성할 수 있다. <<는 doLast와 동일하다.
task hello << { println 'Hello world!' }

task와 파라미터

태스크를 실행할 때 -P파라미터이름=값을 이용하여 파라미터를 전달할 수 있다.
task sayHi { def loopCount = count.toInteger() for(def i in 1..loopCount) { println('LoopCount: ' + i) } }

실행결과

$ gradle -q sayHi -Pcount=3 LoopCount: 1 LoopCount: 2 LoopCount: 3

task간 의존성 설정

태스크가 실행될 때 의존성을 지정하여 태스크의 실행 순서를 지정할 수 있다. dependsOn를 사용하여 먼저 실행할 태스크를 지정할 수 있다.
task AAA(dependsOn:['BBB', 'CCC']) { doFirst { println('doFirst: AAA') } doLast { println('doLast: AAA') } } task BBB { doFirst { println('doFirst: BBB') } doLast { println('doLast: BBB') } } task CCC { doFirst { println('doFirst: CCC') } doLast { println('doLast: CCC') } }

실행 결과

$ gradle AAA > Task :BBB doFirst: BBB doLast: BBB > Task :CCC doFirst: CCC doLast: CCC > Task :AAA doFirst: AAA doLast: AAA BUILD SUCCESSFUL in 603ms 3 actionable tasks: 3 executed

다른 task 호출하기

execute는 Gradle 5.0 에서 제거되었다.
task sayHi { doFirst { println('say Hi') tasks.sayBye.execute() } } task sayBye { doLast { println('say Bye') } }

실행 결과

$ gradle sayHi -q say Hi say Bye
사용자 정의 메서드
직접 메서드를 정의하여 사용할 수 있다.
task methodTask { printMessage('say Hi') } String printMessage(String msg) { println msg }

실행 결과

$ gradle methodTask > Configure project : say Hi BUILD SUCCESSFUL in 593ms
사용자 정의 변수
메서드뿐만 아니라 변수를 정의하여 사용할 수 있다.
task someTask { ext.message = 'say Hi' } task sayHi { println someTask.message }
실행 결과
$ gradle -q sayHi say Hi

6. Gradle 플러그인 사용하기

Gradle 플러그인은 빌드와 배포 프로세스를 확장하는 데 사용됩니다. 예를 들어, Java 플러그인은 Java 컴파일, 테스트, JAR 패키징 등의 태스크를 제공합니다.

6.1. 플러그인이란 무엇인가?

플러그인은 특정 기능을 추가하거나 확장하는 소프트웨어 컴포넌트입니다. Gradle 플러그인은 빌드 스크립트에 새로운 태스크를 추가하거나, 기존 태스크의 동작을 변경합니다.
plugin을 등록하면 해당 플러그인에 포함된 수많은 Task들이 Gradle 파일로 들어온다.

6.2. 플러그인 설치 및 적용하기

Gradle 플러그인은 build.gradle 파일의 plugins 블록에서 선언하고 적용합니다. 선언된 플러그인은 Gradle이 자동으로 다운로드하고, 빌드 스크립트에 적용합니다.
plugins { id 'org.springframework.boot' version '2.5.1' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id "com.ewerk.gradle.plugins.querydsl" version "1.0.10" id "org.asciidoctor.convert" version "1.5.9.2" id 'java' }

7. Gradle이 빌드 속도가 빠른 이유

점진적 빌드(Incremental Build)

  • 이미 빌드된 파일들을 모두 다시 빌드하는 것이 아닌 바뀐 파일들만 빌드
  • gradle은 빌드 실행 중 마지막 빌드 호출 이후에 task의 입력, 출력 혹은 구현이 변경됬는지 확인한다.
  • 최신 상태로 간주하지 않는다면 빌드는 실행되지 않는다.
  • 예를 들어 Kotlin 파일이 20개 있는 상태에서 컴파일이 완료되었으면, 이후 파일들 중 5개만 바뀌었을 경우 20개 모두가 아니라 5개만 다시 컴파일 시킨다

Build Cache

  • 두 개 이상의 빌드가 돌아가고, 하나의 빌드에서 사용되는 파일들이 다른 빌드들에 사용된다면 Gradle은 빌드 캐시를 이용햇 이전 빌드의 결과물을 다른 빌드에서 사용할 수 있다.
  • 다시 빌드하지 않아도 되므로 빌드 시간이 줄어들게 된다.

Daemon Process

  • Gradle은 데몬 프로세스를 지원, Gradle의 데몬 프로세스는 메모리 상에 빌드 결과물을 보관한다.
    • 데몬 프로세스는 서비스의 요청에 응답하기 위해 오래 동안 살아있는 프로세스
  • 한 번 빌드된 프로젝트는 다음 빌드에서 매우 적은 시간만 소요된다. 실제로 안드로이드의 경우 프로젝트가 복잡해지면 처음 빌드하는데 긴 시간이 걸리는데, 둘 째 빌드부터는 매우 적은 시간이 소모된다.
  • 다음 명령어로 데몬 프로세스를 수행하거나 수행하지 않도록 할 수 있다.
gradle build --daemon gradle build --no-daemon

8. 결론: Gradle을 효과적으로 사용하는 방법

Gradle을 효과적으로 사용하기 위해 몇 가지 팁과 추가 리소스를 제공합니다.

8.1. Gradle Best Practices

코드의 중복을피하고, 명확하고 가독성 있는 빌드 스크립트를 작성하는 것이 중요합니다. 또한, 항상 최신 버전의 Gradle을 사용하고, 필요한 의존성만 선언하는 것이 좋습니다.

8.2. 추가 리소스

Gradle에 대해 더 배우고 싶다면, 공식 Gradle 문서를 참조하거나, 각종 커뮤니티와 포럼을 활용해보세요. 또한, 실제 프로젝트의 빌드 스크립트를 살펴보는 것도 많은 도움이 될 것입니다.

이렇게 Gradle에 대한 기본적인 지식과 사용 방법을 알아보았습니다. Gradle은 강력하고 유연한 빌드 도구이므로, 이를 잘 활용하면 프로젝트의 빌드와 배포 과정을 훨씬 효율적으로 관리할 수 있습니다.
 

참고자료