Gradle Plugin로 Github Actions CI에 적용하기

Gradle 공식문서를 읽어가며 학습하며 플러그인의 종류가 정말 다양하다는 것을 알게 되었다.

 

https://docs.gradle.org/current/userguide/plugin_reference.html#plugin_reference

 

해당 문서는 gradle에 내장되어있는 plugin을 소개하는 것으로 정말 다양한 플러그인이 존재한다는 것을 확인해 볼 수 있다.

 

이번 글에서는 Gradle에서 제공해주는 Plugin들을 이용해서 코드 포맷팅, 코드 정적 분석, 테스트 커버리지를 확인할 수 있게 설정하고 이를 Github Actions을 통해 CI에 통합시키는 실습을 진행해보려고 합니다.

 

 

환경은 다음 명령어로 생성된 디렉토리에서 진행하였습니다.

gradle init --type java-application --dsl kotlin

 

1. Plugin 설정하기

 

Plugin은 build.gradle.kts에 추가했다. 이번 실습에서 사용할 플러그인은 Jacoco, Spotless, SpotBugs이다.

각각 테스트 커버리지, 코드 포맷팅, 코드 정적 분석을 진행할 수 있을 것으로 설정 파일은 다음과 같다.

 

import com.github.spotbugs.snom.SpotBugsTask

/*
 * This file was generated by the Gradle 'init' task.
 *
 * This generated file contains a sample Java application project to get you started.
 * For more details on building Java & JVM projects, please refer to https://docs.gradle.org/8.14/userguide/building_java_projects.html in the Gradle documentation.
 */

plugins {
    // Apply the application plugin to add support for building a CLI application in Java.
    application
    id("com.diffplug.spotless") version "7.0.3"
    jacoco
    id("com.github.spotbugs") version "6.1.10"
}

repositories {
    // Use Maven Central for resolving dependencies.
    mavenCentral()
}

spotless{
    java {
        googleJavaFormat("1.20.0")
            .aosp()
        target("src/**/*.java")

        importOrder("java|javax", "org", "com.mycorp", "", "\\#")
        removeUnusedImports()

        trimTrailingWhitespace()
        endWithNewline()
    }
}

jacoco {
    toolVersion = "0.8.13"
}

spotbugs {
    toolVersion.set("4.9.3")
    effort.set(com.github.spotbugs.snom.Effort.MAX)
    reportLevel.set(com.github.spotbugs.snom.Confidence.MEDIUM)
}

dependencies {
    // Use JUnit Jupiter for testing.
    testImplementation(libs.junit.jupiter)

    testRuntimeOnly("org.junit.platform:junit-platform-launcher")

    // This dependency is used by the application.
    implementation(libs.guava)
    testImplementation("org.assertj:assertj-core:3.27.3")
}

// Apply a specific Java toolchain to ease working on different environments.
java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}

application {
    // Define the main class for the application.
    mainClass = "org.example.App"
}

tasks.named<Test>("test") {
    // Use JUnit Platform for unit tests.
    useJUnitPlatform()
    finalizedBy(tasks.jacocoTestReport)
}

tasks.jacocoTestReport {
    dependsOn(tasks.test)
    reports {
        html.required.set(true)
        xml.required.set(true)
    }
}

tasks.named<SpotBugsTask>("spotbugsMain") {

    // 필요 시 커스텀 클래스 경로 지정
    // classes.setFrom(sourceSets.main.get().output)

    reports.apply {
        /* HTML */
        create("html") {
            required.set(true)
            outputLocation.set(
                layout.buildDirectory.file("reports/spotbugs/${name}.html")
            )
            setStylesheet("fancy-hist.xsl")
        }

        /* XML — SonarQube, CI 파싱용 */
        create("xml") {
            required.set(true)
        }

        /* SARIF — GitHub Code Scanning 용 (필요 없으면 true→false) */
        create("sarif") {
            required.set(true)
        }
    }
}

tasks.check { dependsOn("spotbugsMain") }

 

먼저 plugins 블록안에 추가할 plugin을 명시하고 관습 속성과 task 정의를 통해 gradle 명령어로 각 기능을 사용할 수 있도록 설정하였다.

 

jacoco나 spotBugs같은 경우 커스텀 설정할 요소가 많이 없어 직접 매뉴얼을 찾아보고 적용해보시면 좋을거 같습니다.

spotless같은 경우 저는 한줄에 120자 제한으로 설정하고 싶어 reflowLongStrings() 옵션을 설정하지 않았고 indent는 space는 4자리로 진행하고 싶어 aosp() 옵션을 주었습니다. 

 

spotless 같은 경우 저처럼 원하는 코드 포맷팅에 맞게 설정을 진행해 주면 됩니다.

 

위 설정을 마치고 직접 테스트를 해보자.

 

일부러 코드 포맷팅을 틀리게 해서 체크했을 때, 위와 같이 task가 실패하는것을 볼 수 있다.

./gradlew :app:spotlessApply

 

이때, 위 명령어를 사용하면 plugin으로 설정해놓은 코드 포맷팅에 맞게 코드가 수정되는 것을 확인해 볼 수 있다.

(import 위치를 다르게 해서 테스트 진행)

 

 

다음은 코드 정적 분석에서 문제를 찾았을 때 다음과 같이 오류가 뜨고 직접 html파일을 통해 코드에 어떤 문제가 있는지 확인할 수 있다.

 

 

아래 옵션으로 코드 정적 분석의 강도를 조절해줄 수 있다.

    effort.set(com.github.spotbugs.snom.Effort.MAX)
    reportLevel.set(com.github.spotbugs.snom.Confidence.MEDIUM)

 

 

테스트 커버리지의 경우도 아래와 같이 명령어를 입력하면 html 문서로 커버리지의 현황을 확인해 볼 수 있습니다.

 

 

 

2. Github Actions로 CI에 적용하기

 

위 플러그인을 적용해서 local에서 확인하는 것도 좋지만 코드를 모두 작성하고 원격 저장소에 pr을 날릴 때, 자동으로 검사를 수행하도록 설정할 수 있습니다. 예를 들어 그냥 PR을 날려서 합치게 되면 실수로 코드 포맷팅을 지키지 않았거나 테스트 코드를 빠트리고 추가했을 경우 해당 코드의 품질은 더이상 보장할 수 없게 됩니다.

 

이때, 코드 품질을 잘 관리하고 있다는 것을 보여주고 또 실수로 빠트렸던 부분을 자동으로 체크해보기 위해 Github Acitons를 이용해 볼 수 있다.

 

https://github.com/HeeChanN/java-play-ground/blob/main/.github/workflows/ci.yml

 

다음과 같이 저는 CI 파이프라인을 작성하였습니다.

gradle wrapper를 이용하여 ./gradlew <task> 명으로 손쉽게 코드의 품질을 검증하는 파이프라인을 작성할 수 있습니다. 실제 위 파이프라인이 돌아갔을 때 PR에는 다음과 같은 comment가 남게 됩니다.

 

이처럼 PR을 main브랜치나 develop브랜치에 합치기 전에 코드에 문제가 없는지 검사하는 단계를 추가하여 코드의 품질을 높일 수 있다고 생각합니다.

 

 

3. 마치며

이번 CI 파이프라인을 구축하면서 대학교 3학년 때 Jenkins로 처음 작성했던 파이프라인이 떠올랐다. 당시에는 CI/CD’보다는 단순한 배포 자동화에 초점을 맞췄지만, 실제 CI 단계에는 훨씬 다양한 검증 절차가 포함될 수 있다는 점을 이번에 체감했다.

 

이번 기회에 Gradle의 다양한 Plugin을 활용해 단위 테스트 실행, 코드 포매팅 검사, 정적 분석, 커버리지 측정을 하나의 파이프라인으로 통합함으로써 품질 관리가 체계화되었고, 그 결과 코드 품질이 눈에 띄게 향상되는 과정을 직접 확인할 수 있었다.

또한, 파이프라인이 자동으로 댓글을 남겨 현재 상태를 기록 및 보관해 주기에 품질 히스토리까지 손쉽게 얻을 수 있었다.

 

다음 Gradle 관련 글에서는 멀티 모듈 프로젝트를 직접 실습해 보고, 이를 제가 진행 중인 프로젝트에도 적용할 수 있을지 점검해 볼 계획이다. Gradle을 공부할수록 더 깊은 영역이 끝없이 펼쳐지는 느낌이라, 배우는 내내 ‘아직 갈 길이 멀구나’ 하는 겸손함이 절로 생기는 것 같습니다. 😊

'Gradle' 카테고리의 다른 글

Gradle 빌드 툴 학습  (0) 2025.05.01