끄적끄적

gradle 멀티모듈(모노레포) 구성하기 (with spring) 본문

개발/java & spring

gradle 멀티모듈(모노레포) 구성하기 (with spring)

코리이 2022. 10. 25. 00:49

서론

 도메인 주도 개발을 했다면 도메인별로 격리 시켜서 개발하면 편할때가 많다.

그래서 이번에 Srping Boot 를 멀티모듈을 사용해서 모노레포를 구성해보고자 한다.

우선 기본 spring boot 프로젝트를 만들어주자. 필자는 IntlliJ 유료버전을 사용하므로 이를 이용해서 구성하고자 한다.

또한 이전 포스팅 에서 corretto 를 이용해서 자바 17 을 설치했으므로 이를 이용해보자.

 

예제로 도메인 영역인 identity, order 모듈, 그리고 이 도메인들을 이용해서 스프링 앱을 띄우는 api 모듈까지 총 세개의 모듈을 생성할 예정이다.

Spring 프로젝트 생성

이때 어차피 멀티모듈로 루트폴더는 별 의미 없으므로 dependency 는 설정하지 않고 넘어갈 것이다.

또한 이번 프로젝트는 Spring boot 3.0 을 활용해볼 예정이다.

생성이 완료되면 아래 처럼 기본 spring 프로젝트 구조가 나올 것이다.

(사실 gradle 프로젝트로 실행해도 아무 상관 없다.)

멀티모듈 프로젝트로 변경하기

루트 프로젝트 설정

루트 프로젝트에서는 전체 하위 모듈을 관리하기만 하므로 개발을 진행하는 src 디렉토리가 필요 없다. 

그러므로 src 디렉토리를 삭제해주자.

그 후에는 build.gradle 파일을 수정해줘야 한다. 

예시로는 아래처럼 설정해준다.

아마 기존에 존재하던 설정들은 대부분 subproject 로 들어갔음을 알 수 있다. 

// gradle 빌드 전 설정
// 1. 변수 설정
// 2. maven 레파지토리, spring boot 플러그인 관련 설정을 한다.
buildscript {
    ext {
        springBootVersion = '3.0.0-SNAPSHOT'
        springDependencyManagementPluginVersion = '1.1.0.RELEASE'
    }
    repositories {
        mavenCentral()
        maven { url 'https://repo.spring.io/milestone' }
        maven { url 'https://repo.spring.io/snapshot' }
    }
    dependencies {
        classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}"
        classpath "io.spring.gradle:dependency-management-plugin:${springDependencyManagementPluginVersion}"
    }
}

// root 를 포함한 모든 프로젝트에 대한 곤리
allprojects {}

// settings.gradle 에 include 된 관리 (하위 모듈 관련 관리)
subprojects {
    apply plugin: 'org.springframework.boot'
    apply plugin: 'io.spring.dependency-management'
    apply plugin: 'java'

    group = 'com.example'
    version = '0.0.1'
    sourceCompatibility = '17'

    repositories {
        mavenCentral()
        maven { url 'https://repo.spring.io/milestone' }
        maven { url 'https://repo.spring.io/snapshot' }
    }

    dependencies {
        // lombok 설정 (lombok 을 쓰지 않는다면 의미 없다)
        compileOnly 'org.projectlombok:lombok'
        annotationProcessor 'org.projectlombok:lombok'

        // boot 테스트 (exclude 해주면 Junit5 만 사용한다.)
        testImplementation('org.springframework.boot:spring-boot-starter-test') {
            exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
        }
    }

    test {
        useJUnitPlatform()
    }
}

 

또한 settings.gradle 에서 root 프로젝트의 이름과 어떤 모듈을 사용할지를 직접 정해줘야 한다.

앞으로 identity, order, api 모듈을 생성할 것이므로 이를 지정해준다.

pluginManagement {
    repositories {
        maven { url 'https://repo.spring.io/milestone' }
        maven { url 'https://repo.spring.io/snapshot' }
        gradlePluginPortal()
    }
}

// root 프로젝트 이름 설정
rootProject.name = 'demo'

// 사용할 도메인 설정
include 'identity'
include 'order'
include 'api'

하위 모듈 설정

그 후 필요한 하위 모듈을 생성한다.

가장 쉽게 만드는 방법은 아래처럼 IDE 를 활용해서 모듈을 만드는 방법이다.

Gradle Module 로 우선 identity 모듈을 만들어주자

같은 방식으로 order 모듈도 생성하자.

각각의 모듈 생성 후에 submodule 의 build.grade 을 아래처럼 설정해준다.

이때 identity 와 order 는 bootJar 로 패키징 될 필요가 없으므로 enable=false 를 준다.

// identity, order 모듈의 build.gradle
bootJar {
    enabled = false
}

jar {
    enabled = true
}

dependencies {
    // 필요한 dependencies 정의
}

api 패키지는 boot jar 로 패키지 되어야 하므로 true 를 주자

또한 api 패키지는 identity 와 order 모듈을 사용하므로 이를 종속성에 추가해준다.

마지막으로 spring boot 에 의해 api 가 잘 작동하는지 확인하기 위해 web 디펜던시도 추가해줘 보자

// api 패키지의 build.gradle
bootJar {
    enabled = true
}

jar {
    enabled = true
}

dependencies {
    implementation project(':identity')
    implementation project(':order')
    implementation 'org.springframework.boot:spring-boot-starter-web'
}

 

아래처럼 세 개의 모듈이 각각 설정되어 있으면 하위 모듈 설정이 완료된 것이다.

스프링 프로젝트 실행하기

우선 간단한 프로젝트로 order 와 identity 에는 메인 클래스를 한가지씩만 각각 정의만 해줬다.

identity 는 id 와 name 을 가지고, order 는 id 와 status 를 가진다.

 

 

이제 api 를 spring application 으로 만들어 줘야 한다.

api 모듈의 패키지 가장 위에 Application 을 만들어서 부트를 실행시키도록 해주자

package api;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

이제 예시 controller 를 만들어서 api test 를 해보자

controller 에서는 order 와 identity 를 예시 클래스로 가져오는 작업을 한다.

package api;

import identity.Identity;
import order.Order;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping
public class AppController {

    @GetMapping("/identities")
    public Identity getIdentity() {
        return new Identity("1", "홍길동");
    }

    @GetMapping("/orders")
    public Order getOrder() {
        return new Order("1", "pending");
    }

    @GetMapping("/health")
    public String health() {
        return "healthy";
    }
}

결과는 아래처럼 잘 나오는 것을 확인할 수 있다.

결론

드디어 멀티모듈 프로젝트 생성이 끝났다. 물론 각각의 모듈별로 docker 화를 시키거나 테스트 모듈을 추가한다던가 하면 더 많은 설정이 필요하다. 또한 querydsl 설정시 예전에 많은 부분이 꼬였던 기억이 난다.

그래도 뼈대를 만들어 놓으면 추후에 수정하기는 훨씬 편하다는 것을 잊지 말자.