maven 을 쓴지 좀 되었는데 별 생각없이 쓰다가 오늘 이슈를 하나 만났다.
요약하면 같은 라이브러리인데 모듈마다 사용하는 버전이 달라서 하나의 어플리케이션에 버전별로 디펜던시에 추가되는 문제이다.
예를 들면,
mymodule 이라는 모듈이 있고 이것을 Spring application 에서 약간 편리하게 사용하기 위해서 mymodule-spring 이라는 wrapper 같은 것을 만들고 있었는데 스프링 모듈을 가져다 사용해야 했다. 그래서 다음과 같이 pom.xml 에 dependency 를 추가했다.
<!-- spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
그리고는 pom.xml 에 properties 에 spring.version 값을 정의하였다.
그런데 이 모듈을 가져다 사용하는 어플리케이션의 스프링 버전이 mymodule 과 다른 버전이면? 최종적으로 서로 다른 두 버전의 스프링 라이브러리가 필요하게 된다. 처음에 이 mymodule-spring 을 만들게 된 계기가 mybatis-spring 사용하고 난 후여서 mybatis-spring 의 pom.xml 을 확인해봤다. 링크
여기서 확인할 수 있었던 부분이 mybatis 가 spring 모듈을 사용한 부분에서 scope 를 provided로 지정해 준 것이었다.
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
<scope>provided</scope>
</dependency>
provided scope 에 대해서 maven 의 문서를 보면 다음과 같다.
This is much like compile, but indicates you expect the JDK or a container to provide the dependency at runtime. For example, when building a web application for the Java Enterprise Edition, you would set the dependency on the Servlet API and related Java EE APIs to scope provided because the web container provides those classes. This scope is only available on the compilation and test classpath, and is not transitive.
위 내용은 번역해 보자면
이 스코프는 compile 과 유사하지만 JDK 나 container 가 의존성을 런타임에 제공한다. 예를 들면 JavaEE 웹 어플리케이션을 만들 때, Servlet API 나 JavaEE API 같은 의존성들은 provided 스코프로 지정하는데 이는 웹 컨테이너가 해당 클래스들을 제공하기 때문이다. (즉, 웹 컨테이너가 달라지면 웹 어플리케이션에 제공되는 부분들이 달라질 수 있다는 말) 이 스코프는 컴파일과 테스트 classpath 에서만 유효하고 transitive 하지 않다.
여기서 transitive 하다는 말은 (단어 번역을 잘 못하겠는데) 일반적으로 a 가 b 와 연관이 있고 b 가 c 와 연관이 있을 때, a 는 c와 연관이 있다고 말할 수 있는 상태이다. 즉 a 모듈이 b 를 의존성으로 가지면 c 역시 의존성으로 가지게 된다는 것이고 이는 maven 2.0 부터 지원되는 transitive dependencies 의 개념이다.
위 설명에서 provided 는 not transitive 하기 때문에 어플리케이션에서 mymodule 을 의존성으로 가져도 mymodule 에서 provided 로 선언된 스프링 라이브러리는 의존성을 가지지 않게 되는 결과를 가져온다.
그럼 고민 끝.