[Kopring] Multi-Module 에서의 서로 다른 Module 의 의존성 주입과 그 원리

최동근·2023년 6월 3일
1
post-thumbnail

안녕하세요 이번 포스팅에서는 코프링 프로젝트를 진행하면서 마주친 Muliti-Module 의존성 주입에 대한 오류와 이를 해결하면서 알게된 내용들을 정리하려고 합니다 👨‍💻

프로젝트에 대한 소스는 참고 해주세요 ❗️

🧃 Mulit-module 로 설계 하는 이유

많은 개발 전문가들은 Monolitic 방식의 개발보다는 Multi-Module 방식의 개발을 선호하며 현재 대다수의 프로젝트가 이러한 방식으로 진행되고 있습니다.

그렇다면 왜 번거롭게 Multi-Module 을 사용해야할까요?

  1. 모듈성과 재사용성 : Mutli-Module 은 기능 또는 역할에 따라 프로젝트를 분리함으로써 각 모듈은 독립적으로 개발 및 테스트가 가능합니다. 이는 코드의 재사용성을 향상시키고 유지보수를 용이하게 합니다.

  2. 독립적인 빌드와 배포 : 각 모듈은 개별적으로 빌드 및 배포될 수 있으므로 전체 프로젝트의 빌드 및 배포 시간을 단축시킬 수 있습니다.

  3. 개발 효율성 : 각 모듈은 독립적인 개발 프로세스를 가지므로, 여러 개발자가 동시에 다른 모듈에 대한 작업을 수행할 수 있습니다.

  4. 테스트 용이성 : 기능 별로 나누어진 각 모듈은 테스트를 독립적으로 실행할 수 있어 테스트 측면에서 용이합니다.

이처럼 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 관련 어노테이션을 사용할 수 있게 되었습니다.


참고

JAVA Annotation 완전 정복기
멀티모듈 설계 이야기 with Spring, Gradle

profile
비즈니스가치를추구하는개발자

0개의 댓글