Android Multi module(멀티 모듈)의 dependency는 어떻게 하는 게 좋을까?



개인 광고 영역

아키텍쳐가 잘 적용되어 있다면 모듈 나누는 고민도 한 번쯤 해보았을 겁니다. 모듈을 나누는 건 어렵지 않습니다.

모듈을 어떤 식으로 나눌지가 어려운 고민이지만, 단순히 클래스의 기능별로 모듈을 나눌 수 있습니다.

  • Base 모듈 : BaseActivity, BaseViewModel, BaseFragment를 포함하는 모듈
  • Network 모듈 : 네트워크 관련 모듈
  • 유틸 모듈 : 공통 유틸

어떠한 기준으로 모듈을 분리할지만 잘 정하면 어렵지 않습니다.

하지만 사전 설계를 잘 해둬야 추후 중복 디펜던시 발생 및 이유 없는 모듈이 추가됨을 관리할 수 있으리라 생각됩니다.

이 글에서 알아볼 내용

  • 모듈을 나누는 모든 과정을 소개하지는 않습니다.
  • 모듈을 나누면서 디펜던시 의존성을 어떻게 가져갈지 간략하게 정리합니다.


모듈의 설계

모듈 설계 시 좋은 방법을 바로 소개하면

  • 정의 모듈 : interface 정의만을 가지는 모듈 하나
  • 정의에 대한 구현 모듈 : interface 정의에 대한 구현체를 가진 모듈 하나

이 두 개의 모듈은 아래와 같이 구분해 사용하는 게 가장 좋다고 생각합니다.

  • interface 정의만을 한 모듈은 실제 사용할 곳에서 dependencies 정의하고 사용합니다.
  • interface 정의에 대한 구현체를 가진 모듈은 코드 초기화에서 만 사용할 수 있도록 정의하는 게 좋습니다.

위와 같은 형태로 모듈을 만들었을 때 얻는 장점은 어디에서 어떤 모듈을 사용했는지 추적이 가능해지는 장점이 생깁니다.

당연히 모듈을 추가하는 디펜던시 방식에 따라 다를 수 있으니, 디펜던시 정의 방법을 먼저 살펴보고, 예제를 살펴보겠습니다.


디펜던시는 어떤 걸 사용할까?

모듈 간 디펜던시는 크게 implementation으로 정의하는 방법과 api로 정의하는 방법이 있습니다.

  • implementation : 대부분의 모듈에서 이를 활용하고, 의존 라이브러리 수정 시 implementation 하고 있는 모듈까지만 재빌드가 필요
  • api : 의존 라이브러리 수정 시 api를 하고 있는 하위 모듈까지 모두 재빌드 필요

재빌드 요구사항을 보면

  • implementation : 대상 모듈 A - 모듈 A를 implementation 하고 있는 모듈 B, 모듈 B를 implementation 하고 있는 모듈 C
    • 대상 모듈 A가 수정되면, 모듈 B는 재빌드 과정을 거칩니다.
  • api : 대상 모듈 A - 모듈 A를 api 하고 있는 모듈 B, 모듈 B를 implementation 하고 있는 모듈 C
    • 대상 모듈 A가 수정되면, 모듈 B, 모듈 C 모두 재빌드 과정을 거치게 됩니다.

디펜던시 내용은 본 블로그에 잘 나와 링크합니다. (Gradle dependency) api와 implementation 차이

모듈 간 디펜던시를 모두 implementation으로 걸 수도 있겠으나, 하위 모듈에서 어떤 모듈을 사용하고 있는지 모르게 하고 싶다면 api를 사용하는 것도 방법입니다.


모듈 설계 예제

  • library : viewController에 interface를 정의합니다.
  • library-impl : library 모듈에 대한 구현체입니다.
  • base : BaseFragment, BaseViewModel을 구성합니다.
    • 이 모듈에서는 librarylibrary-impl를 디펜던시로 사용합니다.
  • second-view : baselibrary 모듈을 사용해 화면을 구성합니다.
  • app : 앱 모듈에서 위 모듈을 조합해 실제 앱을 완성합니다.
    • 이 모듈에서는 위 모든 디펜던시 의존성을 가집니다.

모듈에 대한 샘플 코드


library 모듈

샘플에서 사용하는 controller에 대한 정의한 모듈입니다.

이 모듈은 아래와 같은 라이브러리에서 사용하는데, 실제 사용하는 모든 라이브러리에서 이를 활용해야 합니다.

  • library-impl 모듈 : 구현을 위한 모듈에서 활용합니다. implementation으로 사용합니다.
  • library-test 모듈 : test를 구현하기 위한 모듈에서 활용합니다. implementation으로 사용합니다.
  • base 모듈 : 구현체를 초기화하고, 이를 활용하기 위한 모듈입니다. 여기서는 implementation으로 사용합니다.
  • second-view 모듈 : 화면을 구성할 때 interface를 사용하기 위해 사용합니다.
  • app 모듈 : 앱에서 실행을 위함

image_01

위와 같이 정리할 수 있습니다. interface를 사용하기 때문에 어디에서든 implementation을 해서 사용하도록 설계하여 controller 사용 시 모든 모듈에서 명시적으로 추가해야 사용 가능합니다.


library-impl 모듈

library interface에 대한 구현체를 정의합니다.

이 모듈은 실제 구현체를 만드는 곳에서 활용합니다.

  • base 모듈 : 실제 구현체를 생성하고, 연결하기 위한 목적으로 사용합니다.
  • app 모듈 : 종합하기 위한 적용

image_02


library-test 모듈

test가 필요한 모듈에서 만 이를 추가하도록 만들었습니다.

여기서는 보통 library를 활용하는 모듈에서 함께 사용합니다.

  • second-view 모듈 : controller 테스트를 위해 사용합니다.
  • app 모듈 : 종합하기 위한 적용

image_03


base 모듈

base 모듈은 BaseFragment, BaseViewModel을 정의합니다.

이 모듈은 화면을 구성하는데 필수 요소로 화면을 그릴 때 사용합니다.

  • second-view 모듈 : 화면을 구성하기 위함
  • app 모듈 : 종합하기 위한 적용

image_04


second-view 모듈

여기서는 Second view를 정의하고, 이를 app 모듈에서 연결시킵니다.

  • app 모듈 : 화면을 연결합니다.

image_05


gradle.kts에서 forEach 이용한 모듈 Import

forEach 문을 활용해 모듈을 import 하면 편리한데 아래와 같이 다양한 implementation을

implementation(Dependency.Kotlin.stdLib)
implementation(Dependency.Coroutines.core)
implementation(Dependency.AndroidX.lifecycleCommonJava8)
implementation(Dependency.AndroidX.liveDataKtx)
implementation(Dependency.AndroidX.coreKtx)
implementation(Dependency.AndroidX.appCompat)
implementation(Dependency.AndroidX.constraintLayout)
implementation(Dependency.AndroidX.navigationFragmentKtx)

forEach를 이용해 아래와 같이 변경할 수 있습니다.

implementations(
    Dependency.Kotlin.stdLib,
    Dependency.Coroutines.core,
    Dependency.AndroidX.lifecycleCommonJava8,
    Dependency.AndroidX.liveDataKtx,
    Dependency.AndroidX.coreKtx,
    Dependency.AndroidX.appCompat,
    Dependency.AndroidX.constraintLayout,
    Dependency.AndroidX.navigationFragmentKtx,
)

implementations 함수를 미리 아래와 같이 만들어두면 위와 같이 활용할 수 있습니다.

fun DependencyHandlerScope.implementations(vararg argument: String) {
    argument.forEach {
        "implementation"(it)
    }
}


마무리

이와 같은 모듈의 형태는 하나의 예에 해당합니다. 저의 경우 위와 같은 형태로 모듈을 나누어 사용하고 있습니다.

디펜던시의 명확한 사용처를 확인할 수 있는 형태로 만들어 사용하고 있는데, 추후 어떻게 변할지는 아직은 잘 모르겠습니다.

디펜던시의 강제화를 통해 얻는 이익은 추적이 가능하다는 점을 미리 생각해 위와 같이 정의하였습니다.



About Taehwan

My name is Taehwan Kwon. I have developed Android for 6 years and blog has been active for eight years.

Comments