멀티 모듈
이란 서로 독립적인 프로젝트를 하나의 프로젝트로 묶어 모듈로서 사용되는 구조를 말합니다. 멀티 모듈
을 사용하면 공통적인 기능을 모아 하나의 모듈로 만드는 것이 가능합니다. 공통적으로 사용해야하는 util
, domain
, Repository
등을 모듈로 분리해서 사용할 수 있습니다.
Gradle을 사용해 멀티 모듈 프로젝트를 생성하겠습니다.먼저 모듈을 모을 Gradle 프로젝트(루트)를 생성합니다.
Name은 자유롭게, 그리고 Group명을 dev.be
로, JDK와 Java는 11버전을 선택하였습니다. 만약 Group명을 다음과 같이 지정해주었다면 모듈에서도 Group명을 지정한대로 해야 합니다. 그리고 Spring Boot는 2.7.1
을 선택하였습니다. 여기서 Dependency를 선택할 수 있는 항목들이 있는데, 모듈을 모을 루트 프로젝트이기 때문에 선택할 필요가 없습니다.
프로젝트가 아래의 사진과 같이 생성된 것을 알 수 있습니다.
루트에서 필요없는 src 폴더를 날립니다. (모듈 안의 src 경로의 파일을 읽으므로 모듈을 모아주는 프로젝트 폴더에서는 필요가 없습니다.) 그리고 build.gradle
에 아래와 같이 입력합니다.
plugins {
id 'org.springframework.boot' version '2.7.1'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'dev.be'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('test') {
useJUnitPlatform()
}
subprojects {
apply plugin: 'java-library'
}
build.gradle에서
api 키워드를 사용하기 위해선 2가지 중 1가지 방법으로 선언을 해야 합니다.
id 'java-library'
추가 subprojects {
apply plugin: 'java-library'
}
상위 루트에서 새로운 모듈을 생성합니다. 이름은 module-api
, module-common
으로 명명하겠습니다. 각각 하위의 모듈은 상위 프로젝트의 Group이름, Java 및 SDK 버전과 Spring Boot 버전이 같아야 합니다. 그리고 모듈에서 Lombok
과 Spring Web
이라는 Dependency를 선택하고 생성합니다.
그리고 하위 모듈에는 gradlew
, gradlew.bat
, HELP.md
, settings.gradle
이 필요가 없기에 과감히 날려줍니다. 만약 남아있다면 해당 모듈을 실행할 때 해당 파일을 우선으로 읽기 때문에 에러가 발생합니다.
추가로 모듈에는 resource폴더 안에 static과 template가 있는데, 필요가 없으므로 삭제, 그리고 properties를 yml
로 변경해줍니다.
그러면 아래와 같은 구조가 됩니다.
그리고 루트의 setting.gradle을 보면 방금 생성한 module-common
과 module-api
가 include
되어있는 것을 확인할 수 있는데, 이는 하위 모듈로 선언한다는 의미입니다. 없다면 include해줍니다. 그리고 gradle build를 다시 해줍니다.
rootProject.name = 'multi-module-test'
include 'module-api'
include 'module-common'
그리고 IntelliJ의 Gradle Tab에서도 module-api
와 module-common
이 하위에 존재하는 것을 확인할 수 있습니다.
module-api
의 build.gradle
은 module-common
에 관해 다음과 같이 아무런 의존성을 넣지 않았습니다.
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
root프로젝트의 build.gradle
에 subprojects
에 관해 설정해줍니다. api 키워드를 사용하기 위해선 2가지 중 1가지 방법으로 선언을 해야 합니다.
id 'java-library'
추가subprojects {
apply plugin: 'java-library'
}
만약 subprojects
에 관해 설정해주지 않으면 root 프로젝트의 api 키워드를 사용할 수 없게 되며 아래의 에러가 발생합니다.
해당 모듈에서는 Member
를 저장하기 위한 domain
과 repository
에 관해 다룹니다.
domain
의 코드는 아래와 같습니다.
// dev.be.modulecommon.domain.Member
package dev.be.modulecommon.domain;
import lombok.*;
import javax.persistence.*;
@Getter
@Entity
@Builder
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String name;
}
repositories
의 코드는 다음과 같습니다.
//dev.be.modulecommon.repositories.MemberRepository
package dev.be.modulecommon.repositories;
import dev.be.modulecommon.domain.Member;
import org.springframework.data.jpa.repository.JpaRepository;
public interface MemberRepository extends JpaRepository<Member, Long> {
}
우선 module-common
에서의 domain과 repository를 extends를 하기 위해 module-api의 build.gradle의 dependencies에서 다음과 같이 입력합니다. 이 때 선언하는 모듈의 이름은 root project에서 settings.gradle에 선언한 값과 같아야 합니다.
dependencies {
.
.
.
implementation project(':module-common')
}
module-api에서는 controller
, 예외처리
, response
와 service
에 대해 다룹니다. 코드는 제 깃헙에 있습니다.
해당 코드를 넣고 실행해보면 Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.
에러가 발생합니다. 당연히 데이터베이스를 이용하겠다고 했는데, 데이터베이스에 관한 설정이 없기 때문입니다.
resource를 보면 application.yml
이 있습니다. 여기서 Multi Profile을 만들어 환경별로 다른 Property 값을 정의해보겠습니다.
Environment별(예를 들어 local
, beta
, prod
등)로 Property 값을 정의하는 것을 말합니다. 프로젝트 시작 시 사용하려는 profile을 지정할 수 있는데, 예를 들어 local로 프로젝트를 실행하면 알아서 local의 property의 값들을 읽어와서 사용합니다.
application-beta
과 application-local
로 베타버전과 로컬 환경의 Property를 지정합니다.
profile-name: "beta"
spring:
main:
allow-bean-definition-overriding: true
datasource:
url: ${DB_URL}
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
database: mysql
database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
hibernate:
ddl-auto: create # or `none` # beta나 실제 서비스에선 none으로 둘 것
properties:
hibernate:
show_sql: true # to System OutDataSourceConfiguration
format_sql: true
use_sql_comments: true
jdbc:
time_zone: Asia/Seoul # @CreatedDate 필드의 Time Zone 값 설정
환경변수를 설정한 후에 IntelliJ가 profile을 읽을 수 있도록 지정해주어야 합니다.
사진과 같이 Run/Debug Configurations
에서 Environment variables를 넣어주고, Active profiles에 local
혹은 beta
를 입력해줍니다. profile 이름은 application-**
이지만 이는 Spring에서 처리해줍니다.
그리고 서버를 실행하면 문제없이 작동하는 것을 확인할 수 있습니다. curl
명령어를 통해 요청을 보내면 리스폰스가 잘 오는 것또한 확인할 수 있습니다.