프로그래밍 PROGRAMMING/아키텍쳐

[SPRING/JAVA] Spring Boot 멀티모듈 프로젝트 구조 설계하기

매운할라피뇽 2026. 4. 2. 08:30
반응형
Spring Boot 멀티모듈 프로젝트 설계

Spring Boot 기반 애플리케이션이 커질수록 단일 모듈 구조는 빌드 시간 증가, 의존성 혼재, 팀 간 경계 모호화 같은 문제를 일으킵니다. 멀티모듈 프로젝트는 코드를 논리적 단위로 분리해 각 모듈의 책임을 명확히 하고, 재사용성과 독립적 빌드를 가능하게 합니다. 이 글에서는 Gradle 기반 Spring Boot 멀티모듈 프로젝트를 실제 프로젝트에서 활용 가능한 수준으로 설계하는 방법을 단계별로 설명합니다.


멀티모듈 구조의 핵심 원칙

멀티모듈 설계에서 가장 중요한 원칙은 의존성 방향을 단방향으로 유지하는 것입니다. 상위 모듈이 하위 모듈에 의존하되, 역방향 의존은 허용하지 않아야 순환 참조 문제를 방지할 수 있습니다.
일반적으로 많이 사용하는 레이어 분리 방식은 다음과 같습니다:

  • app 모듈: Spring Boot 애플리케이션 진입점, 각 도메인 모듈을 조합
  • domain 모듈: 핵심 비즈니스 로직, JPA 엔티티, 도메인 서비스 포함
  • infra 모듈: 외부 시스템 연동(DB, 메시징, 외부 API 등), domain에 의존
  • common 모듈: 공통 유틸리티, 예외 정의, 공통 DTO 등 순수 Java 코드

이 구조에서 domain은 Spring 의존성 없이 순수한 비즈니스 로직만 담도록 설계하면 테스트 속도와 재사용성이 크게 향상됩니다.


루트 build.gradle 및 settings.gradle 설정

프로젝트 루트의 settings.gradle에 모듈을 등록하고, 루트 build.gradle에서 공통 의존성을 관리하는 것이 유지보수에 유리합니다.

아래는 루트 settings.gradle 예시입니다:

// settings.gradle
rootProject.name = 'my-service'

include(
    'app',
    'domain',
    'infra',
    'common'
)

루트 build.gradle에서 subprojects 블록을 활용하면 공통 설정을 한 곳에서 관리할 수 있습니다:

// build.gradle (루트)
plugins {
    id 'java'
    id 'org.springframework.boot' version '3.2.4' apply false  // 각 서브모듈에서 선택 적용
    id 'io.spring.dependency-management' version '1.1.4' apply false
}

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

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

    java {
        sourceCompatibility = JavaVersion.VERSION_21
    }

    repositories {
        mavenCentral()
    }

    dependencyManagement {
        imports {
            mavenBom "org.springframework.boot:spring-boot-dependencies:3.2.4"
        }
    }
}

포인트: org.springframework.boot 플러그인은 실행 가능한 JAR을 생성하므로 app 모듈에만 apply true로 적용합니다. 나머지 모듈에 적용하면 불필요한 Fat JAR이 생성됩니다.


각 모듈의 build.gradle 설정

모듈별 build.gradle에서는 해당 모듈이 실제로 필요한 의존성만 선언합니다.

domain 모듈 — Spring Data JPA를 사용하되 Web 계층 의존성은 포함하지 않습니다:

// domain/build.gradle
dependencies {
    implementation project(':common')          // 공통 모듈 참조

    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    runtimeOnly 'com.mysql:mysql-connector-j'

    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testRuntimeOnly 'com.h2database:h2'        // 테스트용 인메모리 DB
}

app 모듈 — 모든 모듈을 조합해 실행 가능한 애플리케이션을 구성합니다:

// app/build.gradle
plugins {
    id 'org.springframework.boot'  // 여기서만 apply
}

dependencies {
    implementation project(':domain')
    implementation project(':infra')
    implementation project(':common')

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

패키지 구조와 컴포넌트 스캔 주의사항

멀티모듈에서 자주 발생하는 문제 중 하나가 컴포넌트 스캔 범위입니다. app 모듈의 @SpringBootApplication은 기본적으로 해당 클래스의 패키지 하위만 스캔합니다.

// app/src/main/java/com/example/app/MyServiceApplication.java
@SpringBootApplication(
    scanBasePackages = "com.example"  // 모든 모듈의 공통 루트 패키지를 스캔
)
public class MyServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyServiceApplication.class, args);
    }
}

각 모듈의 패키지를 com.example.domain, com.example.infra공통 루트 패키지 하위로 통일하면 scanBasePackages 하나로 전체 모듈의 Bean을 인식할 수 있습니다.

JPA를 사용하는 경우 엔티티와 리포지토리도 별도로 스캔 경로를 지정해야 합니다:

// app 모듈의 JPA 설정 클래스
@Configuration
@EnableJpaRepositories(basePackages = "com.example.domain")  // 리포지토리 위치 명시
@EntityScan(basePackages = "com.example.domain")              // 엔티티 위치 명시
public class JpaConfig {}

맺음말

Spring Boot 멀티모듈 프로젝트는 초기 설정 비용이 들지만, 팀 규모와 코드베이스가 커질수록 그 가치가 분명해집니다. 핵심 설계 원칙을 정리하면 다음과 같습니다:

  1. 의존성 방향 단방향 유지app → infra → domain → common 순으로 흐르게 설계
  2. org.springframework.boot 플러그인은 app 모듈에만 적용 — 불필요한 Fat JAR 방지
  3. 공통 루트 패키지 통일 — 컴포넌트 스캔과 @EntityScan 설정 단순화
  4. 모듈별 최소 의존성 원칙 — 필요한 스타터만 선언해 빌드 시간 단축

멀티모듈 구조를 갖추고 나면 이후 도메인 단위 분리, 독립 배포, 또는 MSA 전환 시에도 낮은 비용으로 대응할 수 있습니다. 추가로 Gradle 공식 멀티프로젝트 문서Spring Boot 공식 멀티모듈 샘플을 함께 참고하면 더 다양한 설정 패턴을 확인할 수 있습니다.

반응형