0. 서론
0.1 왜 이걸 찾아보게 되었는가?
- 멀티 모듈 아키텍처에서 의존성을 정리하면서 다양한 개념들이 나왔지만 필요한 것만 골라서 찾아봄
- 그 당시에는 어떤 역할을 하는지 간단하게 알아보고 사용했기에 잠시만 기억하고, 기억에 남아 있지 않음
- 하지만 계속 사용할 거라고 생각했기 때문에 한번 정리가 필요하다고 생각
0.2 정리하고자 하는 내용
- gradle의 역할 및 특징, build.gradle에서 볼 수 있는 설정들을 1편에서 다루었습니다 -> https://recently0.tistory.com/18
- 의존성과 관련된 implementation, compileOnly, runtimeOnly, runtimeClasspath와 같은 설정들에 대해 알아보려고 합니다.
1. Java Library Plugin
1.1 Java Library Plugin
- 초록색 설정은 사용자가 의존성을 선언하기 위해 사용
- 핑크색 설정은 컴포넌트 컴파일이나 라이브러리 실행을 위해 사용
- 파란색 설정은 컴포넌트 자체 내부에서 사용하기 위한 것
이렇게만 작성했을 때는 초록색 설정들은 사용하면서 접해왔기에 알 수 있지만, 핑크색 및 파란색 설정은 정확히 어떤 내용인지 확인하기 어려울 수 있습니다. 그래서 아래에 색깔별 그리고 비슷한 개념끼리 묶어서 정리했습니다.
2. compileClasspath, runtimeClasspath
2.1 compileClassPath, runtimeClassPath (with Test)
compileClasspath
- 자바 컴파일러가 컴파일할때 필요한 classPath를 설정할 때 사용
- testCompileClasspath도 동일하게 테스트에 필요한 classPath를 설정할 때 사용
- 빌드 결과물에 포함되지 않음
- api, implementation, compileOnly, compileOnlyApi 의존성이 이에 포함됨
runtimeClassPath
- 런타임 때 필요한 classPath를 설정할 때 사용
- testRuntimeClasspath도 동일하게 테스트에 필요한 classPath를 설정할 때 사용
- 작성한 의존성은 빌드 결과물에 포함됨
- api, implementation, runtimeOnly 의존성이 이에 포함됨
3. apiElements, runtimeElements
3.1 apiElements, runtimeElements
apiElements
- apiElements는 해당 라이브러리를 컴파일하기 위해 필요한 모든 요소를 검색할 때 사용
- 다른 모듈이나 프로젝트가 컴파일할 때 필요한 API 제공
- api로 선언된 의존성만 포함하기 때문에 해당 모듈을 참조하는 다른 모듈은 api로 의존성 선언된 API만 사용할 수 있음
runtimeElements
- runtimeElements는 해당 라이브러리를 실행하기 위해 필요한 모든 요소를 검색할때 사용
- api, implementation, runtimeOnly로 선언된 모든 런타임 의존성을 포함
apiElements, runtimeElements 요소 확인
- guava, apache coommons-lang, mysql-connector-java에 대한 의존성을 가지도록 구성
- printConfigurations 테스크를 추가한 후 실행할 수 있도록 구성
- 테스트 실행 apiElements는 guava 의존성만 출력, runtimeElements는 모든 의존성을 출력
plugins {
id("java-library")
}
configurations {
resolvedApiElements {
canBeResolved = true
extendsFrom apiElements
}
resolvedRuntimeElements {
canBeResolved = true
extendsFrom runtimeElements
}
}
dependencies {
api("com.google.guava:guava:31.0.1-jre") // apiElements에 포함
implementation("org.apache.commons:commons-lang3:3.12.0") // runtimeElements에만 포함
runtimeOnly("mysql:mysql-connector-java") // runtimeElements에만 포함
}
// apiElements 및 runtimeElements 출력 테스크 추가
tasks.register("printConfigurations") {
doLast {
println "resolvedApiElements: " + configurations.resolvedApiElements.files
println "resolvedRuntimeElements: " + configurations.resolvedRuntimeElements.files
}
}
4. compileOnly, compileOnlyApi, runtimeOnly
4.1 compileOnly
어떤 역할을 하고 어떤 상황에 사용하는가?
- 컴파일에는 필요하지만 런타임에 필요없을때 compileOnly를 사용
compileOnly 사용 예시
- 예를 들어 Apache Commons Lang 라이브러리에 속한 StringUtils 클래스의 메서드를 아래와 같이 사용한다고 가정
- 아래와 같이 compileClasspath를 기반으로 하는 의존성 설정 (ex.compileOnly, implementation)을 사용하면 문제가 없음
- 만약 의존성을 설정하지 않는 경우에는 컴파일에 실패
public static void main(String[] args) {
String text = "hello world";
System.out.println(StringUtils.hasText(text));
}
dependencies {
//compileClassPath를 기반으로 동작하는 의존성 설정
compileOnly 'org.apache.commons:commons-lang3:3.12.0'
}
compileOnly, compileOnlyApi 비교
compileOnly, compileOnlyApi의 차이점은 다음과 같이 정리할 수 있습니다
- 예를 들어 특정 라이브러리를 만드는데 Lombok을 사용해서 구현했다고 가정
- 해당 라이브러리를 사용하는 유저는 Lombok을 사용하지는 않는다고 했을 때, 내부 모듈의 컴파일단계에서만 필요로 하므로 compileOnly를 사용
- compileOnlyApi는 다른 모듈에도 전달하고 싶은 경우에 사용
차이 요약
- compileOnly → 내부 모듈에서만 사용하고, 다른 모듈에서는 접근 못 함.
- compileOnlyApi → 다른 프로젝트에서도 사용할 수 있지만, 실행할 때는 포함되지 않음.
4.2 runtimeOnly
runtimeOnly 역할
- 사용하려는 라이브러리가 런타임에만 필요한 경우 사용
- runtimeOnly를 사용해 컴파일 시간을 단축할 수 있음
runtimeOnly 예시
- Spring Boot에서 JDBC 인터페이스를 기반으로 DB 연결을 설정했고, MySQL을 사용할 거라고 가정
- 컴파일 타임에는 실제로 데이터베이스와의 연결이 중요치 않기에 MySQL JDDBC 드라이버가 필요 없음
- 그렇기 때문에 아래와 같이 런타임에 필요한 mysql-connector를 runtimeOnly로 의존성 설정
dependencies {
runtimeOnly 'mysql:mysql-connector-java:8.0.30' // MySQL JDBC 드라이버
}
5. api, implementation
5.1 api, implementation
api, implementation 공통점
- 의존성 설정한 라이브러리가 compileClasspath, runtimeClasspath에 속하게 되어 사용
api, implementation 차이점
- api는 모듈을 사용하는 소비자에게 의존성이 외부로 노출됨
- implementation은 모듈을 사용하는 소비자에게 의존성이 노출되지 않아 해당 모듈 내부에서만 사용가능
api에 비해 implementation 장점
- 외부로 의존성이 노출되지 않기 때문에 불필요한 의존성 전파를 막아서 컴파일 속도를 향상
- 모듈 간에 불필요한 의존성들을 정리해 각 모듈을 조금 더 독립적으로 관리하기 용이해짐
- 다만 언제나 implementation만 사용하는 것이 아니라 상황에 맞게끔 사용
api, implementation 사용 예시
- A, B 모듈이 있고, B 모듈이 A 모듈을 의존하고 있다고 가정
- A 모듈에서 commons-lang 의존성을 implementation으로 사용했다고 가정
- 이때 B 모듈은 A 모듈이 의존성 설정한 commons-lang 라이브러리를 사용할 수 없음.
- implementation이 아닌 api로 선언했을 경우는 의존성이 전파되어 별도로 의존성을 설정하지 않더라도 사용 가능
dependencies {
implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.12.0'
}
6. 글을 마치며
여기까지 build.gradle의 dependencies 코드 블럭에서 볼 수 있는 의존성 설정들에 대해서 알아보았습니다. 자주 사용하던 개념들을 공부하다 보니 이를 바탕으로 gradle의 task, gradle의 장점으로 꼽히는 기능들의 원리, 그리고 이외에도 의존성만이 아닌 다양한 build.gradle의 설정들을 더 알아보면 좋겠다고 생각했습니다. 오늘도 읽어주셔서 감사합니다.
'Spring' 카테고리의 다른 글
Gradle 설정 알아보기 (1편) (0) | 2025.02.16 |
---|---|
Flyway 도입 전 탐색해보기 (2) | 2024.12.07 |
환경변수가 있었는데요? 없었습니다 (2) | 2024.11.09 |
JPA 양방향 매핑 탈출기 (0) | 2024.10.27 |
@PathVariable 엔드포인트 매핑 원리 (1) | 2024.10.12 |