Spring Multi Module

김기현·2022년 7월 17일
0
post-thumbnail

Multi Module이란

멀티 모듈이란 서로 독립적인 프로젝트를 하나의 프로젝트로 묶어 모듈로서 사용되는 구조를 말합니다. 멀티 모듈을 사용하면 공통적인 기능을 모아 하나의 모듈로 만드는 것이 가능합니다. 공통적으로 사용해야하는 util, domain, Repository 등을 모듈로 분리해서 사용할 수 있습니다.

Multi Module Root 생성

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가지 방법으로 선언을 해야 합니다.

  1. api를 사용하려는 build.gradle의 plugins에 id 'java-library' 추가
  2. 상위 모듈(=root)에서 'java-library' 추가
        subprojects {
            apply plugin: 'java-library'
        }

새로운 모듈 생성

상위 루트에서 새로운 모듈을 생성합니다. 이름은 module-api, module-common으로 명명하겠습니다. 각각 하위의 모듈은 상위 프로젝트의 Group이름, Java 및 SDK 버전과 Spring Boot 버전이 같아야 합니다. 그리고 모듈에서 LombokSpring Web이라는 Dependency를 선택하고 생성합니다.

그리고 하위 모듈에는 gradlew, gradlew.bat, HELP.md, settings.gradle이 필요가 없기에 과감히 날려줍니다. 만약 남아있다면 해당 모듈을 실행할 때 해당 파일을 우선으로 읽기 때문에 에러가 발생합니다.

추가로 모듈에는 resource폴더 안에 static과 template가 있는데, 필요가 없으므로 삭제, 그리고 properties를 yml로 변경해줍니다.

그러면 아래와 같은 구조가 됩니다.

그리고 루트의 setting.gradle을 보면 방금 생성한 module-commonmodule-apiinclude되어있는 것을 확인할 수 있는데, 이는 하위 모듈로 선언한다는 의미입니다. 없다면 include해줍니다. 그리고 gradle build를 다시 해줍니다.

rootProject.name = 'multi-module-test'
include 'module-api'
include 'module-common'

그리고 IntelliJ의 Gradle Tab에서도 module-apimodule-common이 하위에 존재하는 것을 확인할 수 있습니다.

module-apibuild.gradlemodule-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'
}

subprojects 설정

root프로젝트의 build.gradlesubprojects에 관해 설정해줍니다. api 키워드를 사용하기 위해선 2가지 중 1가지 방법으로 선언을 해야 합니다.

  1. api를 사용하려는 build.gradle의 plugins에 id 'java-library' 추가
  2. 상위 모듈(=root)에서 'java-library' 추가
subprojects {
	apply plugin: 'java-library'
}

만약 subprojects에 관해 설정해주지 않으면 root 프로젝트의 api 키워드를 사용할 수 없게 되며 아래의 에러가 발생합니다.

module-common 설정

해당 모듈에서는 Member를 저장하기 위한 domainrepository에 관해 다룹니다.

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-api 설정

우선 module-common에서의 domain과 repository를 extends를 하기 위해 module-api의 build.gradle의 dependencies에서 다음과 같이 입력합니다. 이 때 선언하는 모듈의 이름은 root project에서 settings.gradle에 선언한 값과 같아야 합니다.

dependencies {
    .
    .
    .	
    implementation project(':module-common') 
}

module-api에서는 controller, 예외처리, responseservice에 대해 다룹니다. 코드는 제 깃헙에 있습니다.

해당 코드를 넣고 실행해보면 Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.에러가 발생합니다. 당연히 데이터베이스를 이용하겠다고 했는데, 데이터베이스에 관한 설정이 없기 때문입니다.

resource를 보면 application.yml이 있습니다. 여기서 Multi Profile을 만들어 환경별로 다른 Property 값을 정의해보겠습니다.

Multi Profile

Environment별(예를 들어 local, beta, prod 등)로 Property 값을 정의하는 것을 말합니다. 프로젝트 시작 시 사용하려는 profile을 지정할 수 있는데, 예를 들어 local로 프로젝트를 실행하면 알아서 local의 property의 값들을 읽어와서 사용합니다.

application-betaapplication-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명령어를 통해 요청을 보내면 리스폰스가 잘 오는 것또한 확인할 수 있습니다.

profile
피자, 코드, 커피를 사랑하는 피코커

0개의 댓글