예를 들어서 회원 프로그램을 개발한다고 했을 때 여러 개의 서버가 필요하다.
① batch 서버
② API 서버
등등
하지만 해당 서버들을 단일 프로젝트로 만들게 되면 다음과 같은 문제가 생긴다.
첫번째, 공통적으로 처리해야하는 코드의 처리
: 아예 분리되어 있는 프로젝트이다 보니 공통되는 코드들은 각 프로젝트에 복붙해서 사용할 수 밖에 없고, 한 파일의 코드가 수정되는 경우 다른 프로젝트의 코드도 수정해줘야 한다.
두번째, 접근성 문제
: 프로젝트 수에 따라 IDE 를 실행시켜야 하고 ,, 프로젝트 수가 많아지면 많아 질 수록 개발하는데 어려움이 동반 될 수 밖에 없다.
이런 문제를 해결하기 위해선 멀티 모듈 프로젝트를 사용하면 된다.
위에서 예시를 들었던 회원 프로그램을 예시로 들자면 위의 그림과 같다.
batch-server module 엔 배치 서버 관련 코드
api-server module 엔 API 관련 코드들이 있다.
두 모듈에서 공통되는 코드는 commons 모듈에 있고 batch-server module 과 api-server module 에서 의존성을 설정해서 사용하는 방식으로 멀티 모듈 프로젝트를 구성할 수 있다.
예제에서는 Gradle, 자바 버전은 1.8 로 프로젝트를 생성해주겠다.
프로젝트 생성을 하게 되면 위와 같은 구조가 되는데 우리는 하나의 프로젝트에 여러개의 모듈이 위치해 있고 공통 코드 같은 경우도 따로 모듈에 들어갈 거기 때문에 src 폴더는 삭제해준다.
➡️ 루트 프로젝트는 하위 모듈을 관리하는 역할 만 하 때문에 src 폴더를 삭제해도 괜찮다.
가장 상위 프로젝트에서 마우스 오른쪽 클릭을 하고, New > Module 을 선택 후, 모듈 이름을 입력해서 모듈을 만들어준다.
해당 프로젝트에서는 velog-api-server, velog-batch-server, velog-core 로 총 3개를 만들어줬다.
velog-api-server : API 관련 모듈
velog-batch-server : Batch 성 관련 모듈
velog-core : velog-api-server, velog-batch-server 에서 사용하는 공통 모듈
setting.gradle 에 새로 만들어진 모듈에 대해서 코드를 추가해줘야 하는데, New > Module 을 통해서 모듈을 만든 경우에는 인텔리제이가 자동으로 코드를 넣어준다.
만일 코드가 없는 경우에는 include #{모듈명}
으로 추가해주자.
buildscript {
ext {
springBootVersion = '2.6.2'
}
repositories {
mavenCentral()
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}"
classpath "io.spring.gradle:dependency-management-plugin:1.0.11.RELEASE"
}
}
// 하위 모든 프로젝트 공통 세팅
subprojects {
apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
group 'org.example'
version '1.0-SNAPSHOT'
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
compileJava.options.encoding = 'UTF-8'
repositories {
mavenCentral()
}
// 하위 모듈에서 공통으로 사용하는 세팅 추가
dependencies {
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
}
test {
useJUnitPlatform()
}
}
project(':velog-core') {
// 공통 코드
bootJar { enabled = false } // core 은 bootJar 로 패키징 할 필요 없음
jar { enabled = true }
dependencies {
}
}
project(':velog-api-server') {
bootJar { enabled = true }
jar { enabled = false }
dependencies {
compile project(':velog-core') // 컴파일 시 velog-core project 로딩
implementation 'org.springframework.boot:spring-boot-starter-web'
}
}
project(':velog-batch-server') {
bootJar { enabled = true }
jar { enabled = false }
dependencies {
compile project(':velog-core') // 컴파일 시 velog-core project 로딩
}
}
subprojects
: setting.gradle 에 include 된 프로젝트를 전부 관리한다.
: rootproject 까지 적용시키고 싶은 경우 allprojects 로 등록하면 된다.
project
: 하위 프로젝트의 의존성 관리한다.
: velog-core 의 경우 공통 모듈이기 때문에 velog-api-server, velog-batch-server 에 의존성을 주입해준다.
위와 같이 설정을 해준다음 velog-api-server 가 velog-core 에 대한 의존성을 가지고 있는지 확인하려면 Gradle > Dependencies > compileClasspath 를 확인해주면 된다.
velog-api-server 와 velog-batch-server 에 velog-core 를 정상적으로 의존성을 가지고 있는 걸 확인할 수 있다 🙂
① velog-api-server :: API 서버 모듈
api 서버 모듈 에 아래 파일을 생성해준다.
build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
}
resources > application.yml
server:
port: 8000
: 서버 포트번호를 8000 으로 설정해준다.
: 원하는 포트번호가 있는 경우 해당 포트 번호로 설정해주면 된다.
com.soyeon.velog > ApiApplication.java
package com.soyeon.velog;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ApiApplication {
public static void main(String[] args) {
SpringApplication.run(ApiApplication.class, args);
}
}
: 패키지를 만들고 ApiApplication 파일을 만들어준다.
: @SpringBootApplication 어노테이션을 통해 웹 어플리케이션이 실행 될 수 있도록 한다.
Your ApplicationContext is unlikely to start due to a @ComponentScan of the default package. 오류가 발생하는 경우
➡️ 패키지를 지정하지 않아 발생하는 오류로 패키지를 만들고 그 안에 Application 코드를 넣어주자.
② velog-batch-server :: 배치 서버 모듈
build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
}
resources > application.yml
server:
port: 8001
: 포트 번호를 8081로 설정해준다.
: 원하는 포트 번호가 있다면 그걸로 지정해주되, velog-api-server 와는 다르게 지정해준다.
com.soyeon.velog > BatchApplication
package com.soyeon.velog;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class BatchApplication {
public static void main(String[] args) {
SpringApplication.run(BatchApplication.class, args);
}
}
: 패키지를 만들고 BatchApplication 파일을 만들어준다.
: @SpringBootApplication 어노테이션을 통해 웹 어플리케이션이 실행 될 수 있도록 한다.
③ 서버 정상작동 확인
ApiApplication 혹은 BatchApplication 을 실행시켜준다.
velog-api-server url 👉 localhost:8000
velog-batch-server url 👉 localhost:8001
예제 그대로 따라했다면 위의 url로
혹은 포트를 수정했다면 수정한 포트로 접속하면 정상 작동을 확인 할 수 있다.
아직 만든 API 가 없기 때문에 각각의 url 로 접속했을 때 아래와 같은 화면이 뜬다면 서버가 정상적으로 실행된 것이다.
velog-core 에 com.soyeon.velog.entity 라는 패키지를 만들고 가상으로 Member entity 를 만들어보자. 코드는 아래와 같다.
com.soyeon.velog.entity > Member.class
package com.soyoen.velog.entity;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
public class Member {
String name;
int age;
}
: Member 는 이름과 나이를 가지고 있다고 가정한다.
: @Getter, @Setter, @ToString 은 롬복 어노테이션으로 get 메서드, set 메소드, toString 메서드를 자동 생성해준다.
velog-api-server 에 ApiController 를 만들어주고 Member 객체를 사용해보자.
velog-api-server 는 velog-core 에 의존성을 가지고 있기 때문에 Member 객체를 사용할 수 있다.
package com.soyeon.velog.controller;
import com.soyoen.velog.entity.Member;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ApiController {
@GetMapping("/test")
public String test() {
Member member = new Member();
member.setAge(10);
member.setName("이름");
return member.toString();
}
}
: import 된 값인 import com.soyoen.velog.entity.Member;
와 위의 사진을 보면 velog-core 에 있는 클래스를 import 해서 사용하고 있다는 걸 확인할 수 있다.
API 로 접속해봤을 때에도 정상적으로 결과가 나오는 걸 확인할 수 있다.
해당 멀티모듈은 공부를 할 때에도 유용하게 사용할 수 있다.
나의 경우에는 velog 예제 프로젝트를 관리할 때 멀티모듈을 사용하는데 하나의 큰 관심사에 여러 프로젝트를 한번에 관리할 수 있는 점이 좋다 👻
velog 예제 프로젝트 👉 https://github.com/soyeon207/velog_example
멀티모듈 설계 이야기 with Spring, Gradle
Gradle 멀티 프로젝트 관리
[스프링] 멀티 모듈(Multi Module) 개념/예제 feat. Gradle
[IntelliJ] 멀티 모듈 프로젝트 만들기
Gradle을 이용한 멀티 모듈
멀티모듈에 대해서 공부하고 있었는데 많은 도움 되었습니다 :) 감사합니다 !