그레이들 기초

그레이들

그루비와 그레이들

그루비 는 자바 가상머신에서 동작하는 오픈 소스 스크립트 언어이다. 그루비는 자바 문법을 더욱 쉽게 쓰기 위해 스크립트 언어와 비슷한 문법으로 되어 있어서, 대부분의 자바 프로그래머는 자바 코드를 작성하는 느낌으로 그루비를 작성할 수 있다. 동적 언어이며 자바와 달리 작성한 스크립트를 컴파일할 필요 없이 직접 실행할 수도 있다.

또한, 일부 자바 프레임워크에서는 그루비를 지원한다. 대표적인 스프링 프레임워크에서도 그루비를 이용할 수 있다.

이처럼 자바와 거의 같고, 스크립트 언어처럼 부담 없이 작성해서 바로 실행할 수 있는 특징을 고려하면, 그루비를 사용해서 자바 빌드 도구를 만들려는 생각은 자연스럽다.

그루비의 이러한 이점을 최대한 활용해서 개발한 빌드 도구가 그레이들 이다.

그레이들이란

그레이들은 그루비를 사용한 빌드 도구이다. 메이븐은 XML을 이용하여 빌드 정보를 기술했는데, 그레이들은 그루비를 이용해 빌드 정보를 기술하기 한다. 때문에 자바 프로그래머가 좀 더 쉽게 다룰 수 있다.

그레이들의 특징은 다음과 같다.

  • 유연한 언어로 기술
    그루비라는 프로그래밍 언어를 사용해서 기술하기 때문에 유연하게 각종 처리를 수행할 수 있다. 또한 기술하는 내용을 분할하거나 구조화하는 것도 간단하다.
  • 태스크로 처리
    그레이들은 ‘태스크’라는 개념을 이용해 프로그램을 작성한다. 다양한 용도별로 태스크를 만들어서 그 안에 처리를 기술한다.
  • 자바/그루비/스칼라 기본 지원 + 알파
    그레이들은 자바 가상 머신에서 동작하는 언어를 중심으로 지원한다. (별도의 네이티브 코드 플러그인을 사용하면 C/C++ 등, 다른 언어에도 대응할 수 있다.)
  • 각종 도구와 통합
    여러 도구들(앤트, 아파치 아이비 등)과 통합되어 처리를 실행할 수 있다. 또한, 메이븐의 pom.xml을 그레이들용으로 변환하는 도구도 있다.
  • 메이븐 중앙 저장소 대응
    그레이들에서는 메이븐 중앙 저장소를 지원하기 때문에, 중앙 저장소에 있는 라이브러리 모두 그대로 이용 가능하다.

그레이들 사용하기

그레이들 소프트웨어에는 그루비가 포함되어 있기 때문에, 단지 그레이들을 사용하는 용도라면 그루비를 설치할 필요는 없다. (그레이들을 설치하는 것만으로는 그루비 언어를 이용해서 프로그래밍할 수는 없다.)

그레이들 프로젝트 생성

그레이들 명령어를 사용해 프로젝트를 생성할 수 있다.

1
2
3
mkdir gradle-app
cd gradle-app
gradle init --type java-library

gradle init

그레이들 명령어는 gradle OO 형태로 실행한다. 위에서 프로젝트를 생성하며 사용한 init은 그레이들에서 '태스크' 라고 부른다. 이 init은 프로젝트의 기본적은 파일과 폴더를 생성한다.

–type은 생성할 프로젝트의 타입을 지정하는 옵션인데, java-library는 자바 프로젝트임을 나타낸다. 지정한 언어의 샘플 코드를 생성한다. 이 옵션을 생략하면 그레이들 프로젝트의 기본적인 파일들만 생성된다.

그레이들 실행과 태스크

그레이들에서는 다양한 처리를 위해 ‘태스크’를 이용한다. 태스크란, 실행할 처리를 모아놓은 단위로 그레이들에서 처음부터 포함된 것도 있고, 프로그래머가 작성할 수도 있다. 이 태스크를 실행하는 것이 그레이들에서 빌드를 관리하는 기본적인 방법이다. 프로젝트를 컴파일하거나, 실행하는 모든 처리에 태스크를 이용한다.

생성된 gradle-app 폴더 내부의 구조를 살펴보자.

  • .gradle 폴더 : 태스크로 생성된 파일 등을 보존한다.
  • gradle 폴더 : 기본값으로는 그레이들 환경을 모아놓은 wrapper 파일이라고 하는 파일들이 들어 있다.
  • src 폴더 : 소스 코드 관련 파일을 이곳에 작성한다.
  • build.gradle : 그레이들 빌드 파일, 이곳에 프로젝트의 빌드 내용을 기술한다.
  • settings.gradle : 빌드 설정 정보를 기술한 파일. 빌드를 실행하기 전에 읽히기 때문에, 필요한 라이브러리를 읽는 등의 기술을 할 수 있다.

이번에는 생성된 파일의 코드를 확인해보자.

build.gradle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 자바 프로그램을 빌드할 경우에는 java 플러그인을 로드한다.
apply plugin: 'java'

// 저장소 정보를 관리하는 프로퍼티이다. 이곳에서 저장소를 설정할 수 있다. (로컬 환경이나 원격 저장소 기술)
// jcenter는 그레이들에서 중앙 저장소로 이용되는 저장소이다.
// 메이븐 중앙 저장소는 mavenCentral 메서드를 이용해서 사용할 수 있다.
repositories {
jcenter()
}

// 의존성에 관한 설정을 관리하는 프로퍼티이다. 필요한 라이브러리등의 정보를 기술한다.
dependencies {
compile 'org.slf4j:slf4j-api:1.7.21'
testCompile 'junit:junit:4.12'
}

settings.gradle

1
2
// 루트 프로젝트의 이름을 설정한다. 루트 프로젝트는 다수의 프로젝트를 관리할 때 기본이 되는 프로젝트를 가리킨다. 여기에서는 '이 빌드 파일로 빌드할 프로젝트의 이름'이라고 생각하면 된다.
rootProject.name = 'gradle-app'

인텔리제이에서 사용하기

인텔리제이는 표준으로 그레이들을 지원한다. 인텔리제이에서 그레이들용 프로젝트를 생성할 경우, gradle init –type java-library 명령어를 사용하는 경우와 결과가 조금 다르다.

build.gradle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
group 'com.jongmin'
version '1.0-SNAPSHOT'

apply plugin: 'java'

sourceCompatibility = 1.8

repositories {
mavenCentral()
}

dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
}

시작 부분에 그룹 ID와 버전을 지정하는 문장이 포함되어 있다. gradle init으로 작성한 build.gradle에서는 포함되지 않았는데, 그 이유는 메이븐 저장소를 이용하기 때문이다. 메이븐에는 모든 프로그램에 그룹 ID와 아티팩트 ID가 할당되어 있다.

sourceCompatibility는 자바 소스 코드의 버전을 가리킨다.

태스크 실행

메이븐과 마찬가지로 실행할 내용을 ‘Run…’ -> ‘Edit Configurations’ 메뉴에서 컨피그레이션에 설정하면 인텔리제이의 ‘Run’으로 프로그램의 빌드와 실행, 디버그 등의 기능을 수행할 수 있다.

build.gradle

그래이들은 build.gradle에 기술한 코드를 필요에 따라 실행하여 빌드를 실행한다.

그레이들은 ‘그루비를 사용하는 빌드 도구’이다. 하지만 이는 정확한 설명은 아니다. 그레이들은 ‘그루비 그 자체’는 아니고 **’그루비 기반의 DSL(Domain Specific Language)’**이다.

DSL은 ‘도메인 고유 언어’라고 불리는데, 특정한 용도에 한정된 언어를 말한다. 그 언어 그 자체는 아니고, 특정한 용도에 맞게 해당 언어를 기반으로 각색한 것이다. 그레이들에서 사용되는 언어는 ‘그루비를 기반으로 작성된 Gradle DLS’이다.

그레이들은 기본적으로 태스크를 작성하여 실행한다. 태스크는 실행할 처리를 모아서 명령어로서 실행할 수 있게 한 것이다. 다음과 같은 형식으로 정의한다.

1
2
3
task 이름{
...실행할 처리...
}

이렇게 정의한 태스크는 명령행에서 gradle 이름 형태로 실행할 수 있다.

간단한 예제를 작성해 실행해보자. 다음과 같이 build.gradle을 수정한다.

1
2
3
4
5
6
7
8
9
task hello {
doLast {
println();
println("=================");
println("Welcome to Gradle!");
println("=================");
println();
}
}

수정한 후, 명령행에서 다음과 같이 실행한다.

1
gradle hello

결과는 다음과 같다.

1
2
3
4
5
6
7
8
> Task :hello

=================
Welcome to Gradle!
=================

BUILD SUCCESSFUL in 2s
1 actionable task: 1 executed

그레이들은 기본적으로 빌드 도구이지만, 여기에서는 빌드도 컴파일도 하지 않고, 단지 메시지를 표시했다. 태스크는 작성된 처리를 실행할 뿐이지, 반드시 빌드와 관련된 기능이 포함되어야 하는 것은 아니다.

quiet 모드로 실행

이번에는 quiet 모드로 실행해보자.

1
gradle -q hello

결과는 다음과 같다.

1
2
3
=================
Welcome to Gradle!
=================

이렇게 하면 hello에서 println한 내용만 출력되며, 그 이외의 내용은 표시되지 않는다. -q 옵션은 태스크를 quiet 모드로 실행해 예외 발생 등 중요한 문제 이외의 표시가 제한된다. 태스크의 실행 결과만 알고 싶을 때 편리한 옵션이다.

액션 리스트

태스크는 다양한 ‘액션’을 내부에 가지고 있다. 태스크를 실행하면 준비된 액션이 순서대로 실행된다. 이 액션을 관리하는 것이 ‘액션 리스트’이다. 액션은 어떤 역할을 하는지가 정해져 있다. 필요에 따라 액션에 처리를 추가해 태스크를 조합할 수 있다.

액션 중에서도 doFirstdoLast가 가장 많이 사용된다. 각각 ‘처음에 실행되는 액션’과 ‘마지막에 실행되는 액션’이다. 이들을 이용해 태스크의 처음과 마지막에 처리를 실행할 수 있다.

매개변수 이용

태스크를 실행할 때 어떤 정보를 태스크에 전달하고 싶은 경우도 있다. 이럴 때 매개변수를 이용할 수 있다.

1
2
3
4
5
6
7
8
9
task hello {
doLast {
def total = 0;
for(def i in 1..num.toInteger()) {
total += i;
}
println("total: " + total);
}
}

1에서 num까지 합하는 태스크이다. 여기서 사용되는 num이 매개변수로 전달되는 프로퍼티이다. 이처럼 매개변수에서 전달되는 값을 프로퍼티로 사용할 수 있다. 단, 주의할 점은 값이 String이라는 것이다.

hello 태스크는 다음과 같이 실행한다.

1
gradle hello -q -Pnum=100

이처럼 태스크를 실행하는 동안에는 -P프로퍼티=값 형태로 특정 변수에 값을 전달할 수 있다.

동적 태스크 실행

스크립트를 사용해서 동적으로 태스크를 생성할 수도 있다.

1
2
3
4
5
6
7
8
def arr = ["one", "two", "three"];
arr.each {s ->
task "$s" {
doLast {
println("this is $s task.");
}
}
}

다음과 같이 실행한다.

1
gradle -q one

java 플러그인 사용하기

자바로 개발할 때 필요한 기본적인 기능은 ‘java’ 플러그인에 포함되어 있다. build.gradle에서 로드하여 이용한다.

1
apply plugin: 'java'

다음의 명령어로 빌드한다.

1
gradle java

위 명령어를 실행하면 프로젝트가 컴파일되고 JAR 파일이 생성된다. 컴파일로 생성된 파일들은 build 폴더에 보관된다. 이 안의 libs 폴더 안에 gradle-app.jar 파일이 생성된다.

build 폴더 안에는 classes 폴더도 있는데, 여기에는 컴파일된 클래스 파일이 보관된다. gradle java에서는 우선 소스 코드를 컴파일하여 클래스 파일을 작성한 후, 이를 모아서 JAR 파일로 만드는 일련의 처리가 자동으로 수행된다.

gradle java와 gradle build

grade java는 자바 프로그램의 빌드를 수행하지만, gradle build는 어떤 언어로 작성된 프로젝트라도 빌드한다. (그레이들은 자바 이외에도 그루비나 스칼라 등 많은 언어를 지원하고 각각의 언어에서 빌드를 수행하는 플러그인을 제공한다.)

java 플러그인의 태스크

java 플러그인에는 이 외에도 몇 가지 태스크가 더 있다.

  • java
    자바 소스 코드를 컴파일하고 그 외에 필요한 리소스 파일들을 모아서 JAR 파일을 생성한다. 프로그램을 배포할 때 이 태스크로 JAR 파일을 만들면 유용하다. 단, 이 java 태스크로 생성된 JAR 파일은 Executable이 아니라는 점에 주의해야 한다.
  • compileJava
    자바 소스 코드를 모두 컴파일한다. 보존할 장소(build 안의 classes 폴더)가 없다면 폴더를 자동으로 생성하고 그 안에 클래스 파일을 작성한다.
  • processResources
    리소스 파일을 클래스 디렉터리(classes 폴더) 안에 복사한다.
  • classes
    소스 코드 컴파일과 리소스 파일 복사를 실행한다. compileJava와 processResources가 합쳐진 것이라 생각해도 된다.
  • test
    프로그램 테스트를 실행한다. 소스 코드와 관련된 컴파일을 수행하고 테스트에 필요한 리소스 복사 등을 수행한 뒤 JUnit으로 테스트를 실행한다. JUnit 라이브러리를 이용할 수 없는 상태에서는 테스트를 실행할 때 오류가 발생한다.
  • jar
    프로그램을 컴파일하고 리소스 파일 등을 준비한 뒤, JAR 파일로 패키징한다. 단, 파일을 단순히 JAR 파일에 모을 뿐이며 Executable jar을 작성하는 것은 아니다.
  • javadoc
    소스 코드를 해석하여 Javadoc 파일을 생성한다. build 안의 docs 폴더 안에 javadoc 폴더를 작성하여 파일을 보관한다.
  • clean
    빌드로 생성된 파일을 모두 삭제한다.

java 플러그인의 태스크 이용하기

1
2
3
4
5
task doit(dependsOn: [compileJava, jar]) {
doLast {
println "*** compiled and created jar! ***"
}
}

doit 태스크에는 매개변수가 포함되어 있다. dependsOn은 이 매개변수가 ‘의존성’을 지정하는 것을 가리킨다. ‘compileJava, jar’은 2개의 태스크를 모아놓은 배열이다. 즉, doit이 compileJava와 jar이라는 2개의 태스크에 의존한다.

이 상태에서 dependsOn에서 태스크를 지정하면 그 태스크가 실행되기 전에 의존하는 모든 태스크가 실행된다. 그리고 의존하는 태스크의 실행이 완료된 후에 doit 태스크의 doLast가 호출된다.

의존성을 지정해서 실행하는 방법 이외에 태스크를 직접 실행할 수도 있는데, 태스크의 execute 메서드를 호출하면 된다.

1
2
3
4
5
6
7
8
task doit {
doLast {
println "*** compiled now! ***"
tasks.compileJava.execute()
println "*** create jar! ***"
tasks.jar.execute()
}
}

실행하면 의존성을 지정해서 실행할 때와 같은 결과를 얻지만, “Deprecated Gradle features were used in this build, making it incompatible with Gradle 5.0.” 라는 메시지가 출력된다. 그레이들 버전 5.0 부터는 execute 메서드를 직접 호출하는 것이 호환되지 않기 때문이다.

application 플러그인

java 플러그인에는 프로그램을 실행하는 태스크가 없어 application 플러그인을 사용해 애플리케이션을 실행해야 한다.

1
apply plugin: 'application"

build.gradle에 이처럼 작성하고 그 다음에 mainClassName 프로퍼티에 메인 클래스(실행할 클래스)를 설정한다.

1
mainClassName = "com.jongmin.gradle.App"

application 플러그인에는 run 태스크가 포함되어 있다. 이것을 실행하면 mainClassName에 지정된 클래스가 실행된다.

1
gradle run

다음과 같이 작성하면 jar을 실행한 후 run으로 클래스를 실행할 수 있다.

1
2
3
4
5
6
7
8
9
10
apply plugin: 'java'
apply plugin: 'application'

mainClassName = "com.jongmin.gradle.App"

task doit(dependsOn:[jar, run]) {
doLast {
println("*** do it! ***")
}
}
Author

KimJongMin

Posted on

2018-08-08

Updated on

2021-03-22

Licensed under

댓글