본문 바로가기

우아한테크코스/프로젝트

Multi Module

gradle에서 서브 프로젝트를 추가하기 위해서는 최상위 프로젝트 settings.gradle에 아래와 같이 추가해준다.

include 'hashtagmap-web'
include 'hashtagmap-core'
include 'hashtagmap-instagram-crawler'
include 'hashtagmap-kakao-api'
include 'hashtagmap-admin'
include 'hashtagmap-kakao-scheduler'
include 'hashtagmap-instagram-scheduler'
include 'hashtagmap-common'
include 'hashtagmap-event'
include 'hashtagmap-batch'

이런식으로도 가능하다.

include 'hashtagmap-web', 'hashtagmap-core', ... 생략

헷갈렸던 부분들을 정리해보자.

complie과 implements

  • compile: 모듈이 갖고 있는 의존성도 사용중인 애플리케이션 모듈이 다 쓸 수 있음
  • implementation: 의존성의 의존성을 사용할 수가 없음, 계층을 넘어가서 의존성을 사용할수가 없음

core의 querydsl의 의존성을 사용하려면 core를 complie 해야 했던 것이 예시이다.

bootJar과 jar

bootJar { enabled = false }
jar { enabled = true }

gradle 빌드시에는 디폴트로 각 프로젝트를 실행가능한 jar형태로 만들게 되는데, main메소드가 없는 모듈을 실행가능한 jar로 만들려고하면 에러가 난다.

main 메서드가 필요없는 참조 모듈을 위해서 있는 설정이 위에서 해줬던 설정이다.

도입 이유

  • 초반 설계 시 우리 프로젝트는 각 기능의 성격이 뚜렷했다. admin(instagram-scheduler, kakao-scheduler)과 web을 실행 가능한 jar 파일로 각각 만들어 서버에 띄우기 위함과 insta관련 모듈들 kakao관련 모듈들 각 모듈들의 성격이 뚜렷해서 공통된 의존성이 별로 없이 각각 최소한의 의존성으로 분리시킬 수 있었다.
  • 멀티 모듈이 아닌 패키지로 구분했어도 admin과 web은 실행가능한 jar 파일로 각각 만들 수 있다. 하지만 필요없는 의존성을 가지게 된다. 관리 포인트가 늘어나게 된다. 자세히는 아래의 장점에서 확인하자.
  • instagram-scheduler나 kakao-scheduler가 커졌을 때 유연하게 분리가 가능하다. 지금까지도 점점 커지고있는 admin관련 모듈들이 몸집이 더 거대해지면 똑 떼어서 실행 가능한 jar파일로 분리가 아주 용이하다. batch 모듈로 분리도 어렵지 않았을 것이다.
  • 프로젝트가 끝난게 아니다. 프로젝트가 더 커지면 MSA로 전환하기가 쉽다. 멀티 모듈은 모놀릭스와 MSA를 왔다 갔다 전환할 수 있게 해주는 구조가 되도록 도와준다. 쉽게 말하면 모놀릭스와 MSA로 언제든지 갈 수 있는 전략적 요충지 역할이다.

멀티 모듈 장점

  • 재사용, 공유 할 수 있다. 멀티 모듈의 각 모듈들을 독립적이고 필요한 최소 의존성을 가지고 있기 때문에 다른 영향을 고려하지 않고 재사용이 가능하다. 만약 다른 프로젝트를 하는데 kakao API가 필요하다면 우리 프로젝트의 kakao-api 모듈을 의존성 추가해서 쉽게 재사용 할 수 있다. 반면 우리 프로젝트가 싱글 모듈이였다면 다른 프로젝트에서 우리 프로젝트 모듈 의존성을 추가할 수 없을 것이다. 해당 프로젝트에 쓸모없는 기능과 의존성이 너무 많기 때문이다.
  • 빌드를 쉽게 할 수 있다. 멀티 프로젝트의 경우에는 각 프로젝트마다 빌드를 해줘야 한다. 하지만 멀티 모듈은 최상위 모듈에서 전체 프로젝트를 빌드 할 수 있다는 점이 큰 장점이다. 모듈 별도 빌드와 테스트도 아주 큰 장점이다. 싱글 모듈인 경우에는 admin관련 기능만 수정했다해도 전체를 빌드해야한다. 전체 빌드 수행 시간과 한 모듈 빌드 수행 시간의 차이는 다들 잘 알것이다.
  • 변경으로 인한 영향 최소화. 버그를 발견하면 전체 시스템이 아닌 버그가 포함된 모듈만 업데이트하면 된다. 그로 인해 수정으로 인한 영향이 최소화 된다. 한 곳을 수정했는데 예상치 못한 곳에서 다른 에러가 터지는 불상사들이 다 얽히고 설킨 의존성 때문이다. 전체를 빌드, 재배포할 필요없이 편하게 버그 모듈만 빌드 재배포할 수 있는 장점도 있다.
  • 하나의 모듈을 업데이트할 때 관련 프로젝트 전체를 이해할 필요가 없다. 각 모듈이 갖는 책임과 역할이 명확해 리팩토링, 기능 변경 영향 파악하기가 쉬워졌기 때문이다. 우리 프로젝트를 처음 보는 사람이 kakao-scheduler를 수정한다면 전체 프로젝트를 이해할 필요없이 build.gradle에 가서 관련 모듈만 파악해도 충분하다.
  • 의존성을 최소화. 계속해서 언급했던 의존성 최소화이다. 의존성을 최소화하면 뭐가 좋을까? 앞서 언급했던 것들에서 생각해보면 우선 변경으로 인한 영향을 최소화할 수 있다. 즉 결합도가 낮아진다. 또한 각 모듈을 가볍게 유지해서 빌드 시간을 줄이고 생산성을 향상시킬 수 있다. 더해서 이동욱님의 블로그에 있는 의존성을 최소화해야 하는 이유를 살펴보자.

자바에서 성능적으로 본다면 사실 그렇게 큰 성능 차이는 아닐 것 입니다. 그러나 좋은 개발환경이란 측면과 Spring Boot 를 사용하는 측면에서는 손해를 볼 수 있습니다. 여러가지 손해들이 있겠지만 아래 두 가지를 짚어보았습니다.

첫번째로는 여지의 개방으로 개발 생산성을 떨어트립니다. 무엇이든 끌어다쓸 수 있게 된다는 측면에서는 초기에는 빠르게 개발을 할 수도 있지만, 그로인해 만들어지는 스파게티 의존으로 점차 개발 생산성이 떨어지게 됩니다.

두번째로 Spring Boot 를 사용하고 있기 때문에 예상치 못했거나, 불필요한 설정이 동작하게 될 수 있습니다. Spring Boot 는 Class 의 존재만으로도 작동되는 설정들이 아주 많이 있습니다.

이로인해 예상치 못한 설정이 로드될 수도 있으며, 혹은 위 잘못된 멀티모듈에서 보았던 사용하지 않는 자원에 대한 설정을 해줘야하는 일들도 발생할 수 있습니다.

단점

  • 멀티 모듈 학습에 대한 비용이 생긴다.
  • 익숙하지 않은 멀티 모듈로 인해 예상치 못한 에러가 나고 시간이 소요된다. 우리 프로젝트에서도 bootJar 설정 문제, implements/complie 문제 등이 있었음
  • 여러 모듈을 유지 관리하기가 더 어려울 수 있다. 많이 존재하고 충분히 문서화되지 않은 경우 필요한 의존성을 찾고 적는 것도 지루한 작업이 될 수 있다.
  • 기능이 추가될수록 처음 설계와 다르게 의존성이 꼬일 가능성이 높다.

그럼 모든 프로젝트에서 멀티모듈을 도입하는게 좋을까?

  • 프로젝트가 커지면 커질수록 멀티 모듈은 거의 필수가 된다고 한다. 하지만 크기가 작은 프로젝트의 경우에는 멀티 모듈의 장점을 살릴 수 없는 의미없는 멀티 모듈이 될 확률이 높다.