본문 바로가기
개인공부

Multimodule(멀티모듈) 개념 및 사용법

by 리승우 2023. 4. 14.

무엇인가?

  • 하나의 프로젝트를 여러 개의 작은 모듈로 나누어서 개발하고 관리하는 방식
  • 각각의 모듈은 독립적으로 개발 및 빌드될 수 있으며, 다른 모듈과 의존성을 가질 수 있음 ⇒ 애플리케이션에서 공통으로 사용하는 것을 공통 모듈로 분리하여 사용가능
  • 패키지 집합체 (관련된 패키지와 리소스들을 재사용할 수 있는 그룹)

 

왜 쓰는가?

상황 가정

회원시스템을 단일 프로젝트로 개발한다고 하면 아래와 같이 서로 독립된 프로젝트 단위가 있을 것이다

  • member internal api
  • member external api
  • member batch

이런 구조를 가지고 있을 때 가장 큰 문제점은 시스템의 중심 Domain 이 가져야할 구조와 규칙 등을  동일하게 보장해주는 메커니즘 이 없다는 것이다.

개발자는 매우 번거롭게, 동일한 Domain 을 가지고 있는 위 3가지 어플리케이션을 열심히 복&붙하며 개발을 하게될 것이다

그런데 멀티 모듈 프로젝트는 기존의 단일 프로젝트를 프로젝트 안의 모듈로서 갖을 수 있을 수 있는 구조를 제공한다

그래서 개발자는 하나의 시스템에서 중심 도메인을 모듈로 분리하여 위와 같은 보장 메커니즘을 제공받을 수 있게 된다

그러면 아래와 같은 생각이 결과로 나온다.

"아! 멀티 모듈 프로젝트는 공통으로 사용하는 코드들을 모아놓고 같이 쓸 수 있게 해주는구나!"

특징

  • 공통적인 코드 처리 가능
  • 휴먼 에러 방지

장점

재사용성

  • 도메인 구조와 규칙에 대한 동일성을 보장한다.
  • 필요한 기능을 다른 모듈에서 가져다 사용할 수 있다.
  • 중복 코드를 최소화한다.

생상성 향상

  • 모듈별로 기능을 분리하기 때문에 기능이 영향을 끼치는 범위를 예측할 수 있다.
  • 전체 프로젝트에 대한 이해도가 필요 없다.
  • 도메인 모델이 어떤 Controller, Service로 이어지는 지 알 필요 없다.
  • 버그나 변경이 발생했을 때 프로젝트 전체를 빌드할 필요없이 해당 모듈만 빌드 재배포한다. (빌드 시간 단축)

의존성 최소화

  • 결합도가 낮아서 변경에 따른 영향력을 최소화할 수 있다.

구조 변경 용이

  • MSA(Microservice Architecture)로 구조 변경 시 각 모듈을 개별 서버로 만들기만 하면 된다.
  • Monolithic Architecture로 변경이 쉽다.

접근성

  • IDE를 프로젝트마다 실행하지 않아도 된다

단점

설정 파일과 패키지 의존성 관리가 필요하다.

  • 공통 모듈 사용에 주의해야 한다.
  • 참조하는 모듈에서 불필요한 의존성으로 인해 어플리케이션이 무거워질 수 있다.

 

어떻게 환경을 구성하나?

1. 기본 프로젝트를 만든 뒤, 루트 프로젝트의 src 폴더와 같은 구성 지움

(해당 루트 프로젝트는 하위 모듈을 관리하는 역할만 주어질테니)

 

2. Root 모듈 클릭 > New > Module…을 통해 다른 모듈(프로젝트) 생성

* 필자는 Common, member, order 모듈을 생성함

 

3. 하위 모듈에서 src를 제외한 나머지들은 모두 삭제

* 필자는 하위 모듈의 관리를 Root 모듈의 build.gradle에서 모두 할 것이기 때문에 이와 같이 진행하였음

* Common 모듈은 서버를 돌리는 용도로 만든 모듈이 아니기에 Application.java파일도 삭제하였음

 

4. Root 프로젝트의 setting.gradle에서 생성한 모듈듈을 include 진행

 

5. Root 프로젝트의 build.gradle 세팅

// Gradle 플러그인을 추가하는 데 사용됩니다. 플러그인은 빌드를 위한 추가 기능이나 도구입니다.
plugins {
    id 'java'
    id 'org.springframework.boot' version '3.0.6'
    id 'io.spring.dependency-management' version '1.1.0'
}

// 이 코드 블록 내에서 정의된 설정은 모든 프로젝트에서 적용됩니다.
allprojects {
    // Gradle 빌드 시스템이 생성하는 라이브러리나 실행 가능한 파일의 그룹을 정의합니다.
    group = 'com.example'
    // Gradle 빌드 시스템이 생성하는 라이브러리나 실행 가능한 파일의 버전을 정의합니다.
    version = '0.0.1-SNAPSHOT'
    // 컴파일러가 사용할 소스 코드의 호환성을 정의합니다.
    sourceCompatibility = '17'

    // Gradle 빌드 시스템에서 사용할 리포지토리를 정의합니다.
    // 이 코드에서는 mavenCentral() 메서드를 통해 Maven Central 리포지토리를 사용합니다.
    repositories {
        mavenCentral()
    }
}

// 이 코드 블록 내에서 정의된 설정은 하위 프로젝트에서 적용됩니다.
// 이 코드 블록은 allprojects와 유사하지만 하위 프로젝트에 대해서만 적용됩니다.
// 이 때 plugins 블록은 사용이 불가능하여 apply plugin을 사용해야함
// core plugin 외의 community plugin의 버전은 앞서 plugins 블록에 선언한 버전을 따라감
subprojects {
    apply plugin: 'java'
    apply plugin: 'org.springframework.boot'
    apply plugin: 'io.spring.dependency-management'

    configurations {
        compileOnly {
            extendsFrom annotationProcessor
        }
    }

    // Gradle 빌드 시스템에서 사용할 의존성을 정의합니다.
    // 의존성은 라이브러리나 외부 모듈을 사용하기 위한 설정입니다.
    dependencies {
        implementation 'org.springframework.boot:spring-boot-starter-web'
        testImplementation 'org.springframework.boot:spring-boot-starter-test'
        annotationProcessor 'org.projectlombok:lombok'
        annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
        compileOnly 'org.projectlombok:lombok'
        testImplementation 'org.springframework.boot:spring-boot-starter-test'
        testCompileOnly 'org.projectlombok:lombok'
        testAnnotationProcessor 'org.projectlombok:lombok'
    }

    // Gradle 빌드 시스템에서 사용할 작업(task)을 정의합니다.
    // 이 코드에서는 test 작업에 useJUnitPlatform() 메서드를 적용하여 JUnit 테스트를 수행합니다.
    tasks.named('test') {
        useJUnitPlatform()
    }
}

// 하위 프로젝트별 개별설정 가능함 (모듈별 설정가능)
project(":member") {
    bootJar {
        enabled = true
    }

    jar {
        enabled = true
    }

    // 해당 모듈에만 필요한 라이브러리가 발생하면 이곳에 추가
    dependencies {
        // 해당 문구를 적음으로써 Common project, order project 로딩
        implementation project(':Common')
        implementation project(':order')
    }
}

project(":order") {
    bootJar {
        enabled = true
    }

    jar {
        enabled = true
    }

    dependencies {

    }
}

project(":Common") {
    /*
		스프링 부트 기반의 멀티 모듈 프로젝트를 구축할 때, Build를 실행하면 자동으로
		bootJar 태스크를 실행함(스프링 부트 플러그인에 이런 설정이 포함되어 있음)

    bootJar는 실행가능한 jar를 만들려 하기 때문에 main()이 필요함
    그렇기 때문에 main()이 없는 Common은 enabled를 false로 해줘야
    결론적으로 저걸 넣지 않으면 추후 Common에 있는 Bean Class를 다른 모듈에서 사용할 때 에러가 발생할 수 있음.
    */
    bootJar {
        enabled = false
    }

    jar {
        enabled = true
    }

    dependencies {

    }
}

 

6. 빌드 후 의존성 확인

> 우측 gradle 탭에 설정한 대로 dependencies가 추가되었는지 확인해본다

 

- Root 프로젝트의 하위 모듈로 Common, member, order가 잘 들어가있음

- member 프로젝트 관리에서 Common, order 프로젝트를 implementation 했기 때문에 member의 dependencies로 Common, order 프로젝트가 추가되었음

 

7. 마음껏 사용

> 멀티모듈 환경 구성이 완료되었습니다

'개인공부' 카테고리의 다른 글

헥사고날 아키텍처 개념 및 사용법  (0) 2023.05.05
Dockerfile, Docker-compose 특징 및 차이  (0) 2023.04.18
jib란?  (0) 2023.04.04
java record 개념 및 사용법  (0) 2023.03.23
2023.03.18 TIL  (0) 2023.03.18

댓글