멀티 모듈 프로젝트는 왜 세팅할까?
A 엔티티가 있고 이걸 다루는 API 서버 어플리케이션이 있다. 근데 마침 같은 A 엔티티를 다루는 배치 어플리케이션의 개발이 필요하다.
별도의 프로젝트로 배치 어플리케이션을 개발하면, 그 배치 어플리케이션 쪽에도 A 엔티티와 관련된 코드를 옮겨줘야 한다.
그리고 이 A엔티티를 다루는 어드민 전용 API 어플리케이션이 또 필요하다. 이것도 별도의 프로젝트로 개발한다면, A 엔티티는 세 군데에 동시에 코드가 존재하게 될 것이다. 마치 아래 그림처럼.
이러한 구조가 되었을 때, 만약 A 엔티티의 요구사항이 변해서 수정이 생기면 어떻게 될까? 각각의 프로젝트에서 각각 A 엔티티에 수정 사항을 반영시켜야 하고, 미처 반영하지 못하고 배포를 나갔을 경우엔 어떠한 문제가 생길지도 가늠하기 어렵다.
A 엔티티에 대한 코드를 한 곳에서 관리하고 각각의 어플리케이션에서 사용하는 방법은 없을까?
멀티 모듈 프로젝트
프로젝트 하나에서 A 엔티티를 다루는 코드를 하나의 공통 모듈에 두고, 각각 어플리케이션 모듈에서 해당 공통 모듈을 import해서 사용하도록 하면 된다. 다음과 같은 그림이 될 수 있겠다.
실무 프로젝트 구조상 하나의 소스 베이스로 여러 어플리케이션을 개발해야할 경우가 굉장히 잦기 때문에 멀티 모듈 프로젝트를 주로 보게 되곤 한다. 모듈을 분리하는 기준은 여러 기준이 있겠지만, 이번 포스팅에선 그러한 기준보단 멀티 모듈 프로젝트를 세팅하는 방법을 다룰 것이다.
멀티 모듈 전환
처음부터 멀티 모듈로 세팅하는 경우도 있지만, 내가 겪은 케이스에선 기존 API 서버 어플리케이션으로 운영하다 이후 배치 어플리케이션이 추가되면서 멀티모듈로 전환하는 케이스가 있었다. 이번엔 기존 API 서비스에서 멀티 모듈로 분리하는 과정을 다뤄볼 예정이다.
API 서버 어플리케이션
생성하는 과정은 생략하겠다. 아래의 패키지 구조로 API 어플리케이션이 처음 존재했다고 가정한다.
여기서 분리할 구조는 domain과 infrastructure 패키지를 core라는 모듈로 옮기고, application과 interfaces 패키지를 api 모듈로 옮길 것이다.
모듈 분리
일단 모듈을 추가한다. 프로젝트 루트 위치에서 New > Module
코틀린 프로젝트이기 때문에 Gradle, Kotlin 선택하여 모듈을 추가했다.
모듈이름은 core 라고 했고 첫 패키지 구조에서 domain, infrastructure 부분을 옮길 것이다.
모듈을 추가하고 나면 settings.gradle.kts에 다음과 같이 하위 모듈이 추가된걸 볼 수 있다.
모듈 추가했을 때 이런 에러가 날 수 있는데, 이건 루트모듈에 명시된 코틀린 버전과 하위에 추가한 모듈의 코틀린 버전이 달라서 그렇다. 어차피 코틀린 버전은 루트쪽 설정으로 통일할 예정이니 일단 하위 모듈의 build.gradle.kts 파일은 루트모듈의 것을 복사해서 똑같이 놔준다.
그리고 루트 프로젝트의 base package 구조와 같이 일단 모듈에 구조를 추가해준다. 모든 모듈이 똑같이 갈거다.
이거 중요하다. 안맞추면 JPA 엔티티, 레포지토리 스캔 범위를 명시적으로 지정해줘야한다.
이러고 루트의 domain과 infrastructure 에 있던 코드를 저 모듈의 하위로 옮기면 된다.
api 모듈도 동일하게 진행한다. 이 모듈에는 application 패키지와 interfaces 패키지를 위치시켰다.
이 모듈에는 main 메소드가 존재하는 메인 클래스가 존재한다. API 어플리케이션을 실행시키기 위해서다.
코드를 옮겼으면, api 모듈에선 build.gradle.kts에 다음과 같은 구문을 추가해야한다.
//...
dependencies {
//...
implementation(project(":core"))
}
//...
이 모듈은 core 모듈을 참조하여 빌드하기 때문에 해당 모듈을 의존하겠다고 선언하는 구문이다.
소스코드를 하위 모듈에 다 옮겼으면 root 모듈의 src 폴더는 과감히 삭제해도 좋다.
build.gradle.kts 정리
하위 모듈에 루트에 있던 내용을 그대로 옮겼기 때문에 이걸 정리할 필요가 있다. 각 모듈은 해당 모듈을 빌드하는 데에 필요한 내용만 들어있으면 되고, 공통된 내용은 루트 모듈에만 남기는 것이 깔끔할 것이다.
일단 루트 모듈의 build.gradle.kts에 plugins 부분에 다음과 같이 apply true를 붙인다.
그 다음 모든 프로젝트에서 공통으로 사용될 설정들은 allprojects 블럭에 넣는다. 코틀린 컴파일 및 빌드 설정 등..
그리고 이젠 의존성 부분을 정리해야한다. 모든 하위 모듈에 공통으로 필요한 의존성들은 아래와 같이 subprojects라는 블록에 정리한다.
spring-data나 querydsl, h2 같은 의존성을 공통에다 넣었는데, 이유는 application에서도 트랜잭션이 돌아가기 때문에 그렇게 했다. 좀 더 괜찮은 구조는 나중에 고민하는걸로...
그리고 각각 core와 api엔 다음과 같이 남겼다.
core에만 존재할 의존성이 따로 없더라.. 스프링 웹에 대한 의존성은 API 어플리케이션 모듈에만 있으면 되기 때문에 api 모듈엔 core 모듈과 스프링 웹에 대한 의존성이 남았다.
batch 모듈 추가
여기다 새 모듈을 추가해볼까? Batch 모듈을 추가해봤다.
batch 관련된 의존성과 core 모듈에 관한 의존성만 추가하면 땡.
이러한 구조의 멀티모듈 프로젝트가 되었다.
프로젝트 소스 코드 : https://github.com/yxxnghwan/multi-module-project-example
'Spring' 카테고리의 다른 글
RedisTemplate는 @Transactional 안에서 어떻게 동작할까? (8) | 2023.06.27 |
---|---|
내편 로깅 정책 정리 (0) | 2022.10.24 |
로깅 성능에 대한 고민 (0) | 2022.10.24 |
로그 잘 남기면 디버깅이 수월해짐 (0) | 2022.10.23 |
요청로깅 AOP로 해야겠다 (0) | 2022.10.23 |