Recent Posts
Recent Comments
Link
11-23 13:47
Today
Total
관리 메뉴

삶 가운데 남긴 기록 AACII.TISTORY.COM

ClassNotFoundException 과 NoClassDefFoundError 본문

DEV&OPS/Java

ClassNotFoundException 과 NoClassDefFoundError

ALEPH.GEM 2021. 8. 5. 13:11

개요

우리는 자바 3rd-party 라이브러리(jar)를 사용할 때 보통 maven이나 gradle 같은 빌드 도구를 사용합니다. 빌드 도구들은 해당 라이브러리에 대한 의존적인 클래스나 라이브러리를 같이 컴파일 및 실행을 해주기 때문에 보통 문제가 없습니다. 하지만 운영을 하다가 수동으로 라이브러리를 추가하거나 업데이트할 일이 있는 경우가 있는데, 이때 우리는 종종 ClassNotFoundException과 NoClassDefFoundError을 만나게 되는 경우가 있습니다. 저는 JDBC, SOAP통신용 라이브러리나 REST Client 용 라이브러리를 추가하거나 업데이트 시에 이런 문제가 생겼기 때문에 해당 오류를 따로 조사를 해봤습니다.

 

Class Loader(JAVA8 이하)

JVM이 class의 메소드, 필드, 상속관계들을 로딩 중이나 실행 중에 확인하면서 검증합니다.
1. Bootstrap Class Loader는 JVM 초기화시 필요한 클래스($JAVA_HOME/jre/lib/rt.jar)를 로딩합니다.
2. Extension Class Loader는 $JAVA_HOME/jre/lib/ext 나 java.ext.dirs 환경변수에 지정된 경로에 클래스들을 로딩합니다.
3. System Class Loader는 환경변수 -classpath 혹은 jar 파일의 Manifest파일의 Class-Path 속성 값으로 지정된 경로의 클래스들을 로딩합니다. 

 

Tomcat

톰캣의 경우 class loading

  • Bootstrap
  • WEB-INF/classes
  • WEB-INF/lib
  • System
  • $CATALINA_BASE/lib 내의 class
  • $CATALINA_BASE/lib 내의 jar
  • $CATALINA_HOME/lib 내의 class
  • $CATALINA_HOME/lib 내의 jar

클래스의 로딩 순서는 WAS별로 다를 수 있습니다.

 

ClassNotFoundException 

1. 원인

  • 이 예외는 말그대로 컴파일 시 클래스 로더가 class를 찾을 수 없는 경우 발생합니다. 즉 참조하는 jar 파일이나 class 파일이 클래스 패스에 없는 경우 발생합니다.

2. 해결

  • class들을 로딩하는 경로들을 조사해서 해당 jar나 class 파일들이 있는지 확인합니다. 리눅스의 경우 액세스 권한도 확인해봅니다.
  • 해당 jar의 버전을 확인합니다. 왜냐하면 jar가 업데이트되면서 특정 클래스나 메서드가 변경되거나 삭제되는 경우가 생각보다 많기 때문입니다. 버전 상세 정보는 해당 라이브러리(jar)를 제공하는 사이트의 정보나 검색을 이용하셔야 합니다.
  • 의존성 라이브러리가 있는지 해당 jar와 호환이 되는 버전인지 확인합니다. 해당 jar나 class에서 다른 jar나 class를 참조하여 인스턴스를 생성하는 경우 의존적인 라이브러리들도 같이 클래스 패스에 넣어 두어야 합니다. 그 의존적인 라이브러리도 버전에 따라서 변경이 될 수 있으므로 호환되는 버전을 반드시 확인해야 합니다.

 

NoClassDefFoundError

1. 원인

  • 이 에러는 컴파일 시에는 해당 클래스를 참조하였으나 런타임(실행) 시 참조하는 클래스를 찾을 수 없는 경우 발생합니다. 그래서 개발 시에는 거의 보기 힘들지만 운영 중에는 종종 만날 수 있습니다. 그러나 해결 방법 Class Not Found Exception과 비슷합니다.

2. 해결

  • 마찬가지로 class들을 로딩하는 경로들을 조사해서 해당 jar나 class 파일들이 있는지 확인합니다. 리눅스의 경우 액세스 권한도 확인해봅니다.
  • jar 파일이 독립적으로 실행 중인지 확인해 봅니다.
  • Manifest파일의 ClassPath 속성에 클래스가 잘 정의되었는지 확인합니다.
  • 해당 jar의 버전을 확인합니다. 왜냐하면 jar가 업데이트되면서 특정 클래스나 메서드가 변경되거나 삭제되는 경우가 생각보다 많기 때문입니다. 버전 상세 정보는 해당 라이브러리(jar)를 제공하는 사이트의 정보나 검색을 이용하셔야 합니다.
  • 의존성 라이브러리가 있는지 해당 jar와 호환이 되는 버전인지 확인합니다. 해당 jar나 class에서 다른 jar나 class를 참조하여 인스턴스를 생성하는 경우 의존적인 라이브러리들도 같이 클래스 패스에 넣어 두어야 합니다. 그 의존적인 라이브러리도 버전에 따라서 변경이 될 수 있으므로 호환되는 버전을 반드시 확인해야 합니다.

 

 

추가

okhttp.jar 2.7.5 버전의 경우 okio 1.6.0 이 의존성 라이브러리였습니다. 마이너 버전 차이는 보통 잘 호환되지만 메이저 버전이 바뀌면 호환이 되지 않는 경우가 많은 것 같습니다. ApacheCXF 도 의존성 라이브러리 버전 때문에 고생을 많이 했던 기억이 납니다.

728x90