Multi-module 을 처음 듣게 된 것은 입사하고 공부를 하다 알게 되었습니다.
Multi-module이란 것을 들었을 때, 문득, 어렴풋이 알고있는 MSA와, 관련있는 것인가?
생각이 들어, 호기심이 생겨서 서칭을 통해 프로젝트를 만들고 진행해보았습니다.
이 글은 프로젝트 진행과정에서 알게 된 점과 어떻게 진행했는지 작성한 글 입니다.
[목차]
- Multi-Module이란? (장점)
- Gradle로 Multi-Module 만들고 실행시키기
Multi-Module 이란 모듈이 여러개란 뜻입니다. 그렇다면 Module의 의미는 무엇일까요? 지난번에는 chatGPT에 물어봤었는데, 요번에는 지금 다시 핫해진 bing chat에게 물어보도록 하겠습니다.
Bing chat에 따르면, 모듈이란, 패키지의 한 단계 위의 집합체이며, 관련된 패키지와 리소스들의 재사용할 수 있는 collection(모음) 이라고 합니다. 좀 더 제가 이해하기 쉬웄던 설명은, "독립적으로 배포될 수 있는 코드의 단위"였습니다.
(출처까지 알려주니, 좋은거 같습니다)
그렇다면? 멀티 모듈이란?
멀티 모듈은 위에 정의한 모듈이 여러개 존재하고, 각 모듈간의 상호작용을 통해 하나의 서비스(프로젝트)를 구성하는 것을 의미합니다.
한 프로젝트에 여러개의 모듈을 두었다면, 어떤 장점이 있을까요?
Multi-Module의 장점을 알기 위해선 이전, 프로젝트들에 어떤 문제점이 있는지 알아보면 좋을거 같습니다.
한 서비스를 구성하는 프로젝트가 여러개 존재하고, 각 프로젝트에 하나의 싱글 모듈이 있다고 생각해보겠습니다.
위 사진과 같이 있을때, Member domain에 어떤 변화가 존재한다고 생각 해보겠습니다. 그렇다면, 각 프로젝트를 열고, 손수 작업을해서 수정해주어야 합니다. 엄청나게 번거로운 일이라는 것을 알게 될 것입니다. 또 개발자의 실수를 유발할 수 있다고 생각합니다. 이러한 문제를 해결해줄 수 있는것이 멀티모듈 입니다.
하나의 프로젝트에서, Member의 의존성을 받고, 코드를 작성하게 되면 Member 모듈의 코드만 고치면 다른 의존하는 곳에서 손수 고치지 않아도 되게 됩니다. 이렇게 간단한 이야기만 있는것은 아닌거 같지만, 일단 중복코드 및 개발자 실수방지가 큰 장점 중 하나라고 생각됩니다.
그리고 모듈은 다음과 같은 특징이 있어야 한다고 합니다.
(이 부분에 대해서는 아직 이해도가 부족합니다)
- 모듈은 독립적인 의미를 갖는다.
- 모듈은 어떠한 추상화 정도에 대한 계층을 가지고 있다.
- 계층 간 의존 관계에 대한 규칙이 있다.
Multi-Module을 통해 마이크로 서비스로(MSA)의 변화도 쉽게 가능하다고 하니 이 부분에 대해서 조금 더 공부해볼 필요가 있겠습니다.
이제부터는 실제로 프로젝트를 만들고, 실행시켜보겠습니다.
저는 간단하게 2개의 모듈만 존재하도록 만들었습니다.
우선 회사에서 계약관련 프로젝트를 만들었어서, 생각난김에 나누어 보았습니다. contract-api, contract-core 각 모듈의 역할은 다음과 같습니다.
contract-api : Controller, Service 비지니스 로직을 수행하는 모듈
contract-core : contract 관련된 Domain 관련 로직을 수행하는 모듈
우선 두가지로만 생각해보았다. 실제 서비스를 만듬에 있어서는 Domain 별로 모듈을 나누고 그 안에서 또, 나누겠지만.. 우선은 이정도로만 테스트를 해보자 생각하였습니다.
각 모듈을 포함하고 있는, 빈껍데기 프로젝트에 src부분은 삭제하고, 2개의 모듈을 추가하였다. 각 모듈의 세팅만 잘 설정해주면 되면 프로젝트는 잘 작동한다. 다음으로, root 프로젝트의 build.gradle 과, settings.gradle을 보겠습니다.
# root 프로젝트 build gradle
plugins {
id 'java'
id 'org.springframework.boot' version '2.7.2'
}
repositories {
mavenCentral()
}
subprojects {
group 'com.example'
version '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
apply plugin: 'java-library'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
repositories {
mavenCentral()
}
dependencies {
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
annotationProcessor "org.springframework.boot:spring-boot-
configuration-processor"
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
}
}
bootJar {
enabled = false
}
jar {
enabled = true
}
test {
useJUnitPlatform()
}
# root 프로젝트 setting.gradle
rootProject.name = 'DataEnvers'
include 'contract-core'
include 'contract-api'
그 다음으로, 도메인 관련 하는 core 모듈을 보겠다.
# core 모듈 build.gradle
bootJar { enabled = false }
jar { enabled = true }
dependencies {
api 'org.springframework.data:spring-data-envers'
api 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'mysql:mysql-connector-java'
}
DB 관련된 외부 라이브러리의 의존성을 설정한 것을 확인할 수 있습니다.
#CF) 추가로 implementation, api keyword의 차이를 알아야한다. 이 부분 때문에 찾아보느라 좀 고생을 했습니다 ㅠ... 이 부분은, 따로 블로깅을 하도록 하겠습니다.
그 다음으로, 계약관련 서비스하는 api 모듈을 보겠다.
# api 모듈 build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation project(':contract-core')
}
bootJar { enabled = true }
jar { enabled = false }
project :contract-core를 의존성을 갖는것을 확인할 수 있다. 이제, api 모듈에 있는 코드를 살펴보자
의존성을 갖기 때문에, 다른 모듈에 있는 Class들을 가져와 사용하는 것을 확인할 수 있다. 위에서 말한 build gradle의 api keyword를 사용하지 않으면, spring-data-jpa 의존성을 갖고있는 코드들은 api 모듈에서 사용할 수 없었다. 이는 외부 참조가 가능한지 안한지에 관련된 것인데 이 부분을 나중에 정리하겠다고 한 것입니다.
https://tecoble.techcourse.co.kr/post/2021-09-06-multi-module/
https://techblog.woowahan.com/2637/
https://hudi.blog/why-use-multi-module/
https://hyeon9mak.github.io/woowahan-multi-module/