JBake와 JGit을 사용한 구성 가능한 Kotlin Gradle 플러그인 TDD
게시: 09 July 2025
Sommaire
이 글은 Kotlin DSL로 작성된 Gradle 플러그인의 설계 및 테스트(TDD) 전체 과정을 소개합니다. 이 플러그인은 *JBake*를 사용한 정적 사이트 생성과 *JGit*을 통한 *Git 배포*를 자동화하며, 모든 것이 구성 YAML 파일에서 시작됩니다.
목표
Gradle 플러그인 만들기 :
-
Kotlin DSL에서 설정 가능;
-
할 수 있는 :
-
JBake에서 정적 사이트 생성 ;
-
원격 Git 저장소에 JGit을 통해 배포 ;
-
CI/CD 워크플로에 통합 가능한 ;
-
TDD 방법론에 따라 테스트된
선언적 구성
다음은 우리 DSL에서 모델링할 YAML 구성의 예시 :
bake:
srcPath: "./site/jbake"
destDirPath: "bake"
pushPage:
from: "bake"
to: "cvs"
repo:
name: "trainings"
repository: "https://github.com/pages-content/trainings.git"
credentials:
username: "USERNAME"
password: "SECRET_TOKEN"
branch: "main"
message: "cheroliv.com"
이 구성은 Kotlin DSL에서 선언 가능한 Gradle 확장을 통해 표현됩니다.
플러그인 아키텍처
@startuml
package "엔진-플러그인" {
class WorkspaceEngineExtension
class GenerateSiteTask
interface GitAdapter
class JGitAdapter
WorkspaceEnginePlugin --> WorkspaceEngineExtension
WorkspaceEnginePlugin --> GenerateSiteTask
GenerateSiteTask --> GitAdapter
JGitAdapter ..|> GitAdapter
}
@enduml
DSL 확장 정의
open class WorkspaceEngineExtension {
var outputDir: String = "build/site"
var template: String = "freemarker"
var repoUrl: String = ""
var branch: String = "main"
var message: String = "Generated by engine"
}
사용자 지정 작업의 정의
abstract class GenerateSiteTask : DefaultTask() {
lateinit var gitAdapter: GitAdapter
@get:Input abstract val repoUrl: Property<String>
@get:InputDirectory abstract val outputDir: DirectoryProperty
@get:Input abstract val branch: Property<String>
@get:Input abstract val message: Property<String>
@TaskAction
fun run() {
val repo = gitAdapter.cloneRepository(repoUrl.get(), outputDir.get().asFile)
gitAdapter.commitAndPush(repo, branch.get(), message.get())
}
}
인터페이스 GitAdapter
interface GitAdapter {
fun cloneRepository(uri: String, directory: File): Repository
fun commitAndPush(repo: Repository, branch: String, message: String)
}
JGit을 사용한 구체적인 구현
class JGitAdapter : GitAdapter {
override fun cloneRepository(uri: String, directory: File): Repository {
return Git.cloneRepository()
.setURI(uri)
.setDirectory(directory)
.call()
.repository
}
override fun commitAndPush(repo: Repository, branch: String, message: String) {
Git(repo).use {
it.add().addFilepattern(".").call()
it.commit().setMessage(message).call()
it.push().call()
}
}
}
Kotlin DSL 구성
workspaceEngine {
outputDir = "build/out"
template = "freemarker"
repoUrl = "https://github.com/cheroliv/trainings.git"
branch = "main"
message = "deploy from plugin"
}
Mockito를 사용한 단위 테스트
의존성
testImplementation("org.mockito:mockito-core:5.12.0")
testImplementation("org.mockito.kotlin:mockito-kotlin:5.2.1")
testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")
Git 동작 테스트
class GenerateSiteTaskMockitoTest {
@TempDir
lateinit var tempDir: File
@Test
fun `task should call clone and commitAndPush`() {
val project = ProjectBuilder.builder().build()
val task = project.tasks.create("generateSite", GenerateSiteTask::class.java)
val mockGitAdapter = mock<GitAdapter>()
val mockRepo = mock<Repository>()
whenever(mockGitAdapter.cloneRepository(any(), any())).thenReturn(mockRepo)
task.gitAdapter = mockGitAdapter
task.repoUrl.set("https://github.com/cheroliv/test.git")
task.outputDir.set(project.layout.projectDirectory.dir(tempDir.name))
task.branch.set("main")
task.message.set("test commit")
task.run()
verify(mockGitAdapter).cloneRepository(eq("https://github.com/cheroliv/test.git"), any())
verify(mockGitAdapter).commitAndPush(eq(mockRepo), eq("main"), eq("test commit"))
}
}
@startuml actor "개발자" as Dev participant "Gradle 프로젝트 빌더" as Project participant "생성사이트작업" as Task participant "GitAdapter (모의)" as Adapter participant "JGitAdapter / Mockito" as Impl Dev -> Project : construit projet Gradle Dev -> Task : configure les propriétés Task -> Adapter : cloneRepository() Adapter -> Impl : simulé par Mockito Task -> Adapter : commitAndPush() @enduml
결론
책임의 분리 via`GitAdapter`Gradle 플러그인 개발에서 TDD를 완전히 적용할 수 있도록 해줍니다:
-
Git 호출은 추상화되고 테스트 가능합니다;
-
DSL 구성은 깔끔하고 명확합니다;
-
플러그인은 실제 구현에 대해 중립적이다.
이 접근 방식은 높은 테스트 가능성과 확장성(GitHub API, GitLab 등)을 보장합니다.
다음 단계
-
JBake 생성을 통합하는`BakeAdapter`
-
통합 테스트 추가하기`GradleRunner`
-
자동 파일 로드 지원`site.yml`SnakeYAML을 통해
관련 기사
HyperFrames-Gradle : AsciiDoc을 MP4 비디오로 변환
31 May 2026
31 May 2026
아키텍처 « 독립 플러그인 + 소비자 루트 » : 왜 내 Gradle 빌드가 중복되는가
14 May 2026
14 May 2026