본문 바로가기
개발/SpringBoot

[SpringBoot/Gradle] ClassNotFound Exception (Feat. implementation & api)

by Mingvel 2022. 8. 7.

 

개발을 즐기는 우리의 곁에는 운명의 짝꿍과도 같은 존재가 있습니다.

 

바로 예외(Exception) 입니다

 

@waterglasstoon 님 출처

 

우리에게는 너무 친숙하지만 되도록 마주치고 싶지 않은 NPE(NullPointerException)부터

 

친절한 IDE 선생님 덕분에 요즘은 마주치기 조차 힘든 ClassNotFoundException까지

 

다양한 에외 상황들을 인식하고, 대비하며 개발을 이어나갑니다

 

이번 글에서는 그중에서도 마주치기 힘들었던 ClassNotFoundException를 만난 경험을 공유하고자 합니다

 

 

먼저 필자는 다음과 같은 서비스 구조의 프로젝트를 진행 중이었습니다.

 

 

A 프로젝트 -> B 프로젝트 -> C 라이브러리

 

 

A 프로젝트.gradle

...

implementation(B)

...

 

B 프로젝트.gradle

...

implementation(C)

...

 

 

 

간단하게 서비스 간 의존관계를 그려보면 다음과 같습니다

 

의존관계

 

 

본론으로 돌아와서 Dev 환경의 Jenkins로 A 서비스를 Build 하던 도중 문제 상황이 터지게 됩니다.

 

...

[INFO] ------------------------------------------------------------------------
Waiting for Jenkins to finish collecting data
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.0:compile (default-compile) on project A-service: Compilation failure
[ERROR] /jenkins/jobs/A-Test/workspace/api/A-service/src/main/java/com/../SomeClass.java:[7,74] package C.SchemaValidator does not exist
[ERROR] -> [Help 1]
[ERROR] 

...

 

에러 내용을 정리하면 다음과 같은 내용이었습니다

 

 

A 서비스의 SomeClass라는 클래스에서 사용 중인 C 라이브러리의 SchemaValidator라는 클래스를 찾을 수 없다

 

 

 

클래스를 못 찾는 에러는 개발 단계에서 확인되었어야 하는 에러인데, 왜 Dev 환경까지 올라가서 발견되었는지 이해가 되지 않았습니다

 

 

 

다급히 Local 환경에서 해당하는 부분의 코드를 확인해 보았습니다

package a;

import c.SchemaValidator;

class SomeClass { 
	private final SchemaValidator = new SchemaValidator();
}

 

빨간 줄도 보이지 않았고, 코드에는 문제가 없어 보였습니다 (심지어 Local 환경에서는 단위 테스트 및 동작 테스트 모두 정상 동작하였습니다)

 

 

Compile 시점에 확인이 가능한 에러가 Local 환경에선 재현이 안되고, Dev에 올라가면 터진다는 사실은 저를 당황스럽게 만들기에 충분했습니다

 

 

동작 상의 의존 관계를 그려보니 다음과 같았고, gradle 세팅 쪽을 의심해보기로 합니다

 

 

Gradle Reference 에서 위 상황의 해답을 얻을 수 있었습니다

 

Java Library plugin - configurations used to declare dependencies

 

 

쉽게 말해서 의존성을 외부로 노출시키고자 할 경우엔 api()를,

의존성을 외부로 노출시키고 싶지 않다면 implementation()을 사용하는 것입니다

 

 

implementation() 사용 

implementation()

 

 

api() 사용 

api()

 

 

 

결론적으로 gralde 세팅을 implementation()에서 api()로 바꾸는 것으로 위 상황은 해결되었습니다

 

하지만 아직도 풀리지 않은 의문이 남아있었습니다

 

 

 

Local 환경에서는 왜 잘 동작한 것인가

 

 

위 질문에 대한 답은 IDE에 있었습니다

 

Intellij 선생님께서 라이브러리 'C'의 데이터를 로컬에 캐싱해놓으셨고

 

전 똑똑한 Intellij 선생님 덕분에 Local에서 신나게 개발할 수 있었던 것입니다.

 

 

 

오늘의 결론 

 

Intellij 선생님을 너무 믿지 말자

 

 

반응형

댓글