[Multi-Module] navigation with multi-module

1 분 소요

👩🏻‍💻 멀티모듈에서 네비게이션 사용하기

현재 feature 모듈을 도메인별로 feature:main, feature:login으로 나눈 상태이다. 이렇게 모듈을 분리했을 때, 모듈 사이 엑티비티 전환은 어떻게 진행해야 하는지를 이번 게시글을 통해 다뤄보고자 한다.

👩🏻‍💻 멀티모듈 구조

우선 프로젝트에 멀티모듈을 적용했다면 크게 공식문서에 나온 것 과 같이 모듈이 분리될 것이다.

여기서 우리가 짚고 넘어갈 부분이 있다.

  • 각 기능 모듈은 직접 또는 간접적으로 app 모듈에 포함된다.
      implementation(project(":domain"))
      implementation(project(":data"))
      implementation(project(":common-ui"))
      implementation(project(":feature:main"))
      implementation(project(":feature:login"))
    

    아마 모듈을 생성할 때마다 다음과 같이 app 모듈에 dependency를 걸어주었을 것이다.

    - app 모듈의 역할

    app 모듈은 각 feature 모듈에 종속되어 있다는 점에서 우리는 app 모듈을 활용해 각 모듈 사이 navigation 처리를 진행할 수 있다.

👩🏻‍💻 진행 과정

1. app module이 모든 feature module에 의존성이 존재함

  • 모둘 사이 실제 navigate 로직이 정의됨


2. feature 모듈에서는 app 모듈을 모르는데?

  • feature 모듈이 공통적으로 아는 common-ui 모듈을 사용한다.
    • navigator Interface 정의
        interface FakeAttNavigator {
            fun navigateToMain(context: Context)
        }
      
    • navigator Injector 정의
      • 뒤에 코드 참조!


3. app에서는 common-ui를 안다!

  • common-ui의 navigator Interface를 구현
      class AttNavigator @Inject constructor(): FakeAttNavigator {
          override fun navigateToMain(context: Context) {
              val intent = Intent(context, MainActivity::class.java)
              context.startActivity(intent)
          }
      }
    
  • hilt 모듈을 생성해 interface와 구현체 bind
      @InstallIn(SingletonComponent::class)
      @Module
      interface NavigatorModule {
          @Binds
          fun bindAttNavigator(
              attNavigator: AttNavigator
          ): FakeAttNavigator
      }
    


4. common-ui에서 hilt를 통해 주입되는 실제 navigator 활용

    sealed interface AttInjector {

        @EntryPoint
        @InstallIn(ActivityComponent::class)
        interface AttNavigatorInjector {
            fun attNavigator(): FakeAttNavigator
        }

    }


5. feature 모듈에서 common-ui를 활용해 navigate 처리

  // attNavigator 가져오기
      private val attNavigator: FakeAttNavigator by lazy {
        EntryPointAccessors.fromActivity(
            requireActivity(),
            AttInjector.AttNavigatorInjector::class.java
        ).attNavigator()
      }

  // navigate 처리
    attNavigator.navigateToMain(requireContext())


👩🏻‍💻 추가적인 개념

EntryPoint

  • EntryPoint가 뭐지?

    An entry point is the boundary where you can get Dagger-provided objects from code that cannot use Dagger to inject its dependencies.

    • dagger가 제공하는 객체를 get할 수 있는 경계점이라 생각하면 될 것 같다.


  • EntryPoint를 언제 사용해야 하는데?

    You will need an entry point when interfacing with non-Dagger libraries or Android components that are not yet supported in Hilt and need to get access to Dagger objects.

    • hilt에 의해서 아직 제공되지 않는 라이브러리나 android component의 interface를 얻고자 할 때 필요로 한다.


  • 어떻게 사용하는데?

      @EntryPoint
      @InstallIn(SingletonComponent::class)
      interface FooBarInterface {
        @Foo fun bar(): Bar
      }
    
    1. interface를 정의하고 제공하고자 하는 객체에 access할 수 있는 함수를 포함시킨다.
    2. @EntryPoint를 붙이고, @InstallIn을 통해 entryPoint를 어디에 설치할지를 정한다.
      attNavigator = EntryPoints.get(requireActivity(), AttNavigatorInjector::class.java).attNavigator()
    

    -> 공식문서에 나온 방법

      private val attNavigator: FakeAttNavigator by lazy {
            EntryPointAccessors.fromActivity(
                requireActivity(),
                AttNavigatorInjector::class.java
            ).attNavigator()
        }
    

    -> delegate 패턴으로 사용

댓글남기기