Google Developer 공식문서를 참조하면
모듈은 소스파일 및 빌드 설정으로 구성된 모음이며, 이를 통해 프로젝트를 별개의 기능 단위로 분할할 수 있습니다. 안드로이드 프로젝트에는 반드시 하나 이상의 앱 모듈이 존재하며, 그 밖에도 하나 이상의 다른 모듈들이 포함될 수 있습니다. 또한, 하나의 모듈이 다른 모듈을 종속 항목으로 사용할 수 있습니다. 각 모듈을 독립적으로 빌드, 테스트, 디버그 할 수 있습니다.
진행하던 프로젝트 크기가 커지고, 클린아키텍처를 적용함에 있어서 멀티모듈화가 필요해졌습니다.
지금부터는 제가 직접 고민하며 멀티모듈을 적용했던 경험을 적어보겠습니다.
MVVM | Clean Architecture |
---|---|
위의 키워드를 공부할 때 가장 많이 보이는 그림입니다.
저는 프로젝트에 MVVM패턴과 클린아키텍처를 적용하고자 했습니다.
왜 MVVM 패턴을 사용했는가?
기존의 MVP 패턴 대신 MVVM을 적용한 가장 큰 이유는 ViewModel의 재사용입니다.
MVVM 패턴에서는 ViewModel과 View과 1:N 관계이기 때문에 하나의 ViewModel로 여러 View를 업데이트 하는 로직을 설계할 수 있습니다. 이는 MVP 패턴에서 Presenter와 View과 1:1 관계로 존재했던 것과 비교했을 때 많은 양의 코드와 클래스 파일들을 줄일 수 있다는 장점이 있습니다.
왜 클린아키텍처를 적용했는가?
가장 큰 이유는 유지보수를 용이하게 하고, 팀프로젝트에서 다른 분야를 담당하는 팀원들과 같이 코드 리뷰를 진행했을 때도 쉽게 이해하도록 하기 위해서 였습니다.
그럼 이제 저의 프로젝트와 함께 실제로 적용한 내용을 살펴 보겠습니다.
우선, 전체 프로젝트의 모듈 간 관계를 그림으로 나타내보았습니다.
그림에는 포함시키지 않았지만,
모든 모듈에서 참조할 수 있는 Util, Model 모듈이 따로 존재합니다.
App 모듈에서는 프로젝트 빌드 전 필요한 초기화 작업이 이루어집니다.
Hilt 라이브러리를 사용하기 때문에 HiltAndroidApp
어노테이션을 붙일 MainApplication 클래스가 있습니다.
또한 Timber 라이브러리를 사용하기 때문에 TimberInitializer 클래스도 존재합니다.
gradle의 defendencies는 아래와 같습니다.
스플래시스크린 이후 로그인이 완료된 후, 플랫폼 서비스를 이용할 수 있으므로 Login 기능을 하나의 모듈로 분리했습니다.
모듈 간 의존 관계를 살펴보면, model
, repository
, common
, utils
모듈에 대해 의존성을 가집니다.
또한, 모듈안의 hierarchy는 다른 feature 모듈과 동일하게 구성했습니다.
@bindingAdapter
어노테이션을 통해 ViewBinding 작업을 해주었기 때문에 해당 메서드 코드들이 위치합니다.Feature module과 app module 사이에 하나의 모듈을 추가한 이유는 Navigation 때문입니다.
저는 Jetpack에서 제공하는 Navigation Component
를 사용했기 때문에 모듈과 모듈 간 fragment 이동을 구현할 수 있는 NavController
가 필요했습니다. 그래서 각 feature module의 nav_graph를 include하여 하나로 그룹화할 수 있는 graph를 생성했습니다.
<!-- common/src/main/res/naviagtion/nav_graph.xml -->
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav_graph"
app:startDestination="@id/graph_home">
<include app:graph="@navigation/graph_home"/>
<include app:graph="@navigation/graph_dip"/>
<include app:graph="@navigation/graph_userhouse"/>
<include app:graph="@navigation/graph_search"/>
<include app:graph="@navigation/graph_more"/>
</navigation>
이외에 class 파일은 MainActivity가 존재하는데, 아래와 같은 기능을 합니다.
nav_host
를 설정합니다.bottom navigation에 존재하는 다섯개의 기능별로 모듈화를 했습니다.
각 모듈 내부의 계층구조는 Login module에서와 동일합니다.
data 폴더를 만들고 내부에 아래와 같이 관련 모듈들을 생성했습니다.
이번 멀티모듈화 작업을 하며 들었던 생각은 개발은 Give and Take
라는 것입니다.
모듈을 기능별로 나누고 의존관계를 설정하는 단계에서 고민하는 시간이 많았고, gradle의 dependencies를 선언할 때도 알 수 없는 에러들을 많아서 해결하는 것이 어려웠습니다.
하지만, 멀티모듈화를 한 후에는 빌드시간도 감소했고, boiler plate 코드도 줄어 더 생산성 있는 작업이 가능했습니다.
그리고 무엇보다 프로젝트를 개발하다보면 drawable파일에 계속 xml파일이 추가되어 관리하기가 힘들고 필요한 파일을 찾을 때도 스트레스 받았었는데 이렇게 기능별로 나누고 각 모듈마다 필요한 drawable 파일을 나누다보니 관리가 매우 편합니다.
=> 결론: 멀티모듈화 하세요!