안녕하세요, 이번 포스팅에서는 Gradle을 이용하여 멀티 모듈을 사용하는 부분에 대해 다루려고 합니다.
틀린 부분이 있다면, 아래 댓글을 통해 알려주시면 감사드리겠습니다.
코드는 이 곳에 있으니 참고해주세요.
먼저 멀티 모듈에 대해 알게 되고, 공부하게 된 계기를 말하며 포스팅을 시작하려합니다.
현재 진행하고 있는 축구 매칭과 관련된 사이드 프로젝트가 있습니다. 이전에도 진행을 했었지만, 개인적인 일로 못하고 있다가 새롭게 다시 시작을 하게 된지 몇 달이 흘렀습니다.
이전에 이 프로젝트를 진행하며 별다른 고민없이 넘어간 부분이 있었는데, 그 부분은 바로 겹치는 클래스들이었습니다.
랭킹을 매기는 집계 혹은 경기 전, 경기 중, 경기 후 등의 데이터 변경을 위해서는 api 서버 외에 간단한 배치 서버 프로젝트를 만들어서 추가로 두었습니다.
즉, 독립적인 프로젝트를 만들었습니다.
이때 데이터베이스에 접근하기 위해 api 서버에도 Member 엔티티가 필요하였고, 배치 서버에도 Member 엔티티가 필요하였습니다. 그 당시에는 똑같은 모델을 그대로 사용하며 만들기에 급급했던 것 같았습니다.
하지만, 이번에 다시 프로젝트를 진행하며 이 부분에 대한 해결책이 분명히 있을 것 같다는 생각을 하였고, gradle을 이용한 멀티모듈 방식이 있다는 것을 알게되었습니다.
위와 같은 방식으로 개발을 할 경우에는, 각각이 독립된 프로젝트 단위이기 때문에, 어떤 도메인을 공유할 수 없는 문제가 있습니다. 이 결과, 똑같은 도메인을 각각의 프로젝트에 두게 됩니다.
멀티 모듈은 독립적인 프로젝트를 하나의 프로젝트 안의 모듈로서 가지고 있을 수 있는 구조를 제공하게 됩니다.
그림에 정확하게 표현하지는 않았지만, common module
을 통해서 공통적으로 사용되는 도메인 및 공통 코드들을 넣고, 다른 모듈들에서 이를 사용하면 되는 방식으로 개발을 할 수 있게 되었습니다.
아직은 공부 단계에 있어, 이런 구조로 만든 것도 장족의 발전이라고 생각했지만, common-module의 몸집이 커지는 부분에 대한 고민도 추후 필요할 것 같습니다.
이에 대한 참고 링크입니다.
위의 구조는 제가 간단하게 만든 구조의 멀티 모듈 프로젝트입니다.
root 프로젝트인 multi-modules-template
프로젝트와 하위 프로젝트(모듈)인 module-api
, module-common
, module-batch
가 있습니다.
이를 만드는 방법에 대해 이제부터 다뤄보도록 하겠습니다.
저는 스프링 부트와 gradle 6버전을 이용하여 프로젝트를 만들었습니다. gradle 6버전을 사용한 이유에 대해서도 이후 다시 한번 다뤄볼 예정입니다.
새로운 프로젝트를 Spring Initializer 를 이용하여 생성하였습니다.
만든 이 프로젝트는 멀티 모듈 프로젝트의 root가 될겁니다.
dependencies {
compile 'mysql:mysql-connector-java'
compile 'org.springframework.boot:spring-boot-starter-data-jpa'
}
bootJar { enabled = false }
jar { enabled = true }
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
buildscript {
ext {
springBootVersion = '2.5.1'
dependencyManagementVersion = '1.0.11.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}"
classpath "io.spring.gradle:dependency-management-plugin:${dependencyManagementVersion}"
}
}
}
subprojects {
group 'io.hala'
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'
implementation 'org.springframework.boot:spring-boot-starter-test'
}
}
project(':module-api') {
dependencies {
compile project(':module-common')
}
}
project(':module-batch') {
dependencies {
compile project(':module-common')
}
}
여기서는 subprojects와 project() 코드가 중요합니다.
subprojects
는 settings.gradle
에 include된 프로젝트를 전부 관리하는 공간입니다.
이때 만약, root 프로젝트까지 적용을 하고 싶을 때는 subprojects 대신 allprojects로 등록하면 됩니다.
project()
는 하위 프로젝트의 의존성을 관리하기 때문에, module-api
와 module-batch
에 module-common
을 추가해주었습니다.
참고로 :
는 디렉토리 path를 의미합니다. 이렇게 되면 module-api
와 module-batch
에서 module-common
을 사용할 수 있게 됩니다. 자세한 코드는 위에 첨부한 저장소를 참고해주시면 될 것 같습니다.
처음 제가 멀티 모듈 프로젝트를 만들때는 Gradle 7버전을 사용하였습니다. 그런데 7버전에서 compile 키워드를 사용하니 아래와 같은 에러가 발생했습니다.
Could not find method compile() for arguments Gradle
이전에도 compile 키워드가 deprecated 된다고 경고문을 보았었지만, Gradle 7에서부터는 완전히 사라졌다고 합니다. 아래는 스택오버플로우에서 가져온 글입니다.
이런 이유로 implementation
을 사용하여 개발을 하였지만, implementation
은 compile
키워드와 다르게 직접 의존하고 있는 모듈외에는 api가 노출되지 않습니다.
즉, module-api
에서 module-common
은 노출되지만, jpa
의존성은 module-api
에 노출되지 않아 save()
, findById()
등의 메소드를 사용하지 못하는 상황이었습니다.
이를 해결하기 위해 위에서 작성했던 subprojects 부분에 jpa 의존성을 추가해주었지만, 이런 상황이 지속된다면 결국 모듈화를 한 의미가 없어질 것 같다는 생각이 들었습니다.
현재는 gradle 6로 버전을 낮추어, compile
을 이용해 코드를 작성하였지만, compile
키워드는 완전히 deprecated되었기 때문에, 7에서도 동작할 수 있는 코드를 고민해본 후에 추후 이에 대한 내용을 업로드 하려 합니다!
위와 같은 내용을 작성한 후에, 지인으로부터 Gradle7에서 compile
을 대체하는 api
를 사용해보는 것은 어떻겠냐라는 조언을 받게 되었습니다.
Gradle7으로 다시 버전을 올리고 api
를 통해 작성한 결과 정상적으로 동작하는 것을 알 수 있었습니다.
제 깃허브에 있는 코드 역시 api
로 수정되어있으므로 참고해주시기 바랍니다 😃
apply plugin: 'java-library' //없으면 api() 오류 나와요 ㅠ