버전 비교

  • 이 줄이 추가되었습니다.
  • 이 줄이 삭제되었습니다.
  • 서식이 변경되었습니다.

...

Dependency의 충돌

Dependency가 언제 충돌하나?

동일한 라이브러리의 다른 버전이 Classpath에 모두 추가 되는 경우

다음은 dependency의 충돌 사례입니다. 프로젝트에서 POM에 dependency를 추가했을 때 추가한 dependency가 다른 dependency를 활용하게 되는데 이때 다음과 같이 버전간의 문제가 발생할 수 있습니다. 추가한 dependency가 의존하는 dependency를 취합하면 서로 다른 두 버전을 쓰게 되는 상황이 연출됩니다. 그렇다면 이 문제를 해결하는 상식적인 방법은 호환이 되면서 하나의 버전만 사용하는 것입니다. 하위 호환이 어느 정도 유지된다고 하면 우선적으로 상위 버전을 적용해볼 수 있겠습니다.

...

버전에 따라서 메소드가 추가 또는 삭제가 될 수 있고, 메소드의 signature가 변경될 수도 있습니다. 이 경우 빌드나 컴파일시 에러가 발생할 수 있습니다. Maven 초보자의 경우 이렇게 발생하는 충돌 문제를 해결하는 것이 쉽지 않습니다. 하수와 고수의 차이가 바로 이 충돌 문제의 해결 능력에 달려있다고 해도 과언이 아닙니다.

...

Java EE 관련 dependency의 버전간 호환성 문제

다음은 Java EE 버전에 따른 Java EE를 구성하는 표준들의 버전을 나열한 표입니다. 만약 Java EE 6를 지원하는 Tomcat을 사용한다면 Servlet API는 3.0을 사용해야 합니다. 그런데 dependency를 확인해보니 Servlet API 2.5 버전이라면 표준이 호환되지 않기 때문에 당연히 Tomcat이 제대로 실행되지 않는 문제가 발생합니다. 이러한 문제가 가장 찾기 어려운 문제중 하나입니다.

SpecificationJava EE 6Java EE 7Java EE 8
Unified Expression Language (EL)2.23.03.0
Servlet3.03.14.0
Managed Beans1.01.01.0
JavaServer Pages Standard Tag Library (JSTL)1.21.21.2
JavaServer Pages (JSP)2.22.32.3
JavaServer Faces (JSF)2.02.22.3
Java Transaction API (JTA)1.11.21.2
Java Persistence API (JPA)2.02.12.2
Java API for WebSocket (WebSocket)n/a1.01.1
Java API for RESTful Web Services (JAX-RS)1.12.02.1
Java API for JSON Processing (JSON-P)n/a1.01.1
Interceptors1.11.21.2
Enterprise JavaBeans (EJB)3.1 Lite3.2 Lite3.2
Dependency Injection for Java1.01.01.0
Debugging Support for Other Languages (JSR-45)1.01.01.0
Contexts and Dependency Injection for the Java EE Platform1.01.12.0
Common Annotations for the Java Platform (JSR-250)1.11.21.3
Bean Validation1.01.12.0

다음은 Apache Tomcat의 버전에 적용된 표준의 버전을 나열한 표입니다. 웹 애플리케이션을 Spring Boot로 개발한다면, Spring WebMVC를 사용한다면 아래 버전을 유심히 확인해야 합니다.

Servlet SpecJSP SpecEL SpecWebSocket SpecAuthentication (JASIC) SpecApache Tomcat VersionLatest Released VersionSupported Java Versions
5.03.04.02.02.010.0.x10.0.0-M4 (alpha)8 and later
4.02.33.01.11.19.0.x9.0.348 and later
3.12.33.01.11.18.5.x8.5.547 and later
3.12.33.01.1N/A8.0.x (superseded)8.0.53 (superseded)7 and later
3.02.22.21.1N/A7.0.x7.0.1036 and later
(7 and later for WebSocket)
2.52.12.1N/AN/A6.0.x (archived)6.0.53 (archived)5 and later
2.42.0N/AN/AN/A5.5.x (archived)5.5.36 (archived)1.4 and later
2.31.2N/AN/AN/A4.1.x (archived)4.1.40 (archived)1.3 and later
2.21.1N/AN/AN/A3.3.x (archived)3.3.2 (archived)1.1 and later

어떻게 충돌 문제를 해결할 수 있을까?

Dependency Conflict가 발생하면 다음과 같은 에러 메시지가 발생하거나 Classpath와 관련된 오류가 발생하는 것이 대표적이고, 그외 앞서 설명한 Java SE/Java EE 표준의 궁합이 맞지 않는 경우는 더 이상한 오류 메시지가 나올 수 있습니다.

  • java.lang.NoSuchMethodError: com.google.common.collect.MapMaker.makeComputingMap(Lcom/google/common/base/Function;)Ljava/util/c oncurrent/ConcurrentMap 

Dependency Tree를 먼저 확인

가장 먼저 확인해야하는 것은 Dependency Tree를 확인하고 어떤 dependency라 문제가 되는지 찾는 것입니다. 아래는 commons-collections 의 충돌 이슈를 해결하기 위해서 별도 옵션을 지정했습니다.

코드 블럭
# mvn dependency:tree -Dverbose -Dincludes=commons-collections
... 생략
[INFO] [dependency:tree]
[INFO] org.apache.maven.plugins:maven-dependency-plugin:maven-plugin:2.0-alpha-5-SNAPSHOT
[INFO] +- org.apache.maven.reporting:maven-reporting-impl:jar:2.0.4:compile
[INFO] |  \- commons-validator:commons-validator:jar:1.2.0:compile
[INFO] |     \- commons-digester:commons-digester:jar:1.6:compile
[INFO] |        \- (commons-collections:commons-collections:jar:2.1:compile - omitted for conflict with 2.0)
[INFO] \- org.apache.maven.doxia:doxia-site-renderer:jar:1.0-alpha-8:compile
[INFO]    \- org.codehaus.plexus:plexus-velocity:jar:1.1.3:compile
[INFO]       \- commons-collections:commons-collections:jar:2.0:compile

충돌이 나는 부분은 dependency 정의시 <exclude> 처리

일단 문제가 되는 dependency를 찾으면 다음과 같이 <exclude> 처리를 합니다. 이렇게 <exclude> 처리를 하면 해당 dependency는 아예 제거됩니다.

코드 블럭
<project>
  ...
  <dependencies>
    <dependency>
      <groupId>sample.ProjectA</groupId>
      <artifactId>Project-A</artifactId>
      <version>1.0</version>
      <scope>compile</scope>
      <exclusions>
        <exclusion>  <!-- declare the exclusion here -->
          <groupId>sample.ProjectB</groupId>
          <artifactId>Project-B</artifactId>
        </exclusion>
      </exclusions> 
    </dependency>
  </dependencies>
</project>

도저히 <exclude> 를 할 수 없는 상황이면 Shading 처리

이 방법은 아주 간단합니다. JAR 파일의 패키지명을 변경하는 것입니다. Maven Shade Plugin을 사용하면 JAR 파일 내부의 패키지명을 변경할 수 있습니다. 예를 들면 com.demo 패키지의 클래스가 충돌이 발생하는 경우 이 패키지를 shade.com.demo 로 변경하여 아예 충돌이 발생하지 않도록 하는 방법입니다. 보통 이 단계까지 진행이 되면 가장 안좋은 상황으로 인지하면 되겠습니다.