멀티 모듈(Multi-Module) 프로젝트 : 하나의 큰 프로젝트를 여러 개의 작은 모듈로 나누어 관리하는 구조
각 모듈들은 독립적으로 개발, 빌드가 될 수 있지만 서로 필요한 기능을 공유할 수가 있다.
각 모듈은 api 모듈 (비즈니스 로직), common 모듈(공통 유틸리티) 와 같이 하나의 역할만 담당한다.
단일 모듈은 전체 프로젝트를 한번에 빌드해야하고 멀티 모듈은 개별로 빌드 가능하다.
멀티 모듈은 보통 대규모 프로젝트에 많이 쓰이고 단일 모듈은 소규모 프로젝트에 많이 쓰인다. 그치만 멀티 모듈은 의존성 관리가 다소 복잡할 수 있다는 단점이 있다.
재사용성 : 공통 기능(common 모듈)을 다른 모듈에서 쉽게 재사용 가능
빠른 빌드 : 변경된 모듈만 빌드하면 되니 시간 절약
유지보수 용이 : 문제 생기면 해당 모듈만 수정
멀티 모듈 프로젝트 생성하기
루트 프로젝트 생성
루트 프로젝트는 하위 프로젝트의 관리하는 역할만 하고 따로 소스코드는 작성하지 않기 때문에 src 폴더 전체를 삭제해도 된다.
모듈 생성
루트 프로젝트에서 모듈들 생성을 한다.
위와 같이 모듈들을 생성하였다. board-api, user-api는 비즈니스 로직, common은 공통 유틸리티, gateway는 요청을 라우팅 해주는 모듈, discovery는 서비스 위치 등록하고 찾아주는 모듈 들을 생성해보았다.
Gateway : 클라이언트의 요청을 적절한 서비스로 전달하는 모듈. 인증/인가 등의 작업도 수행한다.
Discovery : 각 서비스의 위치(주소와 포트)를 등록하고 관리하는 역할을 수행하는 모듈. 서비스들을 Eureka 서버에 등록하면 다른 서비스나 Gateway가 Eureka를 통해 동적으로 해당 서비스를 찾을 수 있음.
각 모듈들의 공통된 의존성 주입은 루트 프로젝트의 build.gradle에서 설정할 수 있고 각 모듈에만 필요한 의존성이라면 해당 모듈의 build.gradle에서 의존성 주입을 해준다.
build.gradle과 src 파일만 있으면 해당 하위 모듈의 빌드를 수행할 수 있기 때문에 나머지 파일은 모두 삭제해도 된다.
하위 프로젝트들을 Unlink Gradle Project를 통해서 독립적인 빌드 환경으로 변경해준다.
루트 프로젝트에서 settings.gradle 설정
rootProject.name = 'multimodule'
include 'board-api'
include 'user-api'
include 'discovery'
include 'common'
include 'gateway'
루트 프로젝트의 settings.gradle에 하위 프로젝트를 포함시켜준다. 이렇게 되면 include를 통해 하위 모듈들을 다시 루프 프로젝트의 빌드 시스템에 포함되게 한다. 이 과정은 독립적인 Gradle 프로젝트에서 루트 프로젝트의 의존성 및 설정을 다시 상속받게 하는 과정이다.
각 모듈 설정 (build.gradle, application.yml)
server:
port: 8081
각 하위 모듈들의 application.yml 파일에서 포트 번호를 겹치지 않게 설정을 해줘야 하위 모듈들을 각각 실행해도 오류가 나지 않는다.
루트 프로젝트
plugins {
id 'java'
id 'org.springframework.boot' version '3.4.2'
id 'io.spring.dependency-management' version '1.1.7'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
bootJar.enabled = false
repositories {
mavenCentral()
}
subprojects {
compileJava {
sourceCompatibility = 17
targetCompatibility = 17
}
apply plugin: 'java'
apply plugin: 'java-library'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
repositories {
mavenCentral()
}
dependencies {
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
}
tasks.named('test') {
useJUnitPlatform()
}
}
게이트 웨이
build.gradle
group = 'com.example'
version = '0.0.1-SNAPSHOT'
ext {
set('springCloudVersion', '2024.0.0')
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
dependencies {
implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
implementation 'io.jsonwebtoken:jjwt-impl:0.11.5'
implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5'
}
application.yml
server:
port:
8080
eureka:
client:
service-url:
defaultZone: http://localhost:8001/eureka/
registerWithEureka: false
fetchRegistry: true
spring:
cloud:
gateway:
routes:
- id: user-api
uri: lb://user-api
predicates:
- Path=/user/**
- id: board-api
uri: lb://board-api
predicates:
- Path=/board/**
filters:
- JwtFilter
Discovery
build.gradle
group = 'com.example.discovery'
version = '0.0.1-SNAPSHOT'
ext {
set('springCloudVersion', '2024.0.0')
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
dependencies {
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-server'
}
application.yml
server:
port: 8001
spring:
application:
name: service-discovery
eureka:
server:
eviction-interval-timer-in-ms: 60000 # ???? ?? ?? (???: 60?)
renewal-percent-threshold: 0.85 # ?? ??? ?? (???: 0.85)
instance:
hostname: localhost
client:
registerWithEureka: false
fetchRegistry: false
user-api
build.gradle
group = 'com.example.user'
version = '0.0.1-SNAPSHOT'
repositories {
mavenCentral()
}
ext {
set('springCloudVersion', '2024.0.0')
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
dependencies {
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
implementation 'io.jsonwebtoken:jjwt-impl:0.11.5'
implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.mariadb.jdbc:mariadb-java-client'
}
application.yml
server:
port: 8082
eureka:
instance:
lease-renewal-interval-in-seconds: 10
lease-expiration-duration-in-seconds: 30
prefer-ip-address: true
client:
service-url:
defaultZone: http://localhost:8001/eureka/
registerWithEureka: true
fetchRegistry: true
spring:
application:
name: user-api
datasource:
url: ${DB_URL}
driver-class-name: org.mariadb.jdbc.Driver
username: ${DB_USER}
password: ${DB_PASSWORD}
jpa:
database-platform: org.hibernate.dialect.MariaDBDialect
hibernate:
ddl-auto: update
properties:
hibernate:
format_sql: true
logging:
level:
org.hibernate.SQL: debug
org.hibernate.orm.jdbc.bind: trace
board-api
build.gradle
group = 'com.example.board'
version = '0.0.1-SNAPSHOT'
repositories {
mavenCentral()
}
ext {
set('springCloudVersion', '2024.0.0')
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
dependencies {
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
implementation project(path: ':user-api')
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.mariadb.jdbc:mariadb-java-client'
}
application.yml
server:
port: 8081
eureka:
instance:
lease-renewal-interval-in-seconds: 10
lease-expiration-duration-in-seconds: 30
prefer-ip-address: true
client:
service-url:
defaultZone: http://localhost:8001/eureka/
registerWithEureka: true
fetchRegistry: true
spring:
application:
name: board-api
datasource:
url: ${DB_URL}
driver-class-name: org.mariadb.jdbc.Driver
username: ${DB_USER}
password: ${DB_PASSWORD}
jpa:
database-platform: org.hibernate.dialect.MariaDBDialect
hibernate:
ddl-auto: update
properties:
hibernate:
format_sql: true
logging:
level:
org.hibernate.SQL: debug
org.hibernate.orm.jdbc.bind: trace
'한화시스템 Beyond SW Camp > 백엔드' 카테고리의 다른 글
[Spring Boot] N+1 문제 (0) | 2025.02.24 |
---|---|
[Spring Boot] Gateway, Eureka (0) | 2025.02.23 |
[Spring Boot] Stomp를 이용한 채팅 시스템 구현 (0) | 2025.02.19 |
[Spring Boot] Kafka, Zookeeper (0) | 2025.02.18 |
[Spring Boot] 웹 소켓(Web Socket) (1) | 2025.02.18 |