Spring Boot 멀티 모듈 구성 및 배포하기

조경민·2024년 8월 13일
0
post-custom-banner

Spring Boot 멀티 모듈

서비스의 규모가 커지고 기능이 다양해지면 코드는 점점 복잡해지기 마련이죠. 신규 기능 추가 및 유지보수를 원활하게 진행하기 위해서는 기능별, 역할별로 모듈을 분리하고 이들의 의존성을 체계적으로 관리하는 것이 좋습니다. 공통 기능을 별도의 모듈을 분리하여 다른 모듈에서 이를 같이 사용함으로써 재사용성과 일관성을 크게 향상시킵니다. 또한 각 모듈을 독립적으로 개발할 수 있기 때문에 생산성 측면의 개선도 기대할 수 있죠.

root/
├── build.gradle.kts
├── settings.gradle.kts
├── api/
│   ├── build.gradle.kts
│   └── src/
│       └── main/
│           ├── kotlin/
│           │	└── io.cloudtype.api
│           │		├── controller
│           │		├── service
│           │		└── ApiApplication.kt
│           └── resources/
├── core/
│   ├── build.gradle.kts
│   └── src/
│       └── main/
│           ├── kotlin/
│           │	└── io.cloudtype.core
│           │		├── config
│           │		├── model
│           │		└── repository
│           └── resources/
└── gradle/
    └── wrapper/
        ├── gradle-wrapper.jar
        └── gradle-wrapper.properties

이 프로젝트는 controller와 service를 담당하는 api와 model, repository를 담당하는 core의 두 개 모듈로 구성되어 있습니다. 각 모듈 내부의 디렉토리 및 파일 구조를 살펴보면 마치 독자적인 Spring Boot 프로젝트를 이루는 것 같은 모습인데요, 자세히 살펴보면 그 내용과 구성에 약간의 차이가 있습니다.

Gradle 의존성 세팅

루트 디렉토리와 각 모듈 내부에는 각각 build.gradle.ktssettings.gradle.kts이 있으며, 모듈 간에 필요한 패키지를 올바르게 참조하고 이를 jar 형태로 패키징하기 위해서 아주 중요한 역할을 합니다.

  • /settings.gradle.kts

    ...
    include("core", "api")

    루트 위치에서 각 모듈의 settings.gradle.kts에 정의된 rootProject.nameinclude()의 인수로 넘깁니다.

  • /build.gradle.kts

    import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
    
    plugins {
        id("org.springframework.boot") version "3.2.8" apply false
        id("io.spring.dependency-management") version "1.1.6"
        kotlin("plugin.jpa") version "1.9.24"
        kotlin("jvm") version "1.9.24"
        kotlin("plugin.spring") version "1.9.24" apply false
    }
    
    allprojects {
        group = "com.example"
        version = "0.0.1-SNAPSHOT"
    
        repositories {
            mavenCentral()
        }
    }
    
    java {
        toolchain {
            languageVersion = JavaLanguageVersion.of(17)
        }
    }
    
    subprojects {
    
        apply(plugin = "org.jetbrains.kotlin.jvm")
        apply(plugin = "org.jetbrains.kotlin.plugin.spring")
        apply(plugin = "org.jetbrains.kotlin.plugin.jpa")
        apply(plugin = "org.springframework.boot")
        apply(plugin = "io.spring.dependency-management")
    
        java {
            sourceCompatibility = JavaVersion.VERSION_17
        }
    
        tasks.withType<KotlinCompile> {
            kotlinOptions {
                freeCompilerArgs += "-Xjsr305=strict"
                jvmTarget = "17"
            }
        }
    
        configurations {
            compileOnly {
                extendsFrom(configurations.annotationProcessor.get())
            }
        }
    
        dependencies {
            implementation("org.springframework.boot:spring-boot-starter-data-jpa")
            implementation("org.springframework.boot:spring-boot-starter-web")
            implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
            implementation("org.jetbrains.kotlin:kotlin-reflect")
            runtimeOnly("com.h2database:h2")
            annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
            testImplementation("org.springframework.boot:spring-boot-starter-test")
            testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
            testRuntimeOnly("org.junit.platform:junit-platform-launcher")
        }
    
        tasks.withType<Test> {
            useJUnitPlatform()
        }
    }
    
    dependencies {
        implementation(kotlin("script-runtime"))
    }

    subprojects에서 각 모듈에 적용될 항목을 명시합니다.

  • core/build.gradle.kts

    plugins {
        kotlin("plugin.spring") version "1.9.24"
    }
    
    dependencies {
        implementation("org.springframework.boot:spring-boot-starter-web")
    }
    
    tasks.bootJar {
        enabled = false
    }
    
    tasks.jar {
        enabled = true
    }
    
    tasks.test {
        enabled = false
    }
  • api/build.gradle.kts

    plugins {
        id("org.springframework.boot")
        kotlin("plugin.spring")
    }
    
    springBoot {
        mainClass.set("io.cloudtype.api.ApiApplicationKt")
    }
    
    dependencies {
        implementation(project(":core"))
        implementation("org.springframework.boot:spring-boot-starter-web")
        runtimeOnly("com.h2database:h2")
    }
    
    tasks.test {
        enabled = false
    }
    • mainClass.set()의 인수로 진입점인 main 함수가 포함된 클래스를 넘깁니다. Kotlin으로 코드를 작성한 경우, 파일명.kt파일명Kt라고 작성해야 합니다.
    • core 모듈에 대한 의존성을 적용하기 위해 dependencies implementation(project(":core"))를 명시합니다.

실습

버전 정보

  • JDK 17
  • Spring Boot 3.2.8
  • Kotlin

준비 사항

GitHub 저장소

실습은 아래의 Spring Boot 어플리케이션을 통해 진행됩니다. 저장소를 clone 하거나 fork 해주세요.

따라하기

Spring Boot 배포

  1. 클라우드타입의 프로젝트 페이지에서 ➕ 버튼을 누르고 Spring Boot를 선택한 후, 미리 fork 해놓은 springboot-multi-module 를 선택합니다.
    멀티 모듈 프로젝트의 경우, 개발자가 gradle 세팅에서 main 클래스로 지정한 모듈의 executable jar 파일을 실행해야 하므로 시작 명령어에 해당 jar 파일의 경로를 정확히 명시해야 정상적으로 어플리케이션이 실행됩니다.
    기타 설정은 아래를 참고하여 입력한 후 배포하기 버튼을 클릭합니다.

    • JDK 버전: v17
    • Start command: java -jar api/build/libs/api-0.0.1-SNAPSHOT.jar
    • Include files in build: api,core
  2. 배포가 완료되면 접속하기 버튼을 누르고 주소창에 /api/users 경로를 추가하여 접속한 후 초기 데이터가 조회되는지 확인합니다.

Reference

profile
Live And Let Live!
post-custom-banner

0개의 댓글