Spring Boot Multi Module 적용하기

김성민·2023년 9월 8일
0

신입사원이 된지 어느덧 만4개월이 넘었다..
회사에선 자사 서비스를 운영하는데 프로젝트가 참 이상하게 구성돼있다.
한 프로젝트 안에 두개의 서비스가 운영되고있고 랜딩페이지나 이상한 잡다한것들이 많다.
그래서 나보고 한번 멀티모듈을 공부 해보라해서 공부하고 정리했다.

일단 멀티 모듈이란 말 그대로 모듈을 사용하는 것이다
모듈이라 함은 내가 이해하기론 여러곳에서 쓸 수 있는 함수
static같은 느낌 같았다.

먼저 우아한녀석들블로그에서 멀티 모듈을 정리한 글이 있어서 그 글을 참고했다. 링크는 맨 하단에 기입해두겠다.

Multi Module이란

하나의 단일 프로젝트를 여러 개의 모듈로 분리해서 구성하는 기법.

모듈간 의존성을 최소화하여 유연하고 확장성 있는 애플리케이션을 개발하기 위해 사용됨.

Multi Module 적용 전

서로 독립적인 프로젝트 단위를 가지고 있다. 이런 구조일 경우 큰 문제점은 시스템의 중심 Domain이 가져야 할 구조와 규칙 등을 동일하게 보장해주는 메커니즘이 없다.

이럴 경우 한 프로젝트에서 수정을 하면 다른 프로젝트에 복&붙하며 개발을 해야한다.

Multi Module 적용

그래서 Domain을 모듈화 하여 공통 코드를 모아두고 같이 쓸 수 있게 해주는 것이다.

실패한 Multi Module

공통으로 사용하는 코드들을 Common(공통)이라는 모듈에 담는다.

‘이제 수정 사항은 Common 모듈에서 하면 된다…’

공통(Common) 모듈의 저주

A 애플리케이션에서 기능을 추가한다. 개발자는 코드를 어디에 작성할지 고민 하게 되고, 공통 모듈에 점점 추가될 것이다. B 애플리케이션도 마찬가지 일 것이다.

이제 C 애플리케이션에서 기능을 추가한다. 공통 모듈에 유용해 보이는 코드들이 있다. 사용한다.

사실 이 기능은 A 애플리케이션을 위해 작성된 코드이다. 이 과정이 반복이 되면 공통 모듈은 점점 커지고 애플리케이션에서 하는 일들이 줄어들고 공통 모듈에서 더 많은 일을 하게 될 것이다.

  1. 스파게티 코드

A를 B로, B를 C로 가공한 코드에서 다시 C에서 A로 가공한 코드가 나온다. 예를 들어 아래와 같이

java
    class AService {
      private final ARepository aRepository;
    
      public A act(Long id) {
        ...
        return aRepository.findById(id);
      }
    }
    
    class BService {
      private final AService aService;
    
      public B act(Long id) {
        A a = aService.act(id);
        ...
        return mapToB(a);
      }
    }
    
    class CService {
      private final BService bService;
    
      public C act(Long id) {
        B b = bService.act(id);
        ...
        return mapToC(b);
      }
    }
    
    class DService {
      private final CService cService;
    
      //띠용!!
      public A act(Long id) {
        C c = cService.act(id);
        ...
        return mapToA(c);
      }
    }
  1. 의존성 덩어리

예를 들어 DB를 사용하지 않는 애플리케이션에서 공통 모듈을 사용하기 위해 데이터베이스와 커넥션을 맺게 된다. 그 외에 다른 의존성도 마찬가지다.

  1. 공통 설정

Thread Pool, Connection Pool, Timeout 등의 설정도 몰리게 된다.

대표적인 예로 DB 커넥션이 있다. 모든 DB에는 가질 수 있는 최대 커넥션 개수가 정해져 있다.


Multi Module 구성 Test

실행 환경

Spring Boot 3.1.3
JDK 17
Oracle - dev, Maria - local
JPA
Gradle 9.0 - Groovy
Intellij IDE

먼저 구조는 이렇게 나눴다.
api api2 core util
api - insert user
api2 - get user
core - entity, repository, config
util - dto

환경 구성

  1. https://start.spring.io/ 에서 spring project를 만든다.
  2. Intellij에서


모듈을 새로 만든다.

언어, 시스템빌드, JDK, Gradle 언어를 선택하고 모듈 이름을 작성한 후 생성

  1. root 프로젝트에서 settings.gradle을 확인하면

모듈을 생성하면 include ‘모듈명’ 이라고 생성 될 것이다. 생성이 안되면 직접 입력하면 된다.

  1. root 프로젝트 build.gradle

    plugins {
    	id 'java'
    	id 'org.springframework.boot' version '3.1.3'
    	id 'io.spring.dependency-management' version '1.1.3'
    }
    
    bootJar.enabled = false
    
    sourceCompatibility = 17
    targetCompatibility = 17
    
    tasks.named('test') {
    	useJUnitPlatform()
    }
    
    subprojects {
    	group = 'com.exam'
    	version = '0.0.1-SNAPSHOT'
    
    	apply plugin: 'java'
    	apply plugin: 'java-library'
    	apply plugin: 'org.springframework.boot'
    	apply plugin: 'io.spring.dependency-management'
    
    	repositories {
    		mavenCentral()
    		maven { url 'https://repo.spring.io/milestone' }
    		maven { url 'https://repo.spring.io/snapshot' }
    		maven { url 'https://maven.atlassian.com/3rdparty/'}
    	}
    
    	configurations {
    		compileOnly {
    			extendsFrom annotationProcessor
    		}
    	}
    
    	dependencies {
    		implementation 'org.springframework.boot:spring-boot-starter-web'
    		compileOnly 'org.projectlombok:lombok'
    		annotationProcessor 'org.projectlombok:lombok'
    		annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
    	}
    }
    
    project(':api') { // 컴파일 시 core 로딩
    	dependencies {
    		compileOnly project(':core')
    		compileOnly project(':util')
    	}
    }
    
    project(':api2') { // 컴파일 시 core 로딩
    	dependencies {
    		compileOnly project(':core')
    		compileOnly project(':util')
    	}
    }
    
    project(':core') { // core는 bootJar로 패키징 할 필요 없음
    	**bootJar** { enabled = false }
    	jar { enabled = true }
    }

subproject에 담긴 정의들이 하위 Module들에 대한 정의이다.

공통적으로 필요한 종속성들만 root - build.gradle에 정의한 후 필요에 따라 각 모듈들의 build.gradle에 종속성을 추가하면 된다.

예) api2 project에만 lucy 종속성을 추가하고 싶을 때

각 서비스에 필요한 모듈만 사용할 수 있다.

root - build.gradle


api, api2 - build.gradle

두 정의는 다르기 때문에 둘 다 기입해야 한다.

root 프로젝트의 dependencies 블록은 모든 하위 프로젝트 간의 공통 의존성을 관리하는 데 사용되며, api 프로젝트의 dependencies 블록은 해당 프로젝트 자체의 의존성을 정의하는 데 사용된. 따라서 이러한 코드 블록은 서로 다른 범위와 역할을 가지고 있으며, 다른 것으로 취급된다.

core - build.gradle

두 개의 서버가 실행되고 있다. 8080 / 8081

하나의 core 모듈로 두 서비스가 실행 가능함

api module Controller

User.java

Entity

Test.java

util Module

TestDto.java

util Module

테스트결과

api module 실행 결과


api2 module controller

api2 module 실행 결과

참고

https://dkswnkk.tistory.com/691

https://techblog.woowahan.com/2637/

https://hyeon9mak.github.io/woowahan-multi-module/#️-msa-멀티-모듈

이 글을 작성하며..

멀티 모듈 적용에 있어서 정답은 없는것 같다.
어떻게 나누고의 문제가 아닌 어떻게 사용하고의 문제인것이다.
위에서 정리한 스파게티코드, 의존성, 설정 같은 문제들이 일어나지 않게 잘.. 잘 작성하면 되지 않을까 싶다 ㅋㅋㅋ
아마 회사에서 멀티모듈 적용시키는것 그 외 여러 테스트(multi db connection, security..)가 끝나면 내 업무의 주가 거의 이것이 될것같다.
이직할때 참 도움이 될만한것같은 느낌이다.
싸구려 구조인 프로젝트 -> 멀티모듈 적용시키기..
주니어도 안되는 껍대기 수준 개발자가 이걸 했다면 과연 좋아는 할까.. 열심히 해야겠다.

profile
웹개발자

0개의 댓글