Dagger2 는 무엇인가?

Dagger2 탐구와 적용기 작성

DI (Dependency Injection)?

Dependency = 의존성, Injection = 주입

##DI를 사용 하는 이유는?

테스트

  • 모듈을 Testable 하게 만들 수 있음. 독립된 모듈에 대한 테스트 코드 작성 유용함

유지보수

  • 하나의 모듈이 변경 되어도 다른 모듈들이 영향을 받지 않아서, 유지보수 용이함

재사용성

  • 객체 색성을 외부에서 하면 클래스 독립성이 증가되며, 재사용 가능성이 높아짐

Dagger?

DI Framework.

@Inject

  • 주입 받는자, 의존성 주입을 요청
  • 의존성 주입을 통해서 해당 어노테이션이 달린 클래스나 필드에게 값을 주입해 달라고 요청

@Component

  • 연결자, 의존성을 주입하는 역할 (Inject —(Component) — Module)
  • 연결된 Module을 이용하여 의존성 객체를 생성하고 Inject 로 요청받은 인스턴스에 객체 주입

@Module

  • 공급자, Component 에 연결되어 의존성 객체 생성. 생성 후 Scope 따라 관리도 가능

@Scope

  • 생성된 객체의 Lifecycle 범위
  • PerActivity, PerFragment 등으로 화면의 생명주기와 맞추어 사용

Dagger 적용하기

build.gradle

apply plugin: 'kotlin-kapt'  
​  
dependencies {  
 implementation 'com.google.dagger:dagger-android:2.21'  
 implementation 'com.google.dagger:dagger-android-support:2.21'  
 kapt 'com.google.dagger:dagger-android-processor:2.21'  
 kapt 'com.google.dagger:dagger-compiler:2.21'  
}

Application Component

AppComponent.kt

@Component(  
 // 연결할 모듈을 정의  
 modules = [  
 AndroidSupportInjectionModule::class,  
 AppModule::class,  
 NetworkModule::class,  
 ActivityBuilder::class]  
)  
@Singleton  
interface AppComponent : AndroidInjector<AppApplication>{  
​  
 // Application 연결을 도와줄 Builder 정의  
 @Component.Builder  
 abstract class Builder : AndroidInjector.Builder<AppApplication>()  
​  
}

어플리케이션 수명 = 컴포넌트 수명

@Component 를 붙이면 어노테이션 프로세서에 이해 자동으로 생성되는 클래스 명이 DaggerAppComponent 가 됨

Application

AppApplication.kt

class AppApplication : DaggerApplication() {  
​  
 // AppComponent 에서 정의한 Builder 로 Component 와 연결  
 override fun applicationInjector(): AndroidInjector<out DaggerApplication> {  
 return DaggerAppComponent.builder().create(this)  
 }  
}

DaggerApplication 로 만든 후, 이전에 만든 컴포넌트를 이용하여 안드로이드 인젝터 생성

Application Module

AppModule.kt

@Module  
class AppModule {  
​  
 // DataRepository 타입 의존성 객체 생성  
 @Provides  
 @Singleton  
 internal fun provideRepository(apiService: ApiService): DataRepository = DataRepository(apiService)  
}

어플리케이션 생명주기 동안, 살아있는 객체들을 제공함 (@Provides 달린 메서드에 @Singleton 스코프 사용하는 이유)

Activity Module

ActivityBuilder.kt

@Module  
abstract class ActivityBuilder {  
​  
 // ContributesAndroidInjector 어노테이션을 달고, 반환타입을 통해 해당 Activity Subcomponent 생성  
 // SubComponent 와 연결할 modules 정의,  Module 들이 실제 의존성 객체를 생성  
 @ActivityScope  
 @ContributesAndroidInjector(modules = [ProfileActivityModule::class])  
 abstract fun bindMainActivity(): ProfileActivity  
}

@PerActivity

  • 액티비티 생명주기에 맞추어 객체 관리 (어노테이션이 달려있는 클래스는 액티비티가 살아있는 동안 존재)

@ContirubutesAndroidInjector

  • 반환하는 객체가 생성되는 시점에 injection을 제공할 모듈을 명시

###Scope

PerActivity.kt

@Scope  
@Retention(AnnotationRetention.RUNTIME)  
annotation class PerActivity

PerFragment.kt

@Scope  
@Retention(AnnotationRetention.RUNTIME)  
annotation class PerFragment

객체의 수명이 Activity orFragment 의 수명을 따를 경우 사용하는 사용자 정의 스코프 어노테이션.

ProfileActivity.kt

// DaggerAppCompatActivity 상속받아 Component 연결  
class ProfileActivity : DaggerAppCompatActivity() {  
​  
 override fun onCreate(savedInstanceState: Bundle?) {  
 super.onCreate(savedInstanceState)  
 setContentView(R.layout.activity_main)  
 setSupportActionBar(toolbar)  
​  
 supportFragmentManager.beginTransaction()  
 .replace(R.id.container, ProfileFragment())  
 .commitNow()  
 }  
}

ProfileFragment.kt

// DaggerFragment 상속받아 Component 연결  
class ProfileFragment : DaggerFragment() {  
​  
 // DataRepository 타입 의존성 주입 요청  
 @Inject  
 lateinit var repository: DataRepository  
​  
 override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {  
 return inflater.inflate(R.layout.fragment_main, container, false)  
 }  
​  
 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {  
 super.onViewCreated(view, savedInstanceState)  
 ....  
 }  
}

소스코드

android-dagger-example - github Dagger2 sample app

참고 하면 좋은글

Written on April 4, 2019