[Spring Boot] Multi Module

by 민갤

Back End /

멀티 모듈 단일 프로젝트

서로 독립적인 프로젝트를 모듈로 구성하여 프로젝트 하나로 묶는다.

모듈

큰 체계 안에 존재하는 독립적인 구성요소

프로그램을 구성하는 시스템을 기능 단위로 분리하여 독립시킨다.

책임과 역할이 명확하다.

의존성을 최소한만 가진다. (불필요한 동작과 스파게티 코드 예방)

장점

재사용성

  • 도메인 구조와 규칙에 대한 동일성을 보장한다.
  • 필요한 기능을 다른 모듈에서 가져다 사용할 수 있다.
  • 중복 코드를 최소화한다.

생상성 향상

  • 기능이 영향을 끼치는 범위를 예측할 수 있다.
  • 버그나 변경이 발생했을 때 프로젝트 전체를 빌드할 필요없이 해당 모듈만 빌드 재배포한다. (빌드 시간 단축)
  • 결합도가 낮아서 변경에 따른 영향력을 최소화할 수 있다.
  • 전체 프로젝트에 대한 이해도가 필요 없다.
  • 도메인 모델이 어떤 Controller, Service로 이어지는 지 알 필요 없다.

구조 변경 용이

  • MSA(Microservice Architecture)로 구조 변경 시 각 모듈을 개별 서버로 만들기만 하면 된다.
  • Monolithic Architecture로 변경이 쉽다.

접근성

  • IDE를 프로젝트마다 실행하지 않아도 된다

단점

설정 파일과 패키지 의존성 관리가 필요하다.

  • 공통 모듈 사용에 주의해야 한다.
  • 참조하는 모듈에서 불필요한 의존성으로 인해 어플리케이션이 무거워질 수 있다.

기본 구조

root project
├── api
│    └── src
│          └── main
│                ├── kotlin
│                │   └── com.dev.api
│                │       ├── ApiApplication.java
│                │       ├── controller
│                │       │   └── UserController.java
│                │       └── service
│                │            └── UserService.java
│                └── resources
│                      └── application.yml
├── batch
│    └── src
│          └── main
│                ├── kotlin
│                │   └── com.dev.batch
│                │       ├── BatchApplication.java
│                │       └── batchjob
│                │           └── SimpleBatchJob.java
│                └── resources
│                      └── application.yml
└── core
      └── src
            └── main
                  ├── kotlin
                  │   └── com.dev.core
                  │       ├── domain
                  │       │   └── User.java
                  │       └── repository
                  │            └── UserRepository.java
                  └── resources
                        └── application.yml

프로젝트로 나뉘어 있던 Client Api, Admin Api, Batch를 모듈로 구성

root project
├── admin api
├── client api
├── batch
└── core

예제

1. Root 프로젝트 생성 (Spring Initializr, Kotlin, Gradle)

[root] build.gradle

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
    id("org.springframework.boot") version "2.7.5" apply false
    id("io.spring.dependency-management") version "1.0.15.RELEASE" apply false

    kotlin("jvm")
    kotlin("plugin.spring") apply false
    kotlin("plugin.allopen") apply false
    kotlin("plugin.noarg") apply false
    kotlin("kapt") apply false
}

allprojects {
    group = "com.example"
    version = "0.0.1-SNAPSHOT"

    repositories {
        mavenCentral()
    }
}

subprojects {
    apply {
        plugin("java")
        plugin("kotlin")
        plugin("kotlin-jpa")
        plugin("org.springframework.boot")
        plugin("io.spring.dependency-management")
    }

    dependencies {
        implementation("org.jetbrains.kotlin:kotlin-reflect")
        implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
        implementation("org.springframework.boot:spring-boot-starter-data-jpa")
    }

    configure<JavaPluginExtension> {
        sourceCompatibility = JavaVersion.VERSION_17
        targetCompatibility = JavaVersion.VERSION_17
    }

    tasks.withType<KotlinCompile> {
        kotlinOptions {
            freeCompilerArgs = listOf("-Xjsr305=strict")
            jvmTarget = JavaVersion.VERSION_17.toString()
        }
    }

    tasks.withType<Test> {
        useJUnitPlatform()
    }
}

project(":dev-core") {
    apply(plugin = "kotlin-spring")
    apply(plugin = "kotlin-kapt")
}

project(":dev-api") {
    apply(plugin = "kotlin-spring")
    dependencies {
        implementation(project(":dev-core"))
    }
}
  • pugins: subproject에 plugin이 바로 적용되지 않게 apply false 설정
  • allprojects: rootProject를 포함한 모든 프로젝트에 적용
  • subprojects: setting.gradle 에 include 된 하위 프로젝트에 적용
  • project: setting.gradle 에 include 된 특정 하위 프로젝트에 의존성 설정
  • 간혹 build.gradle 에서 compile(v6), api(v7)를 사용하여 모듈이 가진 의존성을 상속시키는 경우가 있는데 이는 의존성을 높이기 때문에 권장하지 않는다.

[root] gradle.properties

kotlinPluginVersion=1.6.21

[root] settings.gradle

pluginManagement {
    val kotlinPluginVersion: String by settings
    plugins {
        kotlin("jvm") version kotlinPluginVersion
        kotlin("plugin.spring") version kotlinPluginVersion
        kotlin("plugin.allopen") version kotlinPluginVersion
        kotlin("plugin.noarg") version kotlinPluginVersion
        kotlin("kapt") version kotlinPluginVersion
    }
}

rootProject.name = "multi-module"

2. 불필요한 src 폴더 삭제

루트 프로젝트는 모듈을 묶는 용도

3. Core Module 생성

[dev-core] build.gradle

description = "Domain Module"

allOpen {
    annotation("javax.persistence.Entity")
    annotation("javax.persistence.Embeddable")
    annotation("javax.persistence.MappedSuperclass")
}

noArg {
    annotation("javax.persistence.Entity")
    annotation("javax.persistence.Embeddable")
    annotation("javax.persistence.MappedSuperclass")
}

dependencies {
    implementation("org.springframework.boot:spring-boot-configuration-processor")
    implementation("com.querydsl:querydsl-jpa")
    kapt("com.querydsl:querydsl-apt::jpa")

    implementation("org.springframework.boot:spring-boot-starter-jdbc")
    implementation("mysql:mysql-connector-java")
}

tasks.getByName<org.springframework.boot.gradle.tasks.bundling.BootJar>("bootJar") {
    enabled = false
}

tasks.getByName<Jar>("jar") {
    enabled = true
}

Main Class가 필요없는 모듈은 의존성이 포함되지 않는 plain jar 만 생성하도록 설정한다.

4. Api Module 생성

[dev-api] build.gradle

description = "Api Module"

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web")
    developmentOnly("org.springframework.boot:spring-boot-devtools")
    testImplementation("org.springframework.boot:spring-boot-starter-test")
}

[dev-api] Main Class

core-module 에 있는 entity와 repository bean을 찾을 수 있도록 설정한다.

  • 방법 1: @ComponentScan를 사용. api-module package 에 있는 bean도 읽을 수 있게 같이 설정해야 한다.
@SpringBootApplication
@ComponentScan(basePackages = ["com.dev.api", "com.dev.core"])
class DevApiApplication
  • 방법 2: @EntityScan, @EnableJpaRepositories 를 사용. package를 두 번 작성해야 한다
@SpringBootApplication
@EntityScan(basePackages = ["com.dev.core"])
@EnableJpaRepositories(basePackages = ["com.dev.core"])
class DevHomeApplication

5. 하위 모듈 확인

[root] settings.gradle

...

rootProject.name = "multi-module"
include("dev-core")
include("dev-api")

자동으로 core가 하위 모듈로 설정된다.

Reference

https://techblog.woowahan.com/2637/

https://cjw-awdsd.tistory.com/55

https://kotlinworld.com/324

https://jinhanchoi1.medium.com/spring-boot-gradle-plugin-multi-module-build-bootjar-false-5e53a1f6224c

https://hyeon9mak.github.io/woowahan-multi-module/

https://do-study.tistory.com/122

https://kotlinworld.com/317

https://bokyung.dev/2022/03/23/kotlin-gradle-querydsl/

https://velog.io/@cha-sung-soo/Multi-Module-%EC%82%AC%EC%9A%A9-%EC%9D%B4%EC%9C%A0

https://wildeveloperetrain.tistory.com/183

Author

민갤

민갤

Back-End Developer

꾸잉꾸잉하고 웁니다.

로그인

디코에 오신 것을 환영해요!
전문가들의 수많은 아티클 창고 🤓