프로젝트 규모가 작을 때는 Single-Module로 구성해도 전혀 문제가 없습니다.
하지만 프로젝트 규모가 커지고 기능이 많아지면, 설정이나 코드가 복잡해 집니다.
이때 필요한 구성이 바로 Multi-Module입니다.
코드 재사용성
공통 기능을 하나의 모듈에 구축하고, 여러 모듈에서 활용할 수 있다.
기능 분리
모듈 별로 기능을 분리해서 직관적인 프로젝트 관리가 가능하다.
빌드 유연성
루트 프로젝트에서 전체를 빌드할 수도 있고, 개별 모듈 별로 빌드를 할 수도 있다.
의존성 분리
전체 프로젝트에 의존성을 추가할 필요 없이, 개별 모듈에 필요한 의존성을 지정할 수 있다.
API 프로젝트를 기준으로 설계를 진행해 보겠습니다.
api (프로젝트 루트)
api-core (api 공통)
공통 유틸을 비롯한 프로젝트 공통 기능 모듈
api-domain (api 데이터)
Service, Repository, Entity 등 데이터 모듈
api-web (api 인터페이스)
컨트롤러를 비롯한 통신 모듈
먼저 프로젝트 루트에 사용 할 모듈을 지정합니다.
그리고 전체 프로젝트 빌드 설정을 진행합니다.
루트에서는 별도 코드가 없으므로, src 디렉토리는 제거합니다.
그리고 전체 프로젝트와 서브 프로젝트의 설정을 추가합니다.
settings.gradle.kts (api)
rootProject.name = "api"
include("api-core")
include("api-domain")
include("api-web")
build.gradle.kt (api)
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("org.springframework.boot") version "2.7.7"
id("io.spring.dependency-management") version "1.1.0"
kotlin("jvm") version "1.7.22"
kotlin("plugin.spring") version "1.7.22"
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
allprojects {
group = "com.devfoxstar"
version = "0.0.1-SNAPSHOT"
repositories {
mavenCentral()
}
tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "11"
}
}
tasks.withType<Test> {
useJUnitPlatform()
}
}
subprojects {
apply(plugin = "org.springframework.boot")
apply(plugin = "io.spring.dependency-management")
apply(plugin = "org.jetbrains.kotlin.jvm")
apply(plugin = "org.jetbrains.kotlin.plugin.spring")
}
tasks.bootJar {
enabled = false
}
tasks.jar {
enabled = true
}
프로젝트 구성에서 신규 모듈을 추가합니다.
New Moudle 메뉴로 간단하게 생성이 가능합니다.
이제 개별 모듈에 필요한 의존성과 설정을 추가합니다.
tasks.bootJar 값을 false로 하는 이유는 api-web에서만 bootJar가 필요하기 때문입니다.
build.gradle.kt (:api-core)
dependencies {
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
tasks.bootJar {
enabled = false
}
tasks.jar {
enabled = true
}
build.gradle.kt (:api-domain)
dependencies {
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
tasks.bootJar {
enabled = false
}
tasks.jar {
enabled = true
}
build.gradle.kt (:api-web)
dependencies {
api("io.springfox:springfox-boot-starter:3.0.0")
implementation(project(":api-core"))
implementation(project(":api-domain"))
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
api-web 모듈에서 SpringBoot Application을 실행합니다.
ApiApplication.kt
package com.devfoxstar.api.web
import com.devfoxstar.api.core.ApiCore
import com.devfoxstar.api.domain.ApiDomain
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration
import org.springframework.boot.runApplication
@SpringBootApplication (
scanBasePackageClasses = [ApiCore::class, ApiDomain::class, ApiWeb::class],
exclude = [DataSourceAutoConfiguration::class, ErrorMvcAutoConfiguration::class]
)
class ApiApplication
fun main(args: Array<String>) {
runApplication<ApiApplication>(*args)
}