회사 프로젝트 중에 멀티모듈을 사용하는 레포가 있습니다. gradle 의존성을 정리하던 와중 공통 모듈의 의존성을 포함하지 못하는 이슈가 생겼습니다. 이번에는 해당 이슈를 트러블슈팅한 경험을 소개하겠습니다.
프로젝트 구조
프로젝트 구조는 다음과 같습니다. example-common이라는 이름의 공통 모듈이 하나, example-common을 import 하는 example-service 서비스가 있습니다.
- example-common (공통모듈)
- example-service (서비스)
example-common 모듈의 build.gradle 내용은 다음과 같습니다. 전체는 아니고 일부만 가져왔습니다.
dependencies {
implementation 'org.elasticsearch.client:elasticsearch-rest-client'
}
example-service에서는 example-common모듈의 elasticsearch 라이브러리를 사용하고 싶습니다. 그러나 해당 라이브러리를 사용하니 elasticsearch-rest-client 라이브러리를 참조할 수 없다는 에러 메시지가 뜹니다. 왜 그런 걸까요?
의심 1: 의존성 확인
누가 범인인지 차근차근 생각해 보았습니다. 우선 example-common 모듈의 의존성이 example-service 모듈로 올바르게 전이되고 있는지 확인해야 합니다.
example-service의 build.gradle파일에서 example-common 모듈을 제대로 참조하고 있을까요?
dependencies {
implementation project("example-common")
}
제대로 참조하고 있습니다. 이제 다음 용의자를 만나러 가봅시다.
의심 2: settings.gradle
메인 프로젝트의 settings.gradle 파일에 example-common 모듈이 올바르게 포함되어 있는지 확인해 봅시다.
rootProject.name = 'example'
include 'example-common'
include 'example-service'
역시 제대로 선언되고 있습니다. 도대체 범인은 누구일까요?
해결 완료?
사실 해결방법은 간단합니다. 바로 example-service의 build.gradle 파일에도 똑같은 라이브러리를 추가하면 됩니다.
dependencies {
implementation project("example-common")
implementation 'org.elasticsearch.client:elasticsearch-rest-client'
}
그런데 찝찝하죠? 왜냐면 example-service가 참조하는 example-common에도 똑같은 의존성이 이미 추가가 되어 있기 때문입니다. 너무 비효율적이죠.
example-common 모듈에서 한 번만 의존성을 추가하고, 그 의존성을 example-service에서 사용할 수 있는 방법은 없을까요?
api와 implementation
문제의 핵심은 example-common 모듈의 의존성이 implementation으로 선언되어 있었기 때문입니다. implementation으로 선언하면, 이 모듈의 의존성이 다른 모듈로 전파되지 않습니다.
그러면 모듈의 특정한 의존성을 다른 모듈로 전파하고 싶을 땐 어떤 걸 사용해야 할까요? 바로 api를 사용하면 됩니다.
api와 implementation은 gradle에서 의존성을 선언할 때 사용되는 구성입니다. 이 둘은 모듈 간의 의존성 전파 방식에 영향을 미칩니다.
api
api를 사용하여 의존성을 선언하면, 해당 의존성은 선언된 모듈을 컴파일하는 데 사용될 뿐만 아니라, 해당 모듈을 의존하는 다른 모듈로도 전파됩니다.
예를 들어, A 모듈에서 B 라이브러리를 api로 선언하고, B모듈이 A 모듈에 의존하는 경우, B 모듈은 자동으로 B 라이브러리에 대한 접근 권한을 갖게 됩니다.
implementation
'implementation' 구성을 사용하여 의존성을 선언하면, 해당 의존성은 선언된 모듈 내에서만 사용됩니다. 즉, 해당 모듈을 컴파일할 때만 사용됩니다.
이 구성을 사용하면 컴파일 시간을 단축시키고, 불필요한 의존성이 프로젝트 전반에 걸쳐 전파되는 것을 방지할 수 있습니다.
특정한 목적이 없는 경우에는 implementation 사용을 권장하지만, 이번에는 공통 모듈의 의존성을 가져오고 싶다는 특정한 목적이 있기 때문에 api 구성으로 변경하겠습니다.
해결!
api를 사용하기 위해 example-common 모듈의 build.gradle 파일에 Java 라이브러리 플러그인을 적용합시다. 저는 이 과정을 빠트려서 한번 더 머리를 싸맸습니다.
// example-common의 build.gradle 파일 상단에 추가
plugins {
id 'java-library'
}
플러그인 적용 후에 example-common 모듈의 build.gradle 파일에서 api 의존성을 사용하여 elasticsearch 라이브러리를 선언합니다.
plugins {
id 'java-library'
}
dependencies {
api 'org.elasticsearch.client:elasticsearch-rest-client'
}
gradle 파일을 이렇게 변경하고, 다시 빌드합니다.
이렇게 하면 example-service 모듈에서 example-common 모듈이 올바르게 인식됩니다.
'Java & Spring Boot' 카테고리의 다른 글
Spring Boot의 MongoDB 연동: MongoTemplate vs MongoRepository (2) | 2024.10.10 |
---|---|
자바의 기본 트랜잭션 매니저와 그 구현체들 (0) | 2024.04.13 |
@Getter 어노테이션을 이해해보자 (0) | 2023.08.07 |
Java 예외에는 어떤 유형이 있을까? (0) | 2023.04.02 |
자주 쓰이는 디자인 패턴을 알아보자(with Java) (0) | 2023.03.19 |