# gradle 의존성 ###### tags: `gradle` 본 문서는 gradle 의존성에 대해 다루고 있습니다. (gradle 버전은 5.5.1입니다) [REF - Introudction to Dependency Management](https://docs.gradle.org/current/userguide/introduction_dependency_management.html#introduction_dependency_management) [REF - Managing Dependency Configuration](https://docs.gradle.org/current/userguide/managing_dependency_configurations.html#managing_dependency_configurations) [REF - JAVA 플러그인 Dependency Configuration](https://docs.gradle.org/current/userguide/java_plugin.html#sec:java_plugin_and_dependency_management) [REF - Java Library 플러그인](https://docs.gradle.org/current/userguide/java_library_plugin.html#sec:java_library_configurations_graph) [TOC] ## gradle dependency 소프트웨어는 보통 다른 소프트웨어에 의존하기 마련입니다. 예를 들어, 간단한 웹 애플리케이션을 Java 기반으로 만들기 위해서는 Spring과 관련된 많은 모듈들, Junit과 같은 테스트 모듈들, google guava 같은 유틸성 모듈들을 가져와야 합니다. 만약 gradle 멀티 프로젝트를 설계한다면, 각 프로젝트(모듈) 별로 누군가를 부르고 누군가에게 불리는 관계가 생기게 됩니다. **의존성 관리**란 소프트웨어에서 필요로 하는 다른 모듈들을 선언하고, 가져와 저장하고, 캐싱 하는 등의 관리와 관련된 모든 것을 말합니다. gradle에는 내장된 의존성 관리 지원도구가 있어 여러 시나리오에 대해 의존성을 손쉽게 관리할 수 있습니다. gradle 의존성 관리의 대략적인 그림은 아래와 같습니다. ![](https://i.imgur.com/WrsnvXS.png) 의존성과 관련해서 고려할 내용은 다음과 같습니다. (추가 예정) - 무엇을 (어떤 모듈을) 의존할 것인가 - 의존하는 대상의 타입은 무엇인가 - 어디에서 그 모듈을 가져올 것인가 - 그 모듈이 필요한 범위는 어디인가 - 테스트할 때만 그 모듈이 필요한가 아니면 프로덕션 코드에서만 그 모듈이 필요한가, 둘 다 필요한가 처럼 "의존성의 범위"와 관련된 내용 우선 "그 모듈이 필요한 범위는 어디인가"에 대해 다루어 보겠습니다. ### configuration gradle에서는 의존성의 범위를 **configuration**이라고 부릅니다. gradle 프로젝트에 선언된 모든 의존성에는 특정 범위가 적용됩니다. 예를 들어 어떤 모듈은 컴파일을 할 때 필요하고 어떤 모듈은 런타임 시에만 필요할 수 있습니다. 많은 **Gradle의 플러그인들**은 사전에 정의된 configuration을 가지고 있습니다. 예를 들어 JAVA 플러그인에서는 `implementation`, `compileOnly` 등의 configuration을 제공합니다 ## JAVA 플러그인 configuration 아래는 **Java 플러그인**에서 제공하는 의존성 configuration 목록과 그 의미입니다. ![](https://i.imgur.com/zeJ1Jz5.png) - compile (Deprecated) - `implementation`에 의해 대체되었습니다. - implementation - 구현 시점 (컴파일 시점)의 의존성 정의, compile에서 implementation으로 바뀌면서 하위 관계를 숨기도록 변경되었습니다. - compileOnly - 컴파일 시점의 의존성을 정의하며 runtime 때 사용되지 않습니다. - annotationProcessor - 컴파일 중 사용되는 어노테이션 처리 모듈을 정의 - runtime (Deprecated) - `runtimeOnly`에 의해 대체되었습니다. - runtimeOnly - 런타임 시점의 의존성 정의 task에 의해서만 사용되며 build.gradle 작성 시에는 사용하지 않는 configuration도 있습니다. - compileClasspath - compileOnly와 implementation을 한 번에 처리, 직접 사용할 일은 없습니다. - runtimeClasspath - implementation과 runtimeOnly를 한 번에 처리, 직접 사용할 일은 없습니다. ![](https://i.imgur.com/9tCLqIc.png) - testCompile (Deprecated) - `testImplementation`에 의해 대체되었습니다. - testImplementation - 테스트 코드 구현 시점의 의존성 정의 - testCompileOnly - 테스트 코드 컴파일 시점의 의존성을 정의하며 runtime 때 사용되지 않습니다. - testRuntime (Deprecated) - `testRuntimeOnly`에 의해 대체되었습니다. - testRuntimeOnly - 테스트 런타임 시점의 의존성 정의 마찬가지로 task에 의해서만 사용되며 build.gradle 작성 시에는 사용하지 않는 configuration도 있습니다 - testCompileClassPath - testImplementation과 testCompileOnly를 한 번에 처리, 직접 사용할 일은 없습니다. - testRuntimeClassPath - testRuntimeOnly와 testImplementation을 한 번에 처리, 직접 사용할 일은 없습니다. ### annotationProcessor 사용 annotationProcessor를 별도로 사용하는 이유는, 컴파일 클래스 경로를 annotation processor 클래스 경로와 분리하여 빌드 성능을 향상시킬 수 있기 때문입니다. 이것과는 별개로 gradle 5.0부터는 annotation processor 경로가 implementation 클래스 경로에 포함되어 있더라도 무시하도록 변경되었다. (5.0미만 버전 gradle에서는 warning이 나오게 됩니다) gradle 5.0부터는 annotationProcessor를 꼭 분리하여 적어주어야 합니다. ### implementation과 compileOnly 차이점 모듈 A의 configuration을 implementation로 설정할 경우, Java 프로젝트를 jar로 빌드할 때가 되면, jar 내부의 Import-Package manifest에 사용한 A모듈 package가 추가됩니다. 하지만 compileOnly로 설정한다면 jar로 빌드했을 때 Import-Package manifest에 추가되지 않습니다. [REF - Gradle: compile VS compileOnly](https://liferay.dev/blogs/-/blogs/gradle-compile-vs-compileonly-vs-compileinclude) ## Java-Library 플러그인 configuration java기반 gradle 프로젝트를 생성하면 자동으로 딸려오는 java 플러그인 외에도 **java-library 플러그인**이라는 것이 있습니다. [멀티 모듈 설계 이야기 with Spring, Gradle](http://woowabros.github.io/study/2019/07/01/multi-module.html)에서 언급된 `api`는 java-library 플러그인에서 제공되는 configuration 입니다. `apply plugin: 'java'`를 할 경우 `api`는 사용할 수 없습니다. `implementation`은 java 플러그인과 java-library 플러그인에서 동일한 기능이 제공됩니다. > ... 그래서 제가 사용하는 것이 세번째 방법인 gradle 의 implementation, api 의 활용입니다. gradle 이 업데이트되면서 기존 compile 이 deprecated 되고 두 가지 방식을 제공하도록 바뀌었습니다. 이 두 가지는 매우 유용합니다. api 는 기존의 compile 과 거의 같다고 보면 됩니다. 주목해야할 것은 implementation 입니다. > implementation 는 하위 의존 관계를 숨겨줍니다. 숨겨준다는 것은 즉 하위 의존에 대한 접근을 제한한다는 것 입니다. > ... 개인적인 생각으로, 이 문단을 조심스럽게 정확히 바꾸면 - "gradle의 implementation, api" => "Java Library 플러그인의 implementation, api" - "gradle이 업데이트 되면서" => "java 플러그인이 업데이트 되면서 compile이 deprecated 되고 implementation이 생겼습니다. 또한 java-library 플러그인이 나오면서 api가 추가로 제공되었습니다." 가 되어야 할 것 같습니다. 해당 글의 예제 역시 잘못된 것 같습니다. ![](https://i.imgur.com/D3sRRJn.png) B 모듈이 A를 의존할 때 `api`로 했기 때문에 B를 가져오는 C 입장에서는 `implementation`을 사용하건 `api`를 사용하건 A를 알 수 있습니다. (이 예제에서 C 모듈을 D가 사용할 때 B를 모르게 됩니다.) java-library 플러그인의 configuration은 1. java 플러그인의 implementation을 implementation과 api로 세분화 했다는 것 2. custom configuration을 만들기 위한 몇 가지 configuration이 추가되었다는 것 (build.gradle을 작성할 때 건들일은 없다고 합니다.) 외에 동일합니다. ![](https://i.imgur.com/j4O2ofR.png) ![](https://i.imgur.com/tpzFxkX.png) ### java 플러그인 VS java-library 플러그인 gradle 커뮤니티에서 논의된 이야기가 있어 링크를 걸어둡니다. 추후 gradle 버전에서는 java 플러그인 대신 java-library 플러그인을 default로 적용하는 것을 고려하고 있나 봅니다. [REF - when Should we use 'java' plugin and when 'java-library' plugin?](https://discuss.gradle.org/t/when-should-we-use-java-plugin-and-when-java-library-plugin/25377)