Beeeam

Bottom Navigation in Multi Module with deeplink 본문

Android

Bottom Navigation in Multi Module with deeplink

Beamjun 2024. 3. 26. 16:08

이전에 진행했던 프로젝트에서 클린 아키텍처를 기반으로 Data, Domain, Presentation으로 모듈을 나눠서 프로젝트를 설계했던 경험이 있습니다.

하지만 Presentation 모듈이 타 모듈들에 비해 비대해지는 문제점을 찾았고 이를 해결하기 위해서 Presentation 모듈을 하위 모듈들로 나눠야겠다는 생각을 하게 되었습니다. 그래서 기능별로 모듈을 생성하고 작업하던 중 Navigation을 어떻게 구현해야 할 지에 대한 문제에 직면하게 되었습니다.

이 글에서는 이런 문제를 해결한 방법에 대해서 글을 써보자 합니다.

문제

기능별로 모듈을 나누면서 모듈마다 내부의 컴포넌트들에 대한 navgation graph를 가지게 설계하였습니다. 이 때문에 여러 개의 navigation graph를 체계적으로 관리할 방법을 찾아야 하는 상황이였습니다.

해결

Navigation을 관리하는 모듈을 생성하여 해당 모듈의 navigation graph에서는 다른 모듈들의 navigation graph들을 포함하고 bottom navigation Item의 클릭에 따라 해당 기능의 navigation graph로 이동하도록 설계하였습니다.

글로만 설명하면 어려우니 예시 코드와 함께 더 설명해보겠습니다.

Bottom Navigation에는 A, B, C, D가 있고 아이템을 클릭하면 해당 알파벳으로 시작하는 과일과 관련된 음식이 보이는 간단한 어플입니다.

먼저 다음과 같이 기능별로 모듈을 나눴습니다.

 

app: 화면 이동 관리

apple: apple 관련 기능 관리

banana: banana관련 기능 관리

cherry: cherry 관련 기능 관리

durian: durian 관련 기능 관리

 

그리고 각 모듈은 내부 컴포넌트들에 대한 navigation graph를 가지고 있습니다.

App Module

app 모듈의 navigation graph는 다른 모듈들의 navigation graph를 포함하고 있습니다.

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/nav_graph"
    app:startDestination="@id/nav_graph_apple">

    <include app:graph="@navigation/nav_graph_apple" />
    <include app:graph="@navigation/nav_graph_banana" />
    <include app:graph="@navigation/nav_graph_cherry" />
    <include app:graph="@navigation/nav_graph_durian" />

</navigation>

 

app 모듈 내에는 MainActivity가 있고 여기서 bottom navigation item의 클릭에 따라 해당 navigation graph를 실행하도록 연결해줍니다.

binding.bottomView.setOnItemSelectedListener  { item ->
    navController.navigate(
        when(item.itemId) {
            R.id.navigation_a -> com.beeeam.apple.R.id.nav_graph_apple
            R.id.navigation_b -> com.beeeam.banana.R.id.nav_graph_banana
            R.id.navigation_c -> com.beeeam.cherry.R.id.nav_graph_cherry
            R.id.navigation_d -> com.beeeam.durian.R.id.nav_graph_durian
            else -> 0
        }
    )
    return@setOnItemSelectedListener true
}

여기서 item.itemId는 bottom navigation view와 연결된 menu에서 각 아이템들의 id입니다.

 

여기까지만 해도 잘 동작하는 것을 확인할 수 있습니다.

하지만 다음과 같은 모듈과 모듈 간의 이동은 구현할 수 없습니다.

이런 문제는 deeplink를 사용하여 해결할 수 있습니다.

What is deep link?

공식 문서에서는 다음과 같이 설명하고 있습니다.

In Android, a deep link is a link that takes you directly to a specific destination within an app.

 

간단히 설명하자면 앱 내에서 특정 목적지로 이동할 수 있게 하는 기술입니다. 여기서는 deep link를 간단하게 다루니 설명은 여기까지만 하겠습니다.

 

그럼 어떻게 deep link를 사용하여 모듈간의 이동을 구현할까요?

위의 그림을 예시로 설명하겠습니다.

 

먼저 목적지를 link로 만들어줍니다.

navigation graph에서 이동하고자 하는 목적지에 deep link를 선언합니다.

<deepLink app:uri="banana://fragmentBananaMilk" />

uri에 들어가는 링크는 본인이 마음대로 작성하면 됩니다. 대신 xxx://xxxx 라는 문법은 지켜야 합니다.

만약 인자를 전달하고자 하면 xxx://xxxx/{xxx} 형식으로 링크를 구성하면 됩니다.

<?xml version="1.0" encoding="utf-8"?>
<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_banana"
    app:startDestination="@id/navigation_b">
    ...
    <fragment
        android:id="@+id/navigation_banana_milk"
        android:name="com.beeeam.banana.BananaMilkFragment"
        android:label="fragment_banana_milk"
        tools:layout="@layout/fragment_banana_milk" >
        <deepLink app:uri="banana://fragmentBananaMilk" />
    </fragment>
</navigation>

 

그리고 출발지에서는 위에서 작성한 링크를 토대로 navigate 하는 함수를 동작시키면 구현이 가능합니다.

val request = NavDeepLinkRequest.Builder
    .fromUri("banana://fragmentBananaMilk".toUri())
    .build()
findNavController().navigate(request)

위와 같이 도착지 링크를 토대로 NavDeepLinkRequest를 만들어서 navigate 하면 정상적으로 동작하는 것을 확인할 수 있습니다.

예시 코드는 밑의 링크에서 확인할 수 있습니다.

 

https://github.com/BEEEAM-J/Multi_Module_Navigation

 

GitHub - BEEEAM-J/Multi_Module_Navigation

Contribute to BEEEAM-J/Multi_Module_Navigation development by creating an account on GitHub.

github.com

 

'Android' 카테고리의 다른 글

MVI Pattern without Orbit  (0) 2024.06.17
Sticky Header RecyclerView  (0) 2024.04.22
MVVM ViewModel vs AAC ViewModel  (0) 2024.02.15
명령형 vs 선언형, Jetpack Compose  (0) 2024.02.04
MVI  (0) 2024.01.24