본문 바로가기
Spring/MSA

Gradle Multi Module 프로젝트

by clearinging 2021. 6. 12.
반응형

머리말

하나의 단일 모듈로는 관리에 어려움을 겪게 되었고 이문제를 해결하기 위해서 모듈을 분류하는 multi module을 공부하게 되었습니다.

프로젝트 생성

Spring Boot 2.4.xx
Gradle 6.8
Intellij
Java11

처음 프로젝트 구조

초기 프로젝트 구조에서 app이라는 디렉 토리를 추가하고 project structure를 통해서 모듈을 추가해줄 수 있다.

추가

그리고 new moudle을 클릭하면

새로운 모듈 추가

위와 같이 새로운 spring initializer가 온다.

이렇게 새로 추가하는 방법도 있지만, settings.gradle, HELP.md 와같이 사용하지 않는 파일도 같이 추가 되어서 디렉토리를 추가하고 build.gradle을 추가해주는 방식으로 사용합니다.

프로젝트 구조

현재 프로젝트 구조와 동일하게 먼저 디렉토리를 생성한다.

추가적으로 root project에서 모듈을 검색할 수 있어야하기 때문에 settings.gradle에 module을 추가한다.

rootProject.name = 'newtest'

include 'app:module-api', 'app:module-web', 'app:common'

rootProject는 최상위의 project 이름 입니다. 그러므로 건들지 말고
include를 통해서 모듈을 추가합니다.
directory 구조에 맞게 추가하는데. app이라는 최상위 dir가 있기 때문에 app: 을 다 넣어줬습니다.

Root build.gradle 변경

buildscript {
    ext {
        springBootVersion = '2.4.7'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
        classpath "io.spring.gradle:dependency-management-plugin:1.0.11.RELEASE"
    }
}


subprojects {
    group 'com.example'
    version '1.0'

    apply plugin: 'java'
    apply plugin: 'org.springframework.boot'
    apply plugin: 'io.spring.dependency-management'

    sourceCompatibility = 11

    repositories {
        mavenCentral()
    }

    dependencies {
        compileOnly 'org.projectlombok:lombok'
        annotationProcessor 'org.projectlombok:lombok'
    }
}

project(':app:module-api') {
    dependencies {
        compile project(':app:common')
    }
}

project(':app:module-web') {
    dependencies {
        compile project(':app:common')
    }
}
  • subprojects는 setting.gradle에 include된 프로젝트 전부를 관리할 때 사용합니다,
  • root proejct에 src가 있을경우에는 allprojects에 원하는 설정을 넣을 수 있습니다.
  • 현재는 root project에 코드가 없기 때문에 allprojects를 사용하지 않았습니다.
  • subprojects를 통해서 dependencies를 통해서 모든 모듈에 사용할 lombok을 추가 해주었고, java version 설정과 같은 추가 설정 값을 넣어 줬습니다.
  • project(A) {B} : B의 설정값을 A 모듈에 추가하기 위해서 사용합니다.
    • commonmoudle-api, moudle-web에 사용합니다.
    • commmon의 코드를 import로 사용하기 위해서 compile project(moudle) 을 넣어 줍니다.(나중에 예시를 통해서 해보도록 하겠습니다,)

sub module gradle 설정

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    runtimeOnly 'com.h2database:h2'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

test {
    useJUnitPlatform()
}
  • submdule의 gradle은 위와 같이 root gradle에서 설정한 dependency 외에 필요한 dependecy를 추가해주면 됩니다.
  • main 문이 없는 module
    • common와 같이 entity class를 주로 모아 놓은 클래스인 경우 main이 존재하지 않는경우가 많습니다.
    • 결론적으로 gradle에서 build를 할 때 jar파일이 생성하지 않게 됩니다.
    • 단순 참조형 클래스가 존재할 경우 아래와 같이 코드를 추가하게 된다며, gradle build를 할 때 다른 모듈에서 jar파일을 생성할 수 있게 됩니다.
      • spring 1 버전인 경우 : bootRepackage.enable
      • 2버전 이상
bootJar { enabled = false }
jar { enabled = true }

예시

구성

Common

  • Entity : Member
  • Repository : MemberRepository

module-api

  • Service : MemberService

구현

Common - Entity

  • module 위치 : common
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Member {
    @Id
    @GeneratedValue
    private Long id;

    @Column
    private String name;

    public Member(String name) {
        this(null, name);
    }

    public Member(Long id, String name) {
        this.id = id;
        this.name = name;
    }
}

Common - Repository

  • module 위치 : common
@Repository
public interface MemberRepository extends JpaRepository<Member, Long> {
}

module-api - service

  • module 위치 : module-api
@Service
@RequiredArgsConstructor
public class MemberService {
    private final MemberRepository memberRepository;

    public Long signUp(Member member) {
        return memberRepository.save(member).getId();
    }
}

module-api - test code

  • module 위치 : module-api
@SpringBootTest
class MemberServiceTest {
    @Autowired
    MemberService memberService;

    @Test
    @DisplayName("회원 가입 테스트")
    void signUp() {
        // given
        Member member = new Member("kkh");

        // when
        Long id = memberService.signUp(member);

        // then
        assertThat(id).isEqualTo(1);
    }
}

테스트 결과

테스트 결과

  • 다른 공통 모듈에서 데이터를 잘 가져오는걸 확인하였습니다.

추가

  • root 말고 각 sub module에 common moudle import 하기
  • dependency에 compile로 추가할 수 있습니다.
dependencies {
    compile project(':app:common')
    // ...
}
반응형

'Spring > MSA' 카테고리의 다른 글

Spring Cloud Contract를 이용한 Test 코드 작성  (0) 2022.09.17
Kafka 설치와 환경 구축  (2) 2021.07.05
Spring cloud config 설정  (0) 2021.06.25