안녕하세요 이번 포스팅에서는 코프링 프로젝트를 진행하면서 마주친 Muliti-Module 의존성 주입에 대한 오류와 이를 해결하면서 알게된 내용들을 정리하려고 합니다 👨💻
프로젝트에 대한 소스는 참고 해주세요 ❗️
많은 개발 전문가들은 Monolitic
방식의 개발보다는 Multi-Module
방식의 개발을 선호하며 현재 대다수의 프로젝트가 이러한 방식으로 진행되고 있습니다.
그렇다면 왜 번거롭게 Multi-Module
을 사용해야할까요?
모듈성과 재사용성
: Mutli-Module 은 기능 또는 역할에 따라 프로젝트를 분리함으로써 각 모듈은 독립적으로 개발 및 테스트가 가능합니다. 이는 코드의 재사용성을 향상시키고 유지보수를 용이하게 합니다.
독립적인 빌드와 배포
: 각 모듈은 개별적으로 빌드 및 배포될 수 있으므로 전체 프로젝트의 빌드 및 배포 시간을 단축시킬 수 있습니다.
개발 효율성
: 각 모듈은 독립적인 개발 프로세스를 가지므로, 여러 개발자가 동시에 다른 모듈에 대한 작업을 수행할 수 있습니다.
테스트 용이성
: 기능 별로 나누어진 각 모듈은 테스트를 독립적으로 실행할 수 있어 테스트 측면에서 용이합니다.
이처럼 Multi-module 방식의 개발은 다양한 장점을 가집니다.
Multi-module 로 개발을 진행하지만 각 모듈은 의존관계를 가질 수 있습니다.
예를 들어 api
모듈과 domain
모듈이 있다고 가정해봅시다.
각각은 독립적인 모듈이기에 각자 마다의 독립된 디렉토리 구조와 gradle 파일을 가집니다 🔥
api 모듈은 프론트로부터 넘어오는 요청을 처리하는 인터페이스 역할을 하고 domain 모듈은 DB 관련 Entity 및 Repository 클래스를 가집니다.
api 모듈에서 로직을 처리하는 경우 DB 작업이 필연적입니다. 이때 api 모듈은 domain 모듈에 의존하는 현상이 생깁니다.
spring 프레임워크를 이용해서 개발하는 우리는 의존성 관련해서는 build.gradle.kts
에 관련 라이브러리 및 프레임워크를 작성하는 것을 알고 있습니다.
api 모듈은 domain 모듈에 의존하기에 domain 모듈에서 사용되는 라이브러리 및 프레임워크가 필요합니다. 즉 api 모듈 또한 domain 의 build.gradle.kts
를 참조해야합니다 ❗️
// domain 모듈의 build.gradle.kts
plugins {
kotlin("plugin.jpa")
}
version = "0.0.1"
dependencies {
implementation("org.springframework.boot:spring-boot-starter-data-jpa:2.7.6")
implementation("mysql:mysql-connector-java")
implementation("org.springframework.boot:spring-boot-starter-data-redis")
}
# domain 모듈의 domain-application.yml
spring:
jpa:
generate-ddl: false
show-sql: true
hibernate:
ddl-auto: validate
naming:
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
open-in-view: false
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3305/fintech
username: root
password: test1234
앞서 말했듯이 domain 은 DB 관련 정보를 가지는 모듈입니다.
api 모듈이 domain 모듈에 의존하기에 저는 아무렇지 않게 api build.gradle.kts
에 해당 코드를 추가했습니다.
// api ㅁ모듈의 build.gradle.kts
implementation(project(":domain")) // domain 사용
implemenation(project(":<의존하는 모듈이름>")
을 통해 외부 모듈의 의존성을 사용할 수 있게 됩니다. 즉, api 모듈에서 domain 모듈이 사용하는 의존성을 사용할 수 있게 됩니다.
위에서 볼 수 있듯이, domain 모듈의 build.gradle.kts
에 jpa 의존성이 추가되어 있습니다.
그렇다면 domain 모듈에 의존하는 api 모듈에서 jpa 관련 모든 어노테이션을 사용할 수 있지 않을까요? 🤔
이러한 저의 짧은 생각이 프로젝트에서 의존성 주입 문제를 야기했습니다.
결론은 domain 의존성을 api 모듈이 참조한다고 해도 jpa 관련 어노테이션을 사용하지 못했습니다 🥹
서로 다른 모듈에 있는 의존성을 추가할 때, 의존성은 런타임
시에 추가됩니다. 이는 Gradle 의 의존성 관리 매커니즘에 따라 동작합니다 🤖
의존성은 보통 프로젝트의 빌드 스크립트에서 선언됩니다.
위의 상황을 기반으로 생각해봅시다.
Gradle 은 api 모듈의 build.gradle.kts 에implementation(project(":domain"))
을 보고 api 모듈의 domain 모듈에 의존한다는 사실을 알게 됩니다. 그러면 런타임 시에 Gradle 은 api 모듈이 domain 모듈을 참조할 수 있도록 필요한 라이브러리 및 클래스를 로드합니다 ❗️
하지만 어노테이션은 주로 컴파일 시점에 처리되는 메타데이터입니다.
즉, 어노테이션은 컴파일
시점에 Spring 프레임워크 혹은 기타 관련 라이브러리에 의해 해석되고 처리됩니다.
정리하자면 api 모듈에서 domain 모듈을 참조해서 domain 모듈의 의존성을 사용할 때 런타임 시점에 로드하기 때문에 어노테이션에는 반영되지 못합니다.
그래서 저는 api 모듈에 build.gradle.kts
에 jpa 관련 의존성을 별도로 추가했습니다.
// api 모듈의 build.gradle.kts
implementation(project(":domain")) // domain 사용
implementation("org.springframework.boot:spring-boot-starter-data-jpa:2.7.6") // jpa 의존성 별도 추가
... // 다른 의존성
이렇게 별도로 jpa 의존성을 추가해줬기 때문에 api 모듈에서는 jpa 관련 어노테이션을 사용할 수 있게 되었습니다.