본문 바로가기
Gradle

[개발자의 Gradle] Configurations? 누구냐 너! -1

by 개발자의 2025. 8. 1.

혹시 아래와 같은 구문을 본 적이 있을까?

// groovy DSL
configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}
// kotlin DSL
val compileOnly by configurations
val annotationProcessor by configurations

compileOnly.extendsFrom(annotationProcessor)

둘 중에 뭘로 쓰든 그 의미는 compileOnly라는 configurations 객체가 annotationProcessor라는 configurations 객체를 상속하게 만든다. 즉, annotationProcessor에 적용된 의존성이 전부 compileOnly에도 적용된다는 의미다. 아래와 같이 중복되는 설정을 축약해서 쓸 수 있다.

// extendsFrom 설정이 없을때.
dependencies {
    compileOnly("org.projectlombok:lombok:1.18.30")
    annotationProcessor("org.projectlombok:lombok:1.18.30")
}
// extendsFrom 설정이 있을때.
dependencies {
    annotationProcessor("org.projectlombok:lombok:1.18.30")
}

축약해서 쓰고 싶지 않다면, extendsFrom 설정을 하지 않고, 두 줄짜리 설정을 쓰면 된다.

(축약하지 않고 쓰는 편이 명시적이라고 생각한다.)

Configurations?

서두가 굉장히 길었는데, 하고 싶은 얘기는 따로 있다.

갑자기 Configurations 객체가 어디서 나온 거지??

 

일전에 Gradle Build Phase에 대해서는 아래 3단계를 거친다고 이미 다룬 바 있다.

  1. 초기화 단계 (Initialization Phase)
  2. 구성 단계 (Configuration Phase)
  3. 실행 단계 (Execution Phase)

Gradle은 이중 구성 단계에서 Configurations 객체를 생성한다.

정확하게는 플러그인이 적용되는 시점에, 해당 플러그인이 사용할 가능성이 있는 모든 객체를 만든다. 바꿔 말하면 생성되는 Configurations 객체는 플러그인에 따라서 결정된다.

 

아래 코드를 통해서 java plugin / java-library 플러그인에 따라서 어떤 Configurations 객체가 생성되는지 확인해 보자.

// java plugin
plugins {
    id("java")
}

// java-library plugin
plugins {
    id("java-library")
}

// 결과 로깅.
configurations.forEach { config ->
    println("▶ Configuration: ${config.name}")
}

먼저 java 플러그인 결과는 아래와 같다.

더보기

▶ Configuration: annotationProcessor
▶ Configuration: apiElements
▶ Configuration: archives
▶ Configuration: compileClasspath
▶ Configuration: compileOnly
▶ Configuration: default
▶ Configuration: implementation
▶ Configuration: mainSourceElements
▶ Configuration: runtimeClasspath
▶ Configuration: runtimeElements
▶ Configuration: runtimeOnly
▶ Configuration: testAnnotationProcessor
▶ Configuration: testCompileClasspath
▶ Configuration: testCompileOnly
▶ Configuration: testImplementation
▶ Configuration: testResultsElementsForTest
▶ Configuration: testRuntimeClasspath
▶ Configuration: testRuntimeOnly

다음으로 java-library 플러그인 결과다.

더보기

//... 동일 내용 생략...

▶ Configuration: api
▶ Configuration: compileOnlyApi

//... 동일 내용 생략...

아래 객체들은 플러그인에 따라 유무가 바뀌었다.

  • api : java-library 플러그인 적용 시만 존재.
  • compileOnlyApi : java-library 플러그인 적용 시만 존재.

아래 객체들은 플러그인에 따라 의미가 바뀌었다.

  • apiElements
    • java : 존재하지만, 사용하지 않음.
    • java-library : 존재하고, compile 내용 외부 전파에 사용
  • implementation
    • java : 내부에서만 사용, 외부에 노출됨.
    • java-library : 내부에서만 사용, 외부에 노출되지 않음.
※ 참고 : 외부 노출?

A와 B 프로젝트가 있다.
이때, A 프로젝트에서  B 프로젝트를 인클루드 하고 있다고 가정하자.

외부에 노출이 된다는 것은 이 상황에서 
A 프로젝트의 컴파일 / 런타임 classpath에 B의 의존성이 포함된다는 것이다.

쉽게 말해서 A라는 프로젝트에는 어떤 의존성을 넣지 않았는데, B 프로젝트에 의존성을 넣어뒀을 때 그걸 A에서 사용할 수 있을 때, 
외부에 노출되었다고 표현한다.

플러그인에 따라서 다른 점도 있지만, 추후 테스트해 보기로 하자.

 

여기서 중요한 건 "플러그인에 따라서 Configurations 객체가 변했다"라는 사실만 기억해 두자.

그래서 어디에 써?

여기가 진짜 핵심이다.

가만히 보면 Configurations 객체 이름들이 낯이 익다.

  • implementation
  • compileOnly
  • runtimeOnly

사실 우리가 dependencies {...} 블록에서 키워드처럼 사용하던 것 전부 Configurations 객체다!

 

결국 아래 코드는

// extendsFrom 설정이 없을때.
dependencies {
    compileOnly("org.projectlombok:lombok:1.18.30")
    annotationProcessor("org.projectlombok:lombok:1.18.30")
}

아래와 같은 방식으로 실행된다.

configurations["compileOnly"].dependencies.add(
    dependencies.create("org.projectlombok:lombok:1.18.30")
)

configurations["annotationProcessor"].dependencies.add(
    dependencies.create("org.projectlombok:lombok:1.18.30")
)

 

dependency 블록에서 외부 라이브러리를 추가한다는 것은 결국 특정 Configurations 객체에 의존성을 추가하는 것.

나중에 특정 태스크가 이 Configurations를 참조해서 Classpath를 구성하고 필요시 resolve 해서 JAR 파일을 다운로드한다.

 

※ 참고 : Classpath?

Classpath는 JVM(java)이나 Java Compiler(javac)가 특정 작업(컴파일, 실행, 테스트)을 수행하기 위해 필요한 모든 클래스(. class)와 라이브러리(. jar)들의 위치(경로) 모음이다.

 

주의할 점은, 모든 Configurations 객체가 dependencies {...} 블록에서 사용하는 것은 아니다.

dependencies 블록에서 사용하는 Configurations 객체는 "외부 의존성 추가용" 객체다.

Gradle 내부에서만 사용하는 객체도 있다.

  • compileClasspath : 컴파일 classpath 구성용
  • runtimeClasspath : 런타임 classpath 구성용
  • apiElements : 다른 모듈로 export 할 때 사용
  • runtimeElements : 배포용 아티팩트 export용
  • mainSourceElements : 소스 아티팩트 export용