Spring Boot 분리된 모듈 설계에서 빌드 설정

White 와잇·2024년 12월 17일
post-thumbnail

의문점


현 프로젝트는 api, core, infrastructure 세 모듈로 구성되어있다.
IntelliJ IDEA를 사용하여 Spring Boot 모듈을 추가했을 때 기본적으로 build.gradle을 구성해준다.

그래서 프로젝트의 build.gradle과 각 모듈의 build.gradle 3개를 총합하면 4개의 build.gradle 파일이 존재하고 있다.

각 모듈에서만 사용될 의존성 등은 모듈 속 빌드 설정을 하면 되겠지만
전체 프로젝트의 빌드 설정을 어떻게 하면 효과적으로 사용할 수 있을지 고민이 되었다.

또한,
빌드 결과물이 각 모듈마다 출력된다. 이것들을 추후 배포할 때 어떤 방법을 택해야 할까.


jar vs bootJar


  • build.gradle (:api) 예시
plugins {
    id 'org.springframework.boot' version '3.1.0'
    id 'java'
}

bootJar {
    enabled = true  // Spring Boot 실행 JAR 생성
}

jar {
    enabled = false  // 표준 JAR 비활성화
}

dependencies {
    implementation project(':core')  // 내부 라이브러리 의존성
    implementation 'org.springframework.boot:spring-boot-starter-web'
}

jar

jar 태스크는 표준 Java 라이브러리 jar 파일을 생성

  • 애플리케이션 실행 불가
  • 재사용 가능한 패키지, 라이브러리, 유틸 배포 시 사용
  • Spring 실행 환경 미포함

bootJar

bootJar 태스크는 Spring Boot 실행 가능한 jar 파일을 생성

  • 애플리케이션 실행 가능 java -jar
  • Spring Boot 런타임, 모든 의존성, 애플리케이션 라이브러리 포함

권장 빌드 전략

  • Spring Boot 애플리케이션 (api): bootJar ⭕, jar
  • 라이브러리/유틸리티 모듈 (core, infrastructure): bootJar ❌, jar

❓ 궁금한 것

  • jar, bootJar 둘 다 활성화하면?
    -> build/libs/ 디렉토리에 둘 다 생성됨
  • jar 또는 bootJar 태스크가 명시되지 않으면?
    -> 기본적으로 jar 태스크를 실행하나,
    -> Spring Boot 플러그인이 있다면 bootJar 태스크를 실행 (jar❌)

빌드 전략


멀티 모듈 프로젝트의 빌드 전략..

크게 두 가지의 선택지가 있다.

  1. 통합 단일 JAR 생성

    • 루트 프로젝트에 실행 가능한 JAR 파일 생성
    • api, core, infrastructure 병합됨
    • 단일 서비스의 경우 적합 (REST API만 제공 등)
  2. 모듈별 개별 JAR 생성

    • 모듈 간 독립적 배포 가능
    • 마이크로서비스(개별 서비스를 배포), 라이브러리 배포 등 가능

나는 1번을 선택 (REST API 백엔드 서버)

통합 JAR 생성 전략

  • 프로젝트 구조
multi-module-project/
├── build.gradle
├── settings.gradle
├── api
│   └── build.gradle
├── core
│   └── build.gradle
├── infrastructure
|   └── build.gradle
...
  • 루트 빌드 설정
plugins {
    id 'org.springframework.boot' version '3.1.0' apply false
    id 'io.spring.dependency-management' version '1.1.0'
    id 'java'
}

allprojects {
    group = 'com.example'
    version = '1.0.0'

    repositories {
        mavenCentral()
    }
}

subprojects {
    apply plugin: 'java'
    apply plugin: 'io.spring.dependency-management'

    java {
        sourceCompatibility = JavaVersion.VERSION_17
        targetCompatibility = JavaVersion.VERSION_17
    }
}

dependencies {
    implementation project(':core')
    implementation project(':infrastructure')
}

bootJar {
    enabled = false
}

jar {
    enabled = false
}
  • settings.gradle
rootProject.name = 'multi-module-project'
include('api', 'core', 'infrastructure')
  • api 모듈 빌드 설정
plugins {
    id 'org.springframework.boot' version '3.1.0'
    id 'java'
}

dependencies {
    implementation project(':core')
    implementation project(':infrastructure')

    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
}

bootJar {
    enabled = true
}

jar {
    enabled = false
}
  • core 모듈 빌드 설정
plugins {
    id 'java'
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter'
}

bootJar {
    enabled = false
}

jar {
    enabled = true
}
  • infra 모듈 빌드 설정
plugins {
    id 'java'
}

dependencies {
    implementation project(':core')
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-amqp'
    implementation 'org.postgresql:postgresql'
}

bootJar {
    enabled = false
}

jar {
    enabled = true
}

트러블슈팅 1

루트 빌드 설정에 다음과 같이 lombok 의존성을 추가한 상태.

  • 루트 빌드에 subprojects는 하위 프로젝트(api, core, infrastructure)의 공통 설정
subprojects {
	dependencies {
    	compileOnly 'org.projectlombok:lombok'
        annotationProcessor 'org.projectlombok:lombok'
        ..
    }
}

build.gradle(:core) 에 다음 의존성을 추가한 상태

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-redis'
}

여기서 빌드를 하면 다음과 같은 로그를 볼 수 있다.

Q. 왜 core 에서는 lombok과 redis 의존성을 찾을 수 없나?

A. 버전을 명시해주면 더 이상 해당 오류는 등장하지 않는다.
예) compileOnly 'org.projectlombok:lombok' -> compileOnly 'org.projectlombok:lombok:1.18.30'

Q. 왜 버전을 명시하지 않아도 잘 동작하기도 했었나?

A. 의존성을 자동으로 관리해주는 플러그인 & 의존성이 설정되어있었기 때문이다.

Q. 부모 POM이 무엇인가?

A. Maven 프로젝트에서 상위 프로젝트의 의존성, 플러그인 등 빌드 설정을 정의하는 POM 파일이다. 부모 POM에서 의존성, 플러그인 등을 정의하면 하위 프로젝트에서 버전을 명시하지 않아도 된다.

Q. Maven이 아닌 Gradle 설정은 어떻게 하나?

부모 POM 개념은 없지만 io.spring.dependency-management 플러그인 등을 사용하여 자동 버전 관리 필요

플러그인 없이 라이브러리만 추가하는 것은 가능하지만, IDE와의 통합 및 빌드 과정에서의 편리함이 떨어질 수 있다.

Q. apply plugin: 과, plugins{...}의 차이

  • apply plugin:
    Gradle 스크립트에서 플러그인을 적용하는 전통적인 방법
    플러그인을 적용한 후, 해당 플러그인에 대한 추가 설정 필요

  • plugins{...}
    Gradle의 새로운 DSL(도메인 특화 언어)
    플러그인을 선언적으로 적용하는 방법
    현대적이고 안정성이 높은 방법
    플러그인의 버전을 명시적으로 지정 가능
    플러그인 적용 순서와 관련된 문제를 줄일 수 있음
    Gradle의 초기화 단계에서 플러그인을 적용하므로, 더 안전하고 예측 가능한 방식으로 플러그인을 사용가능

profile
웹개발 도전! 데브옵스 도전!

0개의 댓글