본문 바로가기
SpringBoot/환경설정

SpringBoot 멀티 모듈(Multi Modlue) 설정

by 띵앤띵 2022. 4. 14.
728x90
반응형

- 개발 환경 

1. IntelliJ

2. MAC

3. Gradle

4. SpringBoot

 

멀티 모듈 이란?

멀티 모듈이란 서로 독립적인 프로젝트(인증, 어플리케이션)를 하나의 프로젝트로 묶어 모듈로서 사용되는 구조를 말한다.

멀티 모듈을 사용하면 공통적인 기능을 모아 하나의 모듈로 만드는 것이 가능하다. 즉, 인증과 어플리케이션에서 공통으로 사용하는 util, domain, Repository등을 모듈로 분리해 사용할 수 있는 것이다.

멀티 모듈에 관련해 더 자세하게 알고 싶은 경우 아래 글을 참고하길 바란다.

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


멀티 모듈 간단 예제

이제 간단하게 Gradle을 사용해 멀티 모듈 프로젝트를 만들어보자.

먼저 모듈들을 모을 Gradle 프로젝트를 하나 만들어주자.

MacOS IntelliJ 기준 File→New→Project 경로에 위의 사진처럼 Gradle 프로젝트를 하나 생성한다.

그럼 위와 같은 구조의 프로젝트가 생성된다. 일단 모듈을 담을 프로젝트이기때문에 src폴더가 필요없기 때문에 src폴더는 삭제해주자 그리고 build.gradle 아래와 같이 입력한다.

// Gradle 이 빌드되기전 실행되는 설정
buildscript {
    ext {
        springBootVersion = '2.4.3'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
        classpath "io.spring.gradle:dependency-management-plugin:1.0.11.RELEASE"
    }
}

// 현재의 root 프로젝트와 앞으로 추가될 서브 모듈에 대한 설정
allprojects {}

// 전체 서브 모듈 에 해당되는 설정
subprojects {

//    gradle 6버전 에서는 'java' 이지만 7버전 에서는 'java-library' 이다
//    apply plugin: 'java'
	apply plugin: 'java-library'
    apply plugin: 'org.springframework.boot'
    apply plugin: 'io.spring.dependency-management'

    group = 'kr.multi.ex'
    version = '1.0'
    sourceCompatibility = '11'

    repositories {
        mavenCentral()
    }

    dependencies {
        compileOnly 'org.projectlombok:lombok:1.18.16'
        annotationProcessor 'org.projectlombok:lombok:1.18.16'
        testImplementation 'org.springframework.boot:spring-boot-starter-test'
        testImplementation('org.springframework.boot:spring-boot-starter-test') {
            exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
        }
    }
}

// api 모듈에 core의 의존성을 추가하라는 의미이다.
project(':api') {
    dependencies {
        implementation project(':core')
    }
}

위의 큰 3개의 Closure를 간단하게 설명하면 다음과 같다

buildscript : Gradle 이 빌드되기전 실행되는 설정

allprojects : 현재의 root 프로젝트와 앞으로 추가될 서브 모듈에 대한 설정

subprojects : 전체 서브 모듈 에 해당되는 설정

이제 모듈들을 생성해보자. 모듈은 core, api 를 생성해보자.

core는 spring-boot-starter 의존성을 가지고 있고 이것을 api가 사용할 수 있게 해보자.

root 폴더를 우클릭 → New → Module을 클릭해서 Gradle Module을 생성해주자

생성하면 다음가 같은 폴더 구조가 될 것이다.

이제 settings.gradle을 보면 다음과 같이 방금 생성한 core가 include 된것을 확인할 수 있다. 이것은 하위 모듈로 선언한다는 의미이다.

IntelliJ의 Gradle Tab에서도 보면 다음과 같이 core가 root 하위에 존재하는 것을 확인할 수 있다.

core의 build.gradle 에 아래처럼 의존성만 추가하자

//core build.gradle
dependencies {

    // compile을 사용했는데 그 이유는 implementation은 직접 의존하는 모듈(core)외에서는 사용할 수 없기 때문이다.
    // 만약 하위모듈에 대한 접근을 원치 않을 경우는 implementation을 사용하면된다.
    // 하지만 compile은 gradle에서 권장되지 않는 방식이다.
    // 심지어 현재는 예제의 gradle은 6.x버전이라 compile을 사용할 수 있지만 7.0 버전 이상부터는 compile을 사용할 수 없다.

    // 그럼 어떻게 해야하나?
    // 7.0버전 이상부터는 api 키워드를 사용할 수 있다. api는 compile이랑 비슷한 키워드라 생각하면 된다.
    // 현재 그래들 버전 확인 : {./gradlew -- version}
    // 그래들 버전 변경 : {$ ./gradlew wrapper --gradle-version=7.4.1}

    compile 'org.springframework.boot:spring-boot-starter-web:2.6.1'
}
bootJar {
    enabled = false
}

jar {
    enabled = true
}

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

그리고 의존성을 추가할 때 implementation 대신 compile을 사용했는데 그 이유에 대해서는 밑에서 설명하겠다

이제 gradle을 갱신하면 root의 build.gradle subprojects에 선언한 lombok과 spring-boot-starter가 추가된 것을 확인할 수 있다.

바로 api 모듈도 core가 똑같은 과정을 통해 만들면 다음과 같은 구조가 될 것이다.

api의 build.gradle은 다음과 같이 아무런 의존성을 넣지 않았다.

//api build.gradle
dependencies {}

이제 root의 build.gradle 맨 아래에 다음을 추가해보자.

project(':api') {
    dependencies {
        implementation project(':core')
    }
}

api 모듈에 core의 의존성을 추가하라는 의미이다.

갱신해보면 아래처럼 api에 spring-boot-starter 추가된 것을 확인할 수 있다.

이제 api에서 @SpringBootApplication을 선언했을 때 정상적으로 사용할 수 있다.

core에 Bean Class를 만들고 api에서 호출하는 예제는 다음과 같다. 간단하게 @Service Bean을 만들어 테스트한 코드다.

//core 모듈
@Service
public class TestService {
    public String test() {
        return "core의 Bean Class 테스트";
    }
}

//api 모듈
@RestController
@RequiredArgsConstructor
public class TestController {
    private final TestService testService;
    @GetMapping("/test")
    public String test() {
        return testService.test();
    }
}

구조로 보면 다음과 같다.

이제 api 를 실행후 /test를 호출하면 아래의 결과를 볼 수 있다.

여기서 주의할 점은 core와 api의 패키지 구조다. 현재 상위 패키지가 kr.multi.ex로 통일 된 것을 볼 수있는데 이렇게 통일하지 않으면 api에서 core의 bean을 읽어오지 못해 에러가 발생한다.

만약 위처럼 api의 패키지를 kr.multi.ex2로 변경하면 패키지가 공통이 되지 않기 때문에 아래와 같은 에러를 호출한다.

좀 더 정확히 말하면 Application 클래스의 위치한 곳의 패키지가 공통으로 맞춰져 있어야한다. 만약 패키지를 못맞춘다면 아래와 같이 scanBasePackages 옵션을 통해 Bean을 스캔할 수 있다.

@SpringBootApplication(scanBasePackages = "kr.multi")
public class ApiApplication {
    public static void main(String[] args) {
        SpringApplication.run(ApiApplication.class);
    }
}
core의 의존성을 compile로 선언한 이유

위에서 core 의존성을 추가할 때 compile을 사용했는데 그 이유는 implementation은 직접 의존하는 모듈(core)외에서는 사용할 수 없기 때문이다.

 core의 spring-boot-starter를 implementation으로 선언할 경우 api에서는 spring-boot-starter를 가져오지 않는다.

위처럼 core의 의존성을 implementation으로 할 경우 api에서는 못가져오는 것을 볼 수 있다.

하지만 compile은 gradle에서 권장되지 않는 방식이다. 심지어 현재는 예제의 gradle은 6.x버전이라 compile을 사용할 수 있지만 7.0 버전 이상부터는 compile을 사용할 수 없다.

Gradle버전 확인 하는 법 !

https://thinkandthing.tistory.com/63?category=791664

 

Gradle 버전 확인 방법

1. IntelliJ (IDE) IntelliJ의 Project Window에서 gradle > wrapper > gradle-wrapper.properties 파일을 열어봅니다. 파일 내용을 보면 아래와 같이 버전 정보가 쓰여있다. (7.4.1 버전) 2. 터미널 터미널 명..

thinkandthing.tistory.com

 

그럼 어떻게 해야하나?

7.0버전 이상부터는 api 키워드를 사용할 수 있다. api는 compile이랑 비슷한 키워드라 생각하면 된다.

만약 gradle 버전을 올리고 싶다면 root 프로젝트 위치에서 해당 명령어를 통해 gradle 버전을 변경할 수 있다.

$ ./gradlew wrapper --gradle-version=7.x

api를 사용하기 위해서는 root build.gradle의 subprojects의 다음을 수정해준다.
apply plugin:’java’  apply plugin:’java-library’

그리고 다음처럼 의존성을 추가하면 된다.

dependencies {
    // compile을 사용했는데 그 이유는 implementation은 직접 의존하는 모듈(core)외에서는 사용할 수 없기 때문이다.
    // 만약 하위모듈에 대한 접근을 원치 않을 경우는 implementation을 사용하면된다.
    // 하지만 compile은 gradle에서 권장되지 않는 방식이다.
    // 심지어 현재는 예제의 gradle은 6.x버전이라 compile을 사용할 수 있지만 7.0 버전 이상부터는 compile을 사용할 수 없다.

    // 그럼 어떻게 해야하나?
    // 7.0버전 이상부터는 api 키워드를 사용할 수 있다. api는 compile이랑 비슷한 키워드라 생각하면 된다.
    // 현재 그래들 버전 확인 : {./gradlew -- version}
    // 그래들 버전 변경 : {$ ./gradlew wrapper --gradle-version=7.4.1}

//    compile 'org.springframework.boot:spring-boot-starter-web:2.6.1'
    api 'org.springframework.boot:spring-boot-starter-web:2.6.1'
}

bootJar {
    enabled = false
}

jar {
    enabled = true
}

 

 

 

참조

너무 잘 정리된 참고 사이트를 찾아서 공유 합니다. https://cjw-awdsd.tistory.com/55

 

[스프링] 멀티 모듈(Multi Module) 개념/예제 feat. Gradle

최근 진행하는 토이 프로젝트의 API 서버는 서로 독립된 프로젝트 2개로 이루어져있었다. 인증 서버 어플리케이션 서버 처음에는 기능 구현 자체에 초점을 맞추고 각자의 프로젝트 크기도 크지

cjw-awdsd.tistory.com

 

반응형

댓글