[번역] DroidKaigi 2019 ~ 멀티 모듈 프로젝트에서의 Dagger를 사용해 Dependency Injection

[번역] DroidKaigi 2019 ~ 멀티 모듈 프로젝트에서의 Dagger를 사용해 Dependency Injection

Jan 27, 2020. | By: pluulove

본 포스팅은 DroidKaigi 2019 ~ マルチモジュールプロジェクトでの Dagger2を用いた Dependency Injection 을 기본으로 번역하여 작성했습니다

제 일본어 실력으로 인하여 오역이나 오타가 발생할 수 있습니다.

일부 이미지는 이해를 돕고자 한국어로 수정했습니다.


번역에서 사용한 이미지는 원본은 https://speakerdeck.com/kgmyshin/android-multi-module-with-dagger 에 있습니다.


1p

Goal

Multi Module로 구성된 프로젝트에서 Dagger2를 사용해 Dependency Injection을 할 때의 고민 포인트를 알아보고 그 해결책을 이해한다.

Agenda

  • Dependency Injection이란 (간단하게)
  • Dagger2 재입문
  • Dagger2 Android 사이드 재입문
  • 단일 모듈에서 Dagger2를 사용해 Dependency Injection
  • 멀티 모듈에서 Dependency Injection의 어려움
  • Dagger2를 사용해 멀티 모듈 프로젝트로 DI 구현하기
  • Dynamic Feature Module이 있을 때의 이야기 (시간이 있다면)

2p

멀티 모듈 프로젝트에서의 Dagger를 사용해 Dependency Injection

@kgmyshin DroidKaigi 2019

3p, 자기 소개

  • kgmyshin / 釘宮(kugimiya)
  • Android 개발자
  • 함동회사 DMM.com CTO실 직속

4p

이 세션의 목적지

5p, 이 세션의 목적지

멀티 모듈로 구성된 프로젝트에서 Dagger2를 사용해 Dependency Injection을 할 때의 고민 포인트를 알아보고 그 해결책을 이해한다.

6p

agenda

7p, agenda

  • Dependency Injection이란 (간단하게)
  • Dagger2 재입문
  • Dagger2 Android 사이드 재입문
  • 단일 모듈에서 Dagger2를 사용해 Dependency Injection
  • 멀티 모듈에서 Dependency Injection의 어려움
  • Dagger2를 사용해 멀티 모듈 프로젝트로 DI 구현하기
  • Dynamic Feature Module이 있을 때의 이야기

8p, 발표하기 전에

혹시 세션을 듣는 도중에 모르는 부분이 있다면, 꼭 DMM.com 부스로 와주세요!

9p

Dependency Injection이란

10p, Dependency Injection이란 (간단하게)

한마디로, “의존 객체”를 “주입”하는 디자인 패턴입니다

11p ~ 13p, Dependency Injection이란 (간단하게)

internal class AllergenRepositoryImpl : AllergenRepository {
  private val apiClient = ApiClient()
  
  override fun findAll(
  ): Single<List<Allergen>> =
  	apiClient.getAllergens()

  override fun findById(
    allergenId: AllergenId
  ): Maybe<Allergen> =
  	apiClient.getAllergen(allergenId.value)
}

DI가 없다

internal class AllergenRepositoryImpl @Inject constructor(
  private val apiClient: ApiClient
) : AllergenRepository {

  override fun findAll(
  ): Single<List<Allergen>> =
  	apiClient.getAllergens()

  override fun findById(
    allergenId: AllergenId
  ): Maybe<Allergen> =
  	apiClient.getAllergen(allergenId.value)
}

DI 적용

14p, Dependency Injection이란 (간단하게)

장점

  • 결합이 느슨하게 된다.
  • 단위 테스트를 하기 쉬워진다
  • 특정 라이브러리나 프레임워크의 의존도를 낮출 수 있다

15p, 특정 라이브러리나 프레임워크의 의존도를 낮출 수 있다

  • Volley or Retrofit or Ktor Client
  • Picasso or Glide

이외의 의존도를 구상 항목에 가두는 것으로 손쉽게 라이브러리를 교체할 수 있다

16p, 더 제대로 알고 싶으면

Day2 14:50~ 지금부터 시작하는 의존성 주입 by kobakei-san

17p

Dagger2 재입문

18p ~ 22p, 앱에서 DI를 하기 위해서는?

23p, 앱에서 DI를 하기 위해서는?

앱에서 DI를 하기 위해서는?

어디에 무엇을

Inject할지를 망라할 필요가 있다

24p ~ 26p, Dagger에서는

27p ~ 28p, Dagger2에서 Dependency Injection 적용

Dagger2를 사용한 구현의 흐름

  1. Module을 준비
  2. Component를 준비
  3. Application#onCreate로 Component를 초기화
  4. 각 위치에서 inject

※ 3에서는 상황에 따라서는 여기서는 간략화를 위해서 이 위치에 고정한다 ※ 이후의 예에서는 Fragment에 Inject를 대상으로 하지만 다른 클래스로 교체에도 문제없다

29p, ① Module을 준비

30p ~ 34p, ① Module을 준비

Module의 작성법 사례 : 타입과 인스턴스를 연결한다

35p ~ 36p, Dagger2로 Dependency Injection 하기

Dagger2를 사용한 구현의 흐름

  1. Module을 준비
  2. Component를 준비
  3. Application#onCreate로 Component를 초기화
  4. 각 위치에서 inject

※ 3에서는 상황에 따라서는 여기서는 간략화를 위해서 이 위치에 고정한다 ※ 이후의 예에서는 Fragment에 Inject를 대상으로 하지만 다른 클래스로 교체에도 문제없다

37p ~ 39p, ② Component를 준비

Component에는 “무엇을 제공할까” 이외를 적는다

=> “어디에 제공할 것인가”를 주로 적는다

40p ~ 43p, ② Component를 준비

Component 작성법의 예 : inject 하는 곳을 파라미터로 하는 함수를 정의

@Singleton
@Component(modules = [GreetingModule::class]) 
interface GreetingComponent {
  fun inject(activity: GreetingActivity) // GreetingActivity에 inject
  fun inject(fragment: GreetingFragment) // GreetingFragment에 inject
  fun greeter(): Greeter // 어디든지!
}

44p ~ 45p, Dagger2로 Dependency Injection 하기

Dagger2를 사용한 구현의 흐름

  1. Module을 준비
  2. Component를 준비
  3. Application#onCreate로 Component를 초기화
  4. 각 위치에서 inject

※ 3에서는 상황에 따라서는 여기서는 간략화를 위해서 이 위치에 고정한다 ※ 이후의 예에서는 Fragment에 Inject를 대상으로 하지만 다른 클래스로 교체에도 문제없다

46p ~ 47p, ③ Application#onCreate로 Component를 초기화

class Application : android.app.Application() {
  lateinit var appComponent: AppComponent
  
  override fun onCreate() {
    super.onCreate()
    appComponent = DaggerAppComponent.create() // 초기화
	}
}

48p ~ 49p, Dagger2로 Dependency Injection 하기

Dagger2를 사용한 구현의 흐름

  1. Module을 준비
  2. Component를 준비
  3. Application#onCreate로 Component를 초기화
  4. 각 위치에서 inject

※ 3에서는 상황에 따라서는 여기서는 간략화를 위해서 이 위치에 고정한다 ※ 이후의 예에서는 Fragment에 Inject를 대상으로 하지만 다른 클래스로 교체에도 문제없다

50p ~ 52p, ④ 각 위치에서 inject

53p, Dagger2로 Dependency Injection 하기

Dagger2를 사용한 구현의 흐름

  1. Module을 준비
  2. Component를 준비
  3. Application#onCreate로 Component를 초기화
  4. 각 위치에서 inject

※ 3에서는 상황에 따라서는 여기서는 간략화를 위해서 이 위치에 고정한다 ※ 이후의 예에서는 Fragment에 Inject를 대상으로 하지만 다른 클래스로 교체에도 문제없다

54p

Dagger-Android 재입문

55p ~ 57p, Dagger-Android 모티베이션

58p, Dagger-Android 모티베이션

59p, dagger.android로 Dependency Injection

Dagger2를 사용한 구현의 흐름

  1. Module을 준비
  2. Component를 준비
  3. Application#onCreate로 Component를 초기화
  4. 각 위치에서 inject

60p ~ 62p, dagger.android로 Dependency Injection

dagger.android 사용한 구현의 흐름

  1. Module을 준비
  2. FragmentModule을 준비 (New)
  3. Component를 준비 (Updated)
  4. Application#onCreate로 Component를 초기화 (Updated)
  5. 각 위치에서 inject (Updated)

63p, ① Module을 준비

@Module
abstract class AppModule {
  @Binds
  abstract fun messageProvider(impl: MessageProviderImpl): MessageProvider 
}

64p ~ 65p, dagger.android로 Dependency Injection

dagger.android 사용한 구현의 흐름

  1. Module을 준비
  2. FragmentModule을 준비
  3. Component를 준비
  4. Application#onCreate로 Component를 초기화
  5. 각 위치에서 inject

66p ~ 68p, ② FragmentModule을 준비

@Module
abstract class MainFragmentModule {
  @ContributesAndroidInjector
  abstract fun mainFragment(): MainFragment 
}

69p ~ 70p, dagger.android로 Dependency Injection

dagger.android 사용한 구현의 흐름

  1. Module을 준비
  2. FragmentModule을 준비
  3. Component를 준비
  4. Application#onCreate로 Component를 초기화
  5. 각 위치에서 inject

71p ~ 75p, ③ Component를 준비

@Singleton
@Component(
  modules = [ 
    AndroidInjectionModule::class, // 추가
    AppModule::class, 
    MainFragmentModule::class // 추가
  ]
)
interface AppComponent : AndroidInjector<Application>

76p ~ 77p, dagger.android로 Dependency Injection

dagger.android 사용한 구현의 흐름

  1. Module을 준비
  2. FragmentModule을 준비
  3. Component를 준비
  4. Application#onCreate로 Component를 초기화
  5. 각 위치에서 inject

78p ~ 79p, ④ Application#onCreate로 Component를 초기화

80p ~ 81p, dagger.android로 Dependency Injection

dagger.android 사용한 구현의 흐름

  1. Module을 준비
  2. FragmentModule을 준비
  3. Component를 준비
  4. Application#onCreate로 Component를 초기화
  5. 각 위치에서 inject

82p ~ 83p, ⑤ 각 위치에서 inject

84p, dagger.android로 Dependency Injection

dagger.android 사용한 구현의 흐름

  1. Module을 준비
  2. FragmentModule을 준비
  3. Component를 준비
  4. Application#onCreate로 Component를 초기화
  5. 각 위치에서 inject

85p ~ 86p, 구조

87p ~ 88p, 구조

89p, 구조

90p ~ 97p, 구조

MainFragment에서 Inject 요청 시 먼저 부모 Activity에서 HasSupportFragmentInjector를 구현했는지 확인한 후, Application으로 이동해서 확인한다. 실제 Injector 구현체에 도달하는 흐름이다.

98p

멀티 모듈에서 Dependency Injection의 어려움

99p ~ 100p, 멀티 모듈에서 DI

101p ~ 102p, dagger.android로 Dependency Injection

지금까지와 같은 순서로 dagger.android를 사용해 구현해본다

  1. Module을 준비
  2. FragmentModule을 준비
  3. Component를 준비
  4. Application#onCreate로 Component를 초기화
  5. 각 위치에서 inject

103p ~ 104p, ④ Application#onCreate로 Component를 초기화

각각의 Component Inject가 호출한다

105p ~ 106p, dagger.android로 Dependency Injection

지금까지와 같은 순서로 dagger.android를 사용해 구현해본다

  1. Module을 준비
  2. FragmentModule을 준비
  3. Component를 준비
  4. Application#onCreate로 Component를 초기화
  5. 각 위치에서 inject

107p ~ 108p, ⑤ 각 위치에서 inject

109p ~ 110p, 어떻게 될까?

111p ~ 112p, Component 초기화 순서를 바꿔보면?

113p, 어떻게 될까?

114p ~ 116p, Crash 나는 이유

117p ~ 118p, 구조

119p

Dagger2를 사용해 멀티 모듈 프로젝트로 DI 구현하기

120p ~ 122p, 다시 한번 집어보면, 문제는?

문제

Application에 Fragment용 Injector를 하나밖에 둘 수 없으므로, Injector가 덮어쓰게된다

123p, 해결책의 하나

Application에서 가지는 하나의 FragmentInjector가 복수의 Injector를 가지도록 한다

124p ~ 127p, 나의 Injector가 복수의 Injector를 가지도록 한다

029 

128p ~ 133p, ModuleInjector

134p ~ 136p, dagger.android로 Dependency Injection (multi module)

지금까지와 같은 순서로 dagger.android를 사용해 구현해본다

  1. Module을 준비
  2. FragmentModule을 준비
  3. Injector를 준비 (New)
  4. Component를 준비 (Updated)
  5. Application#onCreate로 Component를 초기화 (Updated)
  6. 각 위치에서 inject

137p, ① Module을 준비

@Module
internal abstract class CounterModule {
  @Singleton
  @Binds
  abstract fun provideCounter(impl: CounterImpl): Counter
}

@Module
internal abstract class GreetingModule {
  @Binds
  abstract fun provideGreeter(impl: GreeterImpl): Greeter
}

138p ~ 139p, dagger.android로 Dependency Injection (multi module)

지금까지와 같은 순서로 dagger.android를 사용해 구현해본다

  1. Module을 준비
  2. FragmentModule을 준비
  3. Injector를 준비
  4. Component를 준비
  5. Application#onCreate로 Component를 초기화
  6. 각 위치에서 inject

140p, ② FragmentModule을 준비

@Module
internal abstract class GreetingFragmentModule {
  @ContributesAndroidInjector
  abstract fun greetingFragment(): GreetingFragment
}

@Module
internal abstract class CounterFragmentModule {
  @ContributesAndroidInjector
  abstract fun counterFragment(): CounterFragment 
}

141p ~ 142p, dagger.android로 Dependency Injection (multi module)

지금까지와 같은 순서로 dagger.android를 사용해 구현해본다

  1. Module을 준비
  2. FragmentModule을 준비
  3. Injector를 준비
  4. Component를 준비
  5. Application#onCreate로 Component를 초기화
  6. 각 위치에서 inject

143p, ③ Injector를 준비

class CounterInjector : HasDispatchingInjector<Fragment> {
  @Inject
  lateinit var supportFragmentInjector: DispatchingAndroidInjector<Fragment> 
  override fun dispatchingAndroidInjector(): DispatchingAndroidInjector<Fragment> =
  		supportFragmentInjector
}

class GreetingInjector : HasDispatchingInjector<Fragment> { 
  @Inject
  lateinit var supportFragmentInjector: DispatchingAndroidInjector<Fragment> 
  override fun dispatchingAndroidInjector(): DispatchingAndroidInjector<Fragment> =
  		supportFragmentInjector
}

144p ~ 145p, dagger.android로 Dependency Injection (multi module)

지금까지와 같은 순서로 dagger.android를 사용해 구현해본다

  1. Module을 준비
  2. FragmentModule을 준비
  3. Injector를 준비
  4. Component를 준비
  5. Application#onCreate로 Component를 초기화
  6. 각 위치에서 inject

146p, ④ Component를 준비

@Singleton 
@Component(
  modules = [ 
    AndroidInjectionModule::class,
    CounterModule::class,
    CounterFragmentModule::class])
interface CounterComponent : AndroidInjector<CounterInjector>

@Singleton 
@Component(
  modules = [ 
    AndroidInjectionModule::class,
    GreetingModule::class,
    GreetingFragmentModule::class])
interface GreetingComponent : AndroidInjector<GreetingInjector>

147p ~ 148p, dagger.android로 Dependency Injection (multi module)

지금까지와 같은 순서로 dagger.android를 사용해 구현해본다

  1. Module을 준비
  2. FragmentModule을 준비
  3. Injector를 준비
  4. Component를 준비
  5. Application#onCreate로 Component를 초기화
  6. 각 위치에서 inject

149p ~ 153p, ⑤ Application#onCreate로 Component를 초기화

154p ~ 155p, dagger.android로 Dependency Injection (multi module)

지금까지와 같은 순서로 dagger.android를 사용해 구현해본다

  1. Module을 준비
  2. FragmentModule을 준비
  3. Injector를 준비
  4. Component를 준비
  5. Application#onCreate로 Component를 초기화
  6. 각 위치에서 inject

156p, ⑥ 각 위치에서 inject

class CounterFragment : Fragment() { 
  @Inject
  lateinit var counter: Counter

  override fun onAttach(context: Context?) {
    AndroidSupportInjection.inject(this)
    super.onAttach(context)
  }
  ...
}
class GreetingFragment : Fragment() { 
  @Inject
  lateinit var greeter: Greeter

  override fun onAttach(context: Context?) {
    AndroidSupportInjection.inject(this)
    super.onAttach(context)
  }
  ...
}

157p ~ 166p, 흐름

167p

Dynamic Feature Module

168p ~ 170p, Dynamic Feature Module이 있는 경우의 DI

171p ~ 172p, dagger.android로 Dependency Injection (multi module)

지금까지와 같은 순서로 dagger.android를 사용해 구현해본다

  1. Module을 준비
  2. FragmentModule을 준비
  3. Injector를 준비
  4. Component를 준비
  5. Application#onCreate로 Component를 초기화 ← X 불가능
  6. 각 위치에서 inject

173p ~ 174p, ⑤ Application#onCreate로 Component를 초기화

175p ~ 176p, 해결책 중 하나

177p ~ 182p, 예를들면, 이런 함수를 만든다

  1. Application내에 원하는 모듈이 있는지 찾는다
  2. 만약 없으면. 원하는 Injector를 만들고 application에 추가한다
  3. AndroidSupportInjection#inject을 호출한다

Activity에 moduleInjector가 있는 경우를 고려해서 AndroidSupportInjection과 같은 구현을 할 수도 있다

※ ActivityInjector를 가질 때에는 이 방법은 잘 안되므로 조건 분기할 필요가 있다!

183p ~ 184p, 이렇게 하면 inject되는 곳은 이 변경만으로 지금처럼 움직인다

185p ~ 186p, 언급한 것을 되짚어보기

  • Dependency Injection이란
  • Dagger2 사용 방법 사례, 순서, 구조
  • Dagger Android Support의 사용 방법 사례, 순서, 구조
  • 위 내용대로는 멀티 모듈 대응이 안되는 케이스가 있다
  • ModuleInjector를 도입하는 것으로 대응 가능
  • Dynamic Feature Module일 때는 적절한 타이밍에 Injector를 ModuleInjector에 꽂아 넣는 것을 필요가 있다

187p

들어주셔서 감사합니다.

comments powered by Disqus

Currnte Pages Tags

Android DroidKaigi

About

Pluu, Android Developer Blog Site

이전 블로그 링크 :네이버 블로그

Using Theme : SOLID SOLID Github

Social Links