프로젝트 규모가 작을 때는 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) }