티스토리 뷰

조각글

Gradle

빅또리 2023. 10. 8. 20:43

jvm 기반 언어 빌드 자동화 tool

다른 선택지로는 maven, ant 정도가 있다. (이거 두개는 xml 기반이라 점점 안쓰는 추세)

  • 편한 의존성 버전 관리
  • 간단한 명령어 한개만 치면 효율적으로(캐싱), 언제나 같은 결과물을 보장하며 deployable unit을 만들어줌 (jar 파일)
    • gradle build
  • 테스트, lint 체크 등 부가적 기능도 제공
    • gradle test
    • gradle check
     

 

사용하는 이유

jdk안에 javac 라는 컴파일러가 들어있기는 하지만

빌드 자동화 툴 없이 쓴다면...

작성한 .java 파일 => .class 파일로 컴파일 => 여러개의 .class 파일 및 dependency 파일 모아서 .jar 파일 생성

이렇게 수동으로 해야함 너무 복잡하고 중복되는 커맨드가 많을 것이다..

더보기

.class 파일 => .java 파일 한개가 컴파일된 것

.jar 파일 => 여러개의 .class 파일 및 dependency들을 하나로 묶어 패키징 한 것.

(rails는 배포할 도커 이미지에 (1) 어플리케이션 코드 복사 + 젬락에 명시된 (2) 젬파일도 전부 다운로드 해야해서 무거운데, 

스프링부트는 .jar 파일 안에 컴파일된 어플리케이션 코드 뿐만 아니라 dependecy도 모두 포함되어 있어서 더 경량이고 dockerfile 작성도 간단할 듯)

 

 

 

이 파일/디렉토리들은 뭘까?

  • build : 빌드된 결과물 (class, jar 파일 및 테스트 결과)
  • gradle : gradle wrapper 버전 설정 등 config 파일 (gradle-wrapper.properties)
  • src : gradle은 자바 소스코드가 src/main/java 디렉토리 아래에 있기를 기대함.
  • build.gradle :  빌드 스크립트. (maven의 pom.xml) 
    • build.gradle : 스크립트 언어 groovy
    • build.gradle.kts : 스크립트 언어 kotlin
  • gradlew : for mac, linux. gradle wrapper 스크립트.
  • gradlew.bat : for window
  • settings.gradle : high-level 프로젝트 config 설정 (rootProject.name)

 

 

 

gradlew (gradle wrapper)

  • gradlew 스크립트 사용하면 gradle 미리 설치되어 있지 않아도 스크립트가 설치해서 실행해줌.

(bundle exec, npx 랑 좀 비슷한가..? system-wide gem 말고 프로젝트 Gemfile.lock에 명시된 버전의 젬으로 실행)

  • 팀 내 프로젝트를 진행할 때 같은 gradle 버전으로 맞추고, git에서 버전관리도 할 수 있어서. 개발 구성원간 일관된 로컬 개발환경 마련하는데 도움을 줌.

전역으로 설치된 gradle 버전 확인 (brew나 sdkman으로 설치했을)

gradle --version

## sdkman을 사용한다면
sdk current gradle

 

(프로젝트별) gradlew의 gradle 버전

# 버전 확인
./gradlew --version


# 버전 바꾸기
# gradle/wrapper/gradle-wrapper.properties 파일 내용이 변경됨
# distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
./gradlew wrapper --gradle-version 8.4

 

 

 

 

gradle basic concepts

 

project

  • 빌드 수행 단위.
  • multi project 일 경우 빌드를 실행하면 sub project 별로 build 디렉토리가 생성됨.

 

task

  • 빌드 프로세스에서 수행되는 단위 작업. unit of work (테스트, 컴파일, jar 파일 생성 등...)
  • ./gradlew (task name) 으로 cli 에서 실행시킬 수 있음.
  • 여러 task가 dependency graph 형태로 연결되어 있음. (gradle build는 내부적으로 assemble과 check task를 실행하는 것)

java 플러그인 apply 하고 ./gradlew build 했을 때 실행되는 하위 태스트들

# 신규 task 등록하기 => register
# 이미 있는 task 추가 수정하기 => named

tasks.register('hello') {
    doLast {
        println 'original do Last'
    }
}
tasks.named('hello') {
    doLast {
        println 'add do last 1'
    }
}
tasks.named('hello') {
    doLast {
        println 'add do last 2'
    }
}
tasks.named('hello') {
    doFirst {
        println 'add do first 1'
    }
}

# > Task :hello
# add do first 1
# original do Last
# add do last 1
# add do last 2

# BUILD SUCCESSFUL in 451ms
# 1 actionable task: 1 executed

 

plugin

  • task 묶음
  • 사용자가 직접 task를 정의할 필요 없게 플러그인으로 제공됨. 높은 재사용성
  • 예를 들어 java 플러그인은 java 개발을 위한 task 모음
    • compileJava (.java -> .class 파일로 컴파일)
    • processResources (resources 디렉토리 아래에 있는 파일들 build 디렉토리로 복사)
    • jar (jar 파일로 패키징. <project name>-<version>.jar )
    • test

( compileJava + processResources => classes 태스크 )

 

 

 

 

gradle 주요 command

empty gradle project 생성하기

gradle init

실행할 수 있는 task 목록

./gradlew tasks

빌드

  • build 디렉토리 아래에 빌드 결과물 생김
    • classes 디렉토리 : 아래에 src 디렉토리 구조 그대로 컴파일된 .class 파일 생성
    • libs 디렉토리 : jar 파일
# check + assesmble
./gradlew build

build 디렉토리 삭제

./gradlew clean

springboot 실행

./gradlew bootRun

 

 

 

build script syntax

  • 어플리케이션 코드 처럼 그냥 프로그램임. 커스텀 함수를 정의해서 사용할 수도 있음.
  • build script 작성하는데 선택할 수 있는 언어
    • groovy
    • kotlin

 

plugins

plugins {
    id 'java'               # core gradle plugins
    id 'com.example.hello' version '1.0.0' apply false
    id 'com.example.goodbye' version '1.0.0' apply false
}

subprojects {
    if (name.startsWith('hello')) {
        apply plugin: 'com.example.hello'
    }
}

# plugins bracket 바깥에서 apply 하려면
apply plugin: 'org.springframework.boot'

3rd party 플러그인일 경우 version 명시 해줘야함.

multi project 일 경우 root에 apply false로 설정한 다음에 필요한 subproject 에서만 apply 해서 사용하도록 할 수 있음.

 

 

build metadata

group = 'com.example'
version = '0.0.1-SNAPSHOT'

 

repositories

  • dependency 다운로드 하는 저장소. 주로 mavenCentral. (Ivy, Google 이런 것도 있는 듯)

 

dependencies

  • gradle이 다운로드해서 사용하는 jar 파일
  • Group, name, version으로 구분.
  • 의존성 구성 키워드
    • implementations : 직접 참조
    • api : 모든 하위 프로젝트와 의존성 공유. 전파됨
    • compileOnly : 컴파일 시에만 필요한 의존성이다.
    • runtimeOnly : 런타임에만 필요한 의존성이다.
    • testImplementation / testCompileOnly : 테스트 코드에서만 사용되는 의존성이다.
     

implementation vs api

  • moduleX 에서 LibraryC 에 있는 메소드 직접 참조 불가능. (implementation 키워드로 의존성 설정)
  • moduleX에서 LibraryD 에 있는 메소드 참조 가능. (api 키워드로 의존성 설정)
  • LibraryC 코드가 수정되면 LibraryA (LibraryC를 직접적으로 참조하고 있는 모듈만), LibraryC만 다시 컴파일되면 됨.
  • LibraryD 코드가 수정된다면 LibraryB, ModuleX 코드도 전부 다시 컴파일 되는 것이 맞음.

 

task 수정/설정

# task, plugin 수정/설정
#
# <task/plugin 이름> {
#    // 설정
# }


# ./gradlew test는 기본적으로 junit4를 사용함...
# junit5를 사용할 수 있도록 test task 수정하는 코드

test {
   useJUnitPlatform()
}

 

변수 / 프로퍼티

(kotlin 기준으로. groovy는 val 키워드 없이 쓰면 되는 듯)

변수 선언 및 사용

// 변수 선언
val projectName = "my-project"
val projectVersion = "1.0.0"

// 변수 사용
println("Project Name: $projectName")
println("Project Version: $projectVersion")

프로퍼티 설정

### 1. gradle.properties에 값
kotlinVersion=1.7.0
springBootVersion=2.6.0
springDependencyManagementVersion=1.1.2

# settings.gradle 에서 변수에 값 할당
val kotlinVersion: String by settings
val springBootVersion: String by settings
val springDependencyManagementVersion: String by settings

# build.gradle 에서 변수에 값 할당
val projectGroup: String by project
val applicationVersion: String by project


### 2. ext 블럭 정의한 프로퍼티
ext {
    kotlinVersion = "1.7.0"         
    springBootVersion = "2.6.0"
    springDependencyManagementVersion = "1.1.2"
}

task printProperties {
    doLast {
        println "myProperty: $myProperty"
        println "apiUrl: $apiUrl"
        println "versionCode: $versionCode"
    }
}

 

 

multi project build

root 프로젝트 1개와 여러개의 sub project들로 구성.

├── .gradle
│   └── ⋮
├── gradle
│   └── wrapper
├── gradlew
├── gradlew.bat
├── settings.gradle  (1)
├── subproject-one
│   └── build.gradle  (2) 
└── subproject-two
    └── build.gradle  (2)
    

# (1) : root 프로젝트 settings.gradle 파일은 모든 sub 프로젝트에 대한 정보를 담고 있어야 함.
# (2) : 모든 sub 프로젝트는 각자의 build.gradle 파일을 포함해야 함.

 

allProjects 블록 => 모든 프로젝트에 대한 설정을 일괄적으로 적용.

subProjects 블록 => 현재 프로젝트의 하위 프로젝트에 대한 설정을 정의.

그냥 외부 블록 => 현재 프로젝트에만 적용.

 

 

gradle lifecycle

1. initialization

  • settings.gradle 파일을 읽어 멀티 프로젝트 구성을 초기화
# settings.gradle

rootProject.name = 'project-name'
include 'sub-proj1', 'sub-proj2', 'sub-proj3'

2. configuration

  • 빌드 스크립트 (build.gradle) 실행해서 태스크 그래프 구성.
  • root project 먼저 -> 하위 프로젝트 순으로 build.gradle 읽고 config 하는 것 실행됨. (breadth-frist ordering)
# 자식 cofig 먼저 실행하고 싶으면
# 부모 gradle.build 에다가
evaluationDependsOnchildren()

3. execution : 태스크 (의존성 순서대로) 실행

 

 

 

* root 프로젝트 말고 특정 하위 프로젝트에서 task 실행하려면 

./gradlew :common:clean

 

 

References

multi project build 관련 공식문서 : https://docs.gradle.org/current/userguide/intro_multi_project_builds.html

implementation vs api : https://bluayer.com/13

'조각글' 카테고리의 다른 글

SDKMAN  (0) 2023.10.08
golang 모듈  (0) 2023.04.05
devops 따라하기 시리즈  (0) 2022.03.22
영역함수  (0) 2021.11.05
git 빠르게 시작하기 2 (내부 구조)  (0) 2021.02.26
댓글
공지사항
최근에 올라온 글