Gradle의 의존성 충돌(Dependency Conflict) 관리 전략

kindof

·

2023. 7. 6. 21:38

0. 문제

애플리케이션의 규모가 커질수록 많은 의존성을 정의하게 되고, 의존성들이 많아질수록 보이지 않는 의존성의 충돌이 잦아지기 마련입니다.

 

예를 들어, 아래 두 개의 의존성을 살펴보겠습니다.

implementation("com.google.guava:guava:20.0")              // [1]
implementation("org.apache.hadoop:hadoop-common:2.7.4")    // [2]

[1] guava는 구글에서 제공하는 Java 라이브러리 집합으로, 컬렉션이나 해시, 문자열 처리 등을 위한 다양한 기능을 제공합니다. 여기서는 버전 20.0으로 선언했습니다.

 

[2] 다음으로 hadoop-common 2.7.4 버전 의존성을 정의했습니다. hadoop-common 라이브러리는 Hadoop 공통 모듈을 지원하는 라이브러리입니다. 

 

위와 같이 두 라이브러리를 정의한 후 Gradle dependency를 확인해보겠습니다.

$ gradle dependencies

productionRuntimeClasspath
+--- com.google.guava:guava:20.0
\--- org.apache.hadoop:hadoop-common:2.7.4
     +--- org.apache.hadoop:hadoop-annotations:2.7.4
     +--- com.google.guava:guava:11.0.2 -> 20.0

...

결과를 확인해보면, guava 라이브러리는 명시한대로 20.0을 사용하고 있는 것이 자연스럽지만 hadoop-common 의존성 하위의 guava는 11.0.2 버전에서 자동으로 20.0으로 업데이트되어 사용되고 있는 것을 볼 수 있습니다. 

 

실제로, hadoop-common-2.7.4.jar 하위의 pom.xml 에서 <parent>로 명시한 hadoop-project-2.7.4.pom 파일을 확인해보면, guava 버전은 11.0.2로 명시된 것을 확인할 수 있습니다.

hadoop-common-2.7.4.jar > hadoop-project-2.7.4.pom

그렇다면 Gradle은 어떤 전략으로 두 가지 충돌되는 버전(guava:20.0 VS guava:11.0.2)의 라이브러리를 관리하고 있는 것일까요?

 

 

1. Gradle의 의존성 해결 전략

Gradle은 의존성 그래프에 존재하는 모든 의존성 버전 중에서 가장 높은 버전을 선택합니다.

 

https://docs.gradle.org/current/userguide/dependency_resolution.html


여기서 '가장 높은' 버전은 아래와 같은 룰을 따르는데요.

  • 숫자들로만 이루어져있다면, 높은 숫자가 우선입니다. 1.1 < 1.2
  • 숫자와 알파벳이 섞여있다면, 숫자가 우선순위를 가집니다. 1.a < 1.1
  • 알파벳끼리의 비교는 소문자, 정렬 순 우선순위를 가집니다. 1.A < 1.B < 1.a < 1.b
  • ...


자세한 내용은 여기에서 확인할 수 있습니다.


따라서, 우리가 앞서 명시한 두 의존성(guava:20.0 VS guava:11.2) 사이에서는 20.0이 높은 버전이기 때문에 해당 버전으로의 통일로 의존성 충돌이 해결됩니다.


2. 주의해야 할 점

앞서 살펴본 예제에서는 guava:20.0 이 명시적으로 선언한 버전이었고, hadoop-common 하위에 guava:11.2가 존재했습니다. 그래서 최종적인 guava 라이브러리의 의존성은 20.0으로 설정되었는데요.


그런데 만약 어떤 라이브러리(L1)의 특정 버전(x.x)을 사용하고자 명시했음에도, 다른 라이브러리(L2)의 하위에서 L1의 x.x 상위 버전(y.y)을 사용하고 있다면 최종적인 L1 라이브러리의 의존성은 y.y로 맞춰집니다.


이 때, 컴파일 시점에 높은 버전으로 인한 버그를 잡아낼 수 있다면 다행이지만 런타임에 버전 간 차이로 인한 버그가 발생한다면 운영 서버에서 장애를 겪을 수 있습니다.


그래서 아래와 같이 Dependency Analysis 툴을 이용해서 반드시 라이브러리들의 버전을 확인하길 권장드립니다.

Gradle > Analyze Dependencies

 

Analyze Dependencies 툴에서는 아래와 같이 충돌이 일어나는 의존성들을 확인할 수 있습니다.

Analyize Dependencies

감사합니다.

 

3. Reference

- https://docs.gradle.org/current/userguide/dependency_resolution.html