나만의 안드로이드 앱 만들기/고급자

나만의 안드로이드 앱 만들기(고급자 편) - 멀티 모듈 프로젝트 구성하기

Victorywskim 2023. 12. 26. 05:36
반응형

본편에서는 멀티 모듈 프로젝트를 직접 구성해보도록 하겠습니다.

 

기준 코드는 다음과 같습니다.

 

GitHub - tmvlke/SimpleDutch: 심플더치 프로젝트 입니다.

심플더치 프로젝트 입니다. Contribute to tmvlke/SimpleDutch development by creating an account on GitHub.

github.com

 

저번 편에서 설명드린 것 처럼 전 4가지 관점을 주로 적용하여 멀티 모듈 프로젝트를 구성하고 있습니다.

 

  • 기능
  • 계층
  • UI 관련 여부
  • 테스트 가능성

 

기능은 저는 주로 탭을 기준으로 구분하고 있으며, 이번편에서도 탭을 기준으로 구분하고자 합니다.

만약 제가 카카오톡 개발자라면 친구 관리 모듈, 채팅 모듈 등으로 멀티 모듈을 구성 했을 것 같습니다.

 

계층은 이미 클린 아키텍쳐가 도입 되어 있어서 이번편에서는 넘어가고자 합니다.

 

UI 관련 여부는 앱에서 공통으로 사용되는 컴포넌트 및 ui 기능(navigation)들을 UI 모듈들에서도 똑같이 적용하고자할때 사용합니다. 

 

테스트 가능성은 기능 관점에서 멀티 모듈 프로젝트를 구성하면서 같이 적용해볼 예정입니다.

 

 

멀티 모듈 프로젝트 구성 소개

 

 

현 프로젝트에서는 홈과 저장소 2개의 탭으로 구성되어 있습니다.

 

 

본편에서는 기존 프로젝트에서는 presentaion 내에 homeTab 과 storageTab, baseUiKit 4가지 모듈을 추가하고자 합니다.

homeTab 과 storageTab 은 메인 서비스를 관리하는 모듈이며, baseUiKit 은 공용 컴포넌트 및 네비게이션, 캘린더, 그래프 등 공통 기능들을 관리하는 모듈입니다.

 

구조는 다음과 같습니다.

  • ...
  • presentaion
    • baseUiKit 
    • homeTab 
    • storageTab 

제약 조건은 다음과 같습니다.

  • presentaion
    • 실질적인 앱입니다.
    • 스플래시 및 메인 페이지 등을 담당하고 있으나, 추후 분리 예정입니다.
    • baseUiKit, homeTab, storageTab 모두 참조 가능합니다.
  • baseUiKit 
    • 컴포넌트 및 페이지, 네이게이션 등을 관리합니다.
    • data, domain 만 참조 가능하며, 다른 모듈들은 참조 불가합니다. 
  • homeTab 
    • 홈 기능을 담당합니다.
    • baseUiKit 만 참조 가능하며, 다른 모듈들은 참조 불가합니다. 
  • storageTab 
    • 저장소 기능을 담당합니다.
    • baseUiKit 만 참조 가능하며, 다른 모듈들은 참조 불가합니다.

 

모듈 생성하기

 

모듈 생성 방법은 다음과 같습니다.

presentaion 모듈에서 우클릭을 하고 New -> Module 을 클릭합니다.

 

그 다음 Android Library 를 선택하고 baseUiKit, homeTab, storageTab 를 생성합니다.

 

생성이 완료되면 다음과 같습니다.

모듈 구조

 

 

의존 관계 설정하기

 

 

presentaion

...

dependencies {

    implementation(project(mapOf("path" to ":presentation:baseUiKit")))
    implementation(project(mapOf("path" to ":presentation:homeTab")))
    implementation(project(mapOf("path" to ":presentation:storageTab")))

    ...
}

...

 

추가된 baseUiKit, homeTab, storageTab 을 각각 추가해주고, 불필요한 의존성을 제거합니다.

 

baseUiKit 

...

dependencies {

    api(project(":domain"))
    api(project(":data"))

    // ktx
    api(SdDependency.Ktx.core)
    api(SdDependency.Ktx.lifecycleRuntime)
    api(SdDependency.Compose.activity)

    api(platform(SdDependency.Compose.bom)) // compose
    api(SdDependency.Compose.ui)
    api(SdDependency.Compose.graphics)
    api(SdDependency.Compose.preview)
    api(SdDependency.Compose.material3)

    api(SdDependency.Navigation.compose) // navigation
    api(SdDependency.Navigation.hilt)

    api(SdDependency.Compose.constraint)

    api(SdDependency.Navigation.animation)

    api(SdDependency.coil) // coil

    // Hilt
    implementation(SdDependency.Hilt.android)
    kapt(SdDependency.Hilt.androidCompiler)

    // tdd
    testImplementation(SdTestDependency.Tdd.junit5)
    testRuntimeOnly(SdTestDependency.Tdd.junit5Engine)
    testImplementation(SdTestDependency.Tdd.junit5Params)
    testRuntimeOnly(SdTestDependency.Tdd.junit5ParamsEngine)

    testImplementation(SdTestDependency.Tdd.mockk)

    testImplementation(SdTestDependency.Tdd.coroutines)

    // bdd1
    androidTestImplementation(SdTestDependency.Bdd.junit)
    androidTestImplementation(platform(SdTestDependency.Bdd.composeBom))
    androidTestImplementation(SdTestDependency.Bdd.composeJunit)

    // bdd2
    debugImplementation(SdTestDependency.Bdd.debugComposeTooling)
    debugImplementation(SdTestDependency.Bdd.debugComposeManifest)
}

...

 

presentaion 에서 공통으로 사용될 의존성을 이전합니다.

 

homeTab 및 storageTab 

...

dependencies {

    implementation(project(mapOf("path" to ":presentation:baseUiKit")))

    // Hilt
    implementation(SdDependency.Hilt.android)
    kapt(SdDependency.Hilt.androidCompiler)

    // tdd
    testImplementation(SdTestDependency.Tdd.junit5)
    testRuntimeOnly(SdTestDependency.Tdd.junit5Engine)
    testImplementation(SdTestDependency.Tdd.junit5Params)
    testRuntimeOnly(SdTestDependency.Tdd.junit5ParamsEngine)

    testImplementation(SdTestDependency.Tdd.mockk)

    testImplementation(SdTestDependency.Tdd.coroutines)

    // bdd1
    androidTestImplementation(SdTestDependency.Bdd.junit)
    androidTestImplementation(platform(SdTestDependency.Bdd.composeBom))
    androidTestImplementation(SdTestDependency.Bdd.composeJunit)

    // bdd2
    debugImplementation(SdTestDependency.Bdd.debugComposeTooling)
    debugImplementation(SdTestDependency.Bdd.debugComposeManifest)
}

...

 

baseUiKit 을 비롯하여 테스트 라이브러리까지 추가해주었습니다.

 

 

소스코드 이전하기

 

presentaion

 

presentaion 은 root 경로로 NavGraph.kt, MainActivity, SdApplication 3 파일만 있으면 됩니다. 하지만 지금은 불필요한 모듈 추가를 지양하고자 main 과 splash 는 남겨두었습니다. 추후 회원가입 이나 소개 페이지 등이 추가되면서 점점 비대해진다면 그땐 별도 모듈로 분리할 예정입니다.

 

baseUiKit

 

baseUiKit 에는 presentaion 에 있던 base 파일을 비롯하여 navigation 과 같이 ui 구성에 공통적으로 사용될 파일들을 이전시켜두었습니다.

 

homeTab

 

homeTab 에는 presentaion 에 있던 홈 관련 page 및 screen 들을 이전해두었습니다.

 

storageTab

 

마지막으로 storageTab 에도 homeTab 과 같이 저장소 관련 page 및 screen 들을 이전해두었습니다.

 

 

마치며

 

storageTab 에서  homeTab 을 사용하려는 경우 에러가 나는 모습

 

위 이미지는 저장소 탭에서 홈 탭의 페이지를 불러오려고 하는 상황입니다.

 

의존 관계가 설정되어 있지 않으니 당연히 호출이 불가능한 상황입니다. 개발자들 간 제약 사항을 아무리 잘 맺는다고 하더라도 이렇게 물리적으로 분리가 되어있어야 더 안정적으로 개발에 임할 수 있다고 생각합니다.

 

본편에서의 작업은 프로젝트 크기가 워낙에 작고 초기 인 점을 감안했을때 큰 공수없이 작업을 마무리 할 수 있었지만, 실제로 운영중인 서비스를 멀티 모듈 프로젝트로 마이그레이션 하는 작업은 생각보다 훨씬 어렵고 복잡한 작업이 될 수 있다고 생각합니다.

 

mvp 로 빠르게 시장 검증을 할때부터 클린 아키텍쳐나 멀티모듈 등을 모두 도입하기는 매우 어려울 것이라고 생각합니다.

 

다만 지금은 적용 못하지만 추후 적용 시에 어떠한 구조로 발전 시킬 수 있을지 지속적으로 탐구하는 과정이 동반되어야 적용 시점이 되었을때 만족스러운 결과를 얻을 수 있지 않을까 싶습니다.

 

멀티 모듈을 적용한 전체 소스코드입니다.

 

 

GitHub - tmvlke/SimpleDutch: 심플더치 프로젝트 입니다.

심플더치 프로젝트 입니다. Contribute to tmvlke/SimpleDutch development by creating an account on GitHub.

github.com

 

감사합니다.

반응형