1. 멀티 모듈이 왜 필요할까?
Spring Boot로 프로젝트를 개발하다 보면, 코드가 점점 커지면서 관리가 어려워지고 같은 코드를 여러 곳에서 중복해서 쓴다는 문제점이 있다. 이런 문제를 해결하기 위해 멀티 모듈 프로젝트를 사용하면 여러 서비스 모듈을 독립적으로 관리할 수 있다.
2. 멀티 모듈 프로젝트란?
Spring Boot에서 프로젝트를 개발할 때 단일 프로젝트 방식과 멀티 모듈 프로젝트 방식이 있다.
각 방식의 차이를 비교하고, 멀티 모듈이 필요한 경우와 실제 사용 사례를 살펴보자.
멀티 모듈 vs 단일 모듈
단일 모듈
- 서로 다른 프로젝트에서 공통된 코드가 사용된다면, 코드를 복붙해서 사용해야한다.
- 여러 프로젝트에서 사용하기 위해 IDE, 인스턴스를 N개 실행해야 한다.
- 하나의 수정에도 전체 프로젝트를 빌드해야 함
- 전체를 한 번에 배포
- 패키지끼리 의존성이 강해서 하나의 수정이 N개의 오류를 발생시킬 수 있다.
구성예제
my-project/
├── src/main/java/com/example
│ ├── controller/
│ ├── service/
│ ├── repository/
│ ├── entity/
│ ├── config/
├── src/main/resources/application.yml
├── build.gradle
멀티 모듈
- 코드의 재사용성이 높아진다.
- 여러 프로젝트를 모듈화 시켜 하나의 IDE에서 실행할 수 있다.
- 각각의 모듈과 패키지가 독립적인 역할을 하므로 의존성이 낮아진다.
- 한가지 기능의 수정으로 전체 프로젝트를 빌드할 필요가 없다. 수정한 기능 모듈만 리빌드 하면 된다.
구성예제
my-project/
├── core/ # 공통 모듈 (Entity, DTO, Service, Repository)
├── web/ # API 서버 모듈 (Controller, API)
├── admin/ # 관리자 페이지 API 모듈
├── gateway/ # API Gateway 모듈 (Spring Cloud Gateway)
├── build.gradle
├── settings.gradle
멀티 모듈 프로젝트의 장점
각 모듈이 독립적으로 개발 및 배포 가능
변경 사항이 있을 때 해당 모듈만 빌드 가능 -> 빌드 속도 향상
공통 기능을 여러 프로젝트에서 재사용 가능
종속성 충돌을 줄이고, 필요없는 라이브러리를 불필요하게 로드하지 않는다.
멀티 모듈를 사용하면 좋은 경우
1) MSA (Microservices Architecture) 또는 대규모 프로젝트
2) 공통 모듈을 여러 프로젝트에서 사용할 때
3) 팀 개발 시 모듈별 담당이 필요할 때
4) 빌드 속도를 최적화할 때
단점
1) 모듈 간의 의존성 관리가 복잡해질 수 있다.
2) 프로젝트 초기 설정이 어려워진다.
3) 공통 모듈이 너무 커지면 오히려 복잡해지고 스파게티 코드가 될 수 있다.
3. 멀티 모듈 프로젝트 생성
1) 루트 프로젝트 생성
루트 프로젝트는 하위 모듈 관리만 담당하므로 src 폴더는 지워도 괜찮다.
2) 모듈 추가
하위 모듈은 core, web, admin, gateway를 생성
모두 gradle 프로젝트로 생성
core 모듈은 다른 모든 모듈에서 공통으로 사용하는 모듈이다.
4. Gradle 설정 및 의존성 관리
모듈을 추가하면 루트 프로젝트의 setting.gradle에 하위 프로젝트를 포함시켜야 한다.
settings.gradle
rootProject.name = 'demo1'
include 'core'
include 'web'
include 'admin'
include 'gateway'
루트 프로젝트의 build.gradle에서 하위 모듈의 gradle 세팅을 모두 할 수도 있고 각 모듈에서 build.gradle을 따로 세팅해도 된다.
그리고 우측 코끼리 모양 Gradle에서 새로운 모듈의 Gradle 연결을 해제해야 제대로 루트 프로젝트 밑으로 들어간다.
그리고 생성한 모듈의 src랑 build.gradle만 남기고 필요없는건 삭제해주자.
또 각 모듈의 application.yml에서 포트번호도 수정해줘야한다.
root 모듈
build.gradle
plugins {
id 'java'
id 'org.springframework.boot' version '3.4.2'
id 'io.spring.dependency-management' version '1.1.7'
}
repositories {
mavenCentral()
}
subprojects{
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
group = 'com.example'
version = '0.0.1-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
compileOnly 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
}
bootJar {
enabled = false
}
jar {
enabled = true
}
test {
useJUnitPlatform()
}
core 모듈
공통기능을 관리하는 모듈로, 다른 모듈에서 가져다가 쓴다.
dependencies {
// Spring Data JPA
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
// Database (MariaDB)
runtimeOnly 'org.mariadb.jdbc:mariadb-java-client'
}
web 모듈
core 모듈을 사용하여 엔티티, 의존성 등을 활용한다.
dependencies {
// core 모듈 의존성 추가
implementation project(':core')
// Spring Boot Web
implementation 'org.springframework.boot:spring-boot-starter-web'
// Validation
implementation 'org.springframework.boot:spring-boot-starter-validation'
}
gateway 모듈
클라이언트의 요청을 적절한 서버로 라우팅한다.
ext {
set('springCloudVersion', "2024.0.0")
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
implementation 'io.jsonwebtoken:jjwt-api:0.12.6'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.6'
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
gateway 모듈의 application.yml 설정
server:
port: 8080
eureka:
client:
service-url:
defaultZone: http://localhost:8001/eureka // eureka 없으면 지우기
register-with-eureka: true
fetch-registry: true
spring:
application:
name: gateway
cloud:
gateway:
routes:
- id: web
uri: lb://web
predicates:
- Path=/web/**
filters:
- JwtAuthenticationFilter // 해당경로에 필터 추가
6. 발생했던 문제 & 해결 방법
문제 1. 다른 모듈의 Bean을 사용하고 싶은데 ComponentScan이 안됨
- 해결법: ComponentScan("com.example")로 바꾸어서 스캔 범위를 넓힘
7. 정리
멀티 모듈을 도입하면 큰 프로젝트에서 유지보수가 쉬워지고 확장성이 높아진다.
'Spring' 카테고리의 다른 글
낙관적 락과 비관적 락 (0) | 2025.02.26 |
---|---|
Spring Boot에서 Kafka 설정하기! (0) | 2025.02.19 |
Spring Boot에서 웹소켓(WebSocket) 사용하기 (0) | 2025.02.17 |
Spring Boot에서 Presigned URL을 사용한 S3 업로드 (0) | 2025.02.14 |
[Spring] Spring Boot에서 OAuth 사용 (0) | 2025.02.13 |