엔지니어로 가는 길

메이븐과 그래들이 버전 충돌을 해결하는 방식 본문

프로그래밍/빌드툴

메이븐과 그래들이 버전 충돌을 해결하는 방식

탐p슨 2022. 10. 30. 15:26
728x90

버전 충돌이란 하나의 dependency 그래프 내에 같은 라이브러리의 버전이 두 개 이상 존재하는 것을 말한다. 빌드툴은 버전 충돌을 해결해야 하며, 빌드툴마다 다른 버전 충돌 해결 전략을 가질 수 있다. 가장 대표적인 빌드툴에 해당하는 메이븐과 그래들은 버전 충돌 해결 전략이 다르다. 이 글에서는 버전 충돌이 무엇인지, 메이븐과 그래들에서는 각각 버전 충돌을 어떻게 해결하는지에 대해 알아본다.

참고) 메이븐에서는 버전 충돌 해결 프로세스를 dependency mediation, 그래들에서는 version conflict resolution이라고 부른다.

 

The concepts of version resolution

 

Version resolution란 unresolved dependency 그래프를 입력으로 받아 resolved dependency 그래프를 출력하는 프로세스이다.

 

Dependency 그래프란 각 라이브러리에 의해 명시된 의존성을 반영하는데, Unresolved dependency 그래프에서는 각 라이브러리가 둘 이상의 버전을 가질 수 있다. Version resolution 프로세스는 unresolved dependency 그래프를 따라가며 둘 이상의 버전을 가진 라이브러리를 만날 때마다 어떤 버전을 사용할지 결정한다. 빌드툴에서 resolution의 결과로 그래프를 생성하든 생성하지 않든 resolution의 결과를 그래프로 생각하는 것은 유용하다. 이러한 그래프를 resolved dependency graph라 부른다.

예시


위의 그래프에서 A는 10.0과 10.1 두 개의 버전을 가지고 있다. version resolution 프로세스는 하나의 버전을 고르는데, 어떤 버전을 고를지는 알고리즘에 따라 달라진다. 10.0이 선택된다면 아래와 같이 10.1에 대한 C의 의존성은 10.0으로 덮어쓰여진다.

 

하나의 버전을 선택해야 하는 이유


각각의 클래스 로더는 런타임에 오직 한 버전의 fully-qualified class name을 로드한다. 따라서 둘 이상의 버전을 클래스패스에 둘 경우 문제가 생기는데, 자바 빌드 시스템은 클래스패스가 생성되기도 전에 각 라이브러리마다 하나의 버전을 선택함으로써 이러한 이슈를 예방한다.

 

그래들과 메이븐의 version resolution 알고리즘


그래들: dependency 그래프에서 만난 버전 중 가장 최신 버전을 선택한다.


메이븐: dependency 그래프의 루트와 가장 가까운 버전을 선택하고, 만약 거리가 같은 경우 먼저 만난 버전을 선택한다. (BFS로 dependency 그래프를 순회할 때 만난 최초의 버전을 선택한다.)

 

그래들과 같은 dependency 그래프에서 메이븐은 아래와 같은 선택을 한다.

 


물론 메이븐과 그래들이 언제나 다른 선택을 하는 것은 아니다.

Interaction between Maven and Gradle


라이브러리가 빌드될 때, 라이브러리의 빌드 시스템은 자신만의 version resolution을 수행하기 때문에 라이브러리의 사용자가 라이브러리의 빌드 시스템과 다른 빌드 시스템을 사용할 경우 선택되는 버전이 다를 수도 있다. 때로는 사용자의 빌드 시스템에 의해 선택된 버전이 라이브러리에서 선택한 버전과 호환되지 않을 수도 있다.

예시


위의 그래프를 예로 들어보자. D는 A 10.0과 C에 의존하는데 C는 A 10.1에서 새로 추가된 기능에 의존하고 있다고 가정하자. 만약 A 10.0이 선택되면 C는 런타임에 문제가 생긴다. 라이브러리 D의 입장에서 보면 현재 dependency 그래프는 문제가 없다. 그래들의 경우 10.1을 선택할 것이기 때문이다.

여기서 메이븐을 사용하고 D에 의존하는 새로운 라이브러리 E를 생각해보자. (아래 그림) E가 빌드될 때 메이븐은 D는 자신이 빌드될 때 그래들에 의해 resolution이 이루어졌음에도 불구하고 D의 서브그래프를 포함하여 전체 dependency 그래프를 다시 resolve 한다. 이때 메이븐은 그래들과 달리 A 10.0을 선택하고, 이는 C를 망가뜨린다. D만 놓고 보면 전혀 내부적으로 문제가 없음에도 이런 상황이 발생할 수 있다.


ecosystem 관점에서 보면 라이브러리 D의 주인은 메이븐 사용자가 D를 사용해도 문제가 없도록 라이브러리를 만들어야 한다. 현재의 예시에서 해결 방법은 간단하다. 메이븐이 resolve 할 때도 같은 버전을 선택하도록 D가 A 10.0이 아닌 10.1에 바로 의존하도록 수정하면 된다.


원문: https://jlbp.dev/how-does-version-resolution-work-in-maven-and-gradle

728x90
Comments