Slide 1

Slide 1 text

Dagger 아닌 Hilt로 Android DI 하기 이승민 Google Developers Expert Banksalad Frontend Engineering Manager

Slide 2

Slide 2 text

2019 | Confidential and Proprietary 목차 1. DI Recap 2. Dagger Recap 3. A New DI Solution Hilt 4. Hilt 사용하기 5. Hilt 살펴보기 6. 정리

Slide 3

Slide 3 text

DI Recap

Slide 4

Slide 4 text

० Dependency Injection (의존성 주입) ० 의존성 객체를 외부로부터 주입받는다 DI는 무엇인가? A Class B Class new

Slide 5

Slide 5 text

No Dependency Injection class GithubRepoViewModel( private val repository: GithubRepository = GithubRepository() )

Slide 6

Slide 6 text

class GithubRepoViewModel( private val repository: GithubRepository = GithubRepository() ) No Dependency Injection 의존성 생성

Slide 7

Slide 7 text

With Dependency Injection class GithubRepoViewModel( private val repository: GithubRepository ) fun main() { GithubRepoViewModel( GithubRepository() ) }

Slide 8

Slide 8 text

class GithubRepoViewModel( private val repository: GithubRepository ) fun main() { GithubRepoViewModel( GithubRepository() ) } With Dependency Injection 의존성 주입 Constructor Parameter

Slide 9

Slide 9 text

Dagger Recap

Slide 10

Slide 10 text

Dagger는 무엇인가? ० Square에서 만든 DI 라이브러리 ० Google에서 Square의 Dagger를 포크하여 Dagger2 개발 ० Annotation 기반 컴파일 타임 Generated Code로 의존성 주입

Slide 11

Slide 11 text

Dagger의 장점과 단점 장점 ० 컴파일 타임에 검증한다. 안전하다. ० 컴파일 타임 Generated Code로 동작하니 런타임 퍼포먼스에 영향을 주지 않는다

Slide 12

Slide 12 text

Dagger의 장점과 단점 장점 ० 컴파일 타임에 검증한다. 안전하다. ० 컴파일 타임 Generated Code로 동작하니 런타임 퍼포먼스에 영향을 주지 않는다 단점 ० 학습비용이 높다. 배우기 어렵다 ० 많은 보일러 플레이트 코드가 필요하다

Slide 13

Slide 13 text

Dagger는 왜 어려울까? ० Android는 Application, Activity 등 프레임워크 클래스 단위로 의존성 주입 ० Android 프레임워크 클래스의 객체는 OS에서 자체 생성 ० 외부 라이브러리 Dagger가 OS에서 생성되는 프레임워크 클래스 단위로 의존성을 주입하기 위해 많은 지식과 보일러 플레이트 코드 요구

Slide 14

Slide 14 text

Dagger Boilerplate Code @Singleton @Component(modules = [NetworkModule::class, SubcomponentsModule::class]) interface ApplicationComponent { fun inject(activity: LoginActivity) fun loginComponent(): LoginComponent.Factory } @Subcomponent interface LoginComponent { @Subcomponent.Factory interface Factory { fun create(): LoginComponent } fun inject(loginActivity: LoginActivity) fun inject(usernameFragment: LoginUsernameFragment) fun inject(passwordFragment: LoginPasswordFragment) } @Module(subcomponents = LoginComponent::class) class SubcomponentsModule {}

Slide 15

Slide 15 text

Dagger Boilerplate Code @Singleton @Component(modules = [NetworkModule::class, SubcomponentsModule::class]) interface ApplicationComponent { fun inject(activity: LoginActivity) fun loginComponent(): LoginComponent.Factory } @Subcomponent interface LoginComponent { @Subcomponent.Factory interface Factory { fun create(): LoginComponent } fun inject(loginActivity: LoginActivity) fun inject(usernameFragment: LoginUsernameFragment) fun inject(passwordFragment: LoginPasswordFragment) } @Module(subcomponents = LoginComponent::class) class SubcomponentsModule {} Component, Module 정의만 한세월 Subcomponent는 Component와 무엇이 다르지? Factory는 뭐지?

Slide 16

Slide 16 text

Dagger의 강력한 기능은 그대로 가지면서 사용하기 쉬운 더 좋은 DI 라이브러리는 없을까?

Slide 17

Slide 17 text

A New DI Solution Hilt

Slide 18

Slide 18 text

Hilt는 무엇인가요? Dagger 기반 ० Dagger 기반으로 Google이 발전시킨 DI 라이브러리 ० Dagger의 강력한 기능을 그대로 활용 가능 dependencies { implementation "com.google.dagger:hilt-android:2.28-alpha" kapt "com.google.dagger:hilt-android-compiler:2.28-alpha" }

Slide 19

Slide 19 text

Hilt는 무엇인가요? Dagger 기반 ० Dagger 기반으로 Google이 발전시킨 DI 라이브러리 ० Dagger의 강력한 기능을 그대로 활용 가능 Jetpack 라이브러리 ० Jetpack에 포함 ० Activity 등 Android 프레임워크 클래스를 위한 보일러 플레이트 코드 삭제. 의존성을 주입하는 DI 본연의 목적에 집중.

Slide 20

Slide 20 text

Hilt는 무엇인가요? Dagger 기반 ० Dagger 기반으로 Google이 발전시킨 DI 라이브러리 ० Dagger의 강력한 기능을 그대로 활용 가능 Jetpack 라이브러리 ० Jetpack에 포함 ० Activity 등 Android 프레임워크 클래스를 위한 보일러 플레이트 코드 삭제. 의존성을 주입하는 DI 본연의 목적에 집중. Dagger의 강력한 기능은 그대로 가지면서 사용하기 쉬운 더 좋은 DI 라이브러리

Slide 21

Slide 21 text

Hilt 사용하기

Slide 22

Slide 22 text

Hilt 시작하기 ० Hilt Code Generation 시작점 지정하기 (의존성 주입 시작점 지정하기) ० @HiltAndroidApp @AndroidEntryPoint

Slide 23

Slide 23 text

@HiltAndroidApp @AndroidEntryPoint @HiltAndroidApp class BaseApplication : Application() @AndroidEntryPoint class GithubReposActivity : AppCompatActivity() {

Slide 24

Slide 24 text

Hilt 사용하기 ० Constructor을 호출하여 의존성을 주입받는 포인트 선언 ○ @Inject ० 의존성을 생성하는 Constructor 호출 포인트 선언 ○ @Inject constructor ○ @Module @Provides @Binds

Slide 25

Slide 25 text

Hilt 사용하기 ० Constructor을 호출하여 의존성을 주입받는 포인트 선언 ○ @Inject ० 의존성을 생성하는 Constructor 호출 포인트 선언 ○ @Inject constructor ○ @Module @Provides @Binds

Slide 26

Slide 26 text

의존성 주입받기 - @Inject @AndroidEntryPoint class GithubReposActivity : AppCompatActivity() { @Inject lateinit var adapter: GithubReposAdapter }

Slide 27

Slide 27 text

의존성 주입받기 - @Inject @AndroidEntryPoint class GithubReposActivity : AppCompatActivity() { @Inject lateinit var adapter: GithubReposAdapter } 의존성을 주입받으려는 변수에 @Inject

Slide 28

Slide 28 text

의존성 주입받기 - @Inject @AndroidEntryPoint class GithubReposActivity : AppCompatActivity() { @Inject lateinit var adapter: GithubReposAdapter } 의존성을 주입받으려는 변수에 @Inject 어떤 constructor를 호출하지?

Slide 29

Slide 29 text

Hilt 사용하기 ० Constructor을 호출하여 의존성을 주입받는 포인트 선언 ○ @Inject ० 의존성을 생성하는 Constructor 호출 포인트 선언 ○ @Inject constructor ○ @Module @Provides @Binds

Slide 30

Slide 30 text

의존성 생성하기 - @Inject constructor @AndroidEntryPoint class GithubReposActivity : AppCompatActivity() { @Inject lateinit var adapter: GithubReposAdapter } 의존성을 주입받으려는 변수에 @Inject 어떤 constructor를 호출하지?

Slide 31

Slide 31 text

의존성 생성하기 - @Inject constructor @AndroidEntryPoint class GithubReposActivity : AppCompatActivity() { @Inject lateinit var adapter: GithubReposAdapter } class GithubReposAdapter : @Inject constructor() 의존성을 생성하는 constructor에 @Inject

Slide 32

Slide 32 text

Hilt 사용하기 ० Constructor을 호출하여 의존성을 주입받는 포인트 선언 ○ @Inject ० 의존성을 생성하는 Constructor 호출 포인트 선언 ○ @Inject constructor ○ @Module @Provides @Binds

Slide 33

Slide 33 text

의존성 생성하기 - @Module @Provides @Binds @Module @InstallIn(ActivityComponent::class) abstract class SchedulerProviderModule { @Binds abstract fun bindSchedulerProvider( schedulerProvider: SchedulerProvider ): SchedulerProviderInterface @Provides fun providesSchedulerProvider(): SchedulerProviderInterface { return SchedulerProvider() } }

Slide 34

Slide 34 text

의존성 생성하기 - @Module @Provides @Binds @Module @InstallIn(ActivityComponent::class) abstract class SchedulerProviderModule { @Binds abstract fun bindSchedulerProvider( schedulerProvider: SchedulerProvider ): SchedulerProviderInterface @Provides fun providesSchedulerProvider(): SchedulerProviderInterface { return SchedulerProvider() } } @Module @InstallIn constructor 호출하는 모듈 선언

Slide 35

Slide 35 text

의존성 생성하기 - @Module @Provides @Binds @Module @InstallIn(ActivityComponent::class) abstract class SchedulerProviderModule { @Binds abstract fun bindSchedulerProvider( schedulerProvider: SchedulerProvider ): SchedulerProviderInterface @Provides fun providesSchedulerProvider(): SchedulerProviderInterface { return SchedulerProvider() } } @Binds @Provides constructor 호출 (의존성 생성)

Slide 36

Slide 36 text

Hilt 사용하기 ० Constructor을 호출하여 의존성을 주입받는 포인트 선언 ○ @Inject ० 의존성을 생성하는 Constructor 호출 포인트 선언 ○ @Inject constructor ○ @Module @Provides @Binds

Slide 37

Slide 37 text

Hilt 사용하기 ० Constructor을 호출하여 의존성을 주입받는 포인트 선언 ○ @Inject ० 의존성을 생성하는 Constructor 호출 포인트 선언 ○ @Inject constructor ○ @Module @Provides @Binds 직관적인 Annotaion만으로 쉽게 DI 가능!

Slide 38

Slide 38 text

Androidx Jetpack Support ० AAC ViewModel 주입 Support

Slide 39

Slide 39 text

의존성 생성하기 - @ViewModelInject constructor class GithubReposViewModel @ViewModelInject constructor( private val repository: GithubRepository, private val schedulerProvider: SchedulerProviderInterface, @Assisted private val savedStateHandle: SavedStateHandle ) : ViewModel() {

Slide 40

Slide 40 text

의존성 생성하기 - @ViewModelInject constructor class GithubReposViewModel @ViewModelInject constructor( private val repository: GithubRepository, private val schedulerProvider: SchedulerProviderInterface, @Assisted private val savedStateHandle: SavedStateHandle ) : ViewModel() { 의존성을 생성하는 ViewModel constructor에 @ViewModelInject

Slide 41

Slide 41 text

의존성 주입받기 - by viewModels() class GithubReposViewModel @ViewModelInject constructor( private val repository: GithubRepository, private val schedulerProvider: SchedulerProviderInterface, @Assisted private val savedStateHandle: SavedStateHandle ) : ViewModel() { @AndroidEntryPoint class GithubReposActivity : BaseViewModelActivity() { val viewModel: GithubReposViewModel by viewModels() }

Slide 42

Slide 42 text

의존성 주입받기 - by viewModels() class GithubReposViewModel @ViewModelInject constructor( private val repository: GithubRepository, private val schedulerProvider: SchedulerProviderInterface, @Assisted private val savedStateHandle: SavedStateHandle ) : ViewModel() { @AndroidEntryPoint class GithubReposActivity : BaseViewModelActivity() { val viewModel: GithubReposViewModel by viewModels() } 의존성을 주입받으려는 viewModel 변수에 by androidx.activity.viewModels()

Slide 43

Slide 43 text

@Assisted SavedStateHandle class GithubReposViewModel @ViewModelInject constructor( private val repository: GithubRepository, private val schedulerProvider: SchedulerProviderInterface, @Assisted private val savedStateHandle: SavedStateHandle ) : ViewModel() { @AndroidEntryPoint class GithubReposActivity : BaseViewModelActivity() { val viewModel: GithubReposViewModel by viewModels() }

Slide 44

Slide 44 text

Hilt로 Test하기 ० Unit Test는 지원하지 않음 ○ Mocking으로 의존성 생성 가능 ० 통합 Test에서 Hilt로 의존성 생성 가능 ○ @HiltAndroidTest @HiltAndroidRule ० 테스트 클래스 단위로 Module을 재선언 ○ @UninstallModules

Slide 45

Slide 45 text

통합 Test 의존성 생성 @HiltAndroidTest @RunWith(AndroidJUnit4::class) class SchedulerProviderTest { @get:Rule var hiltRule = HiltAndroidRule(this) @Inject lateinit var schedulerProvider: SchedulerProviderInterface @Before fun init() { hiltRule.inject() } }

Slide 46

Slide 46 text

통합 Test 의존성 생성 @HiltAndroidTest @RunWith(AndroidJUnit4::class) class SchedulerProviderTest { @get:Rule var hiltRule = HiltAndroidRule(this) @Inject lateinit var schedulerProvider: SchedulerProviderInterface @Before fun init() { hiltRule.inject() } } @HiltAndroidTest @HiltAndroidRule 활용하여 inject()

Slide 47

Slide 47 text

테스트 클래스 Module 재선언 @HiltAndroidTest @UninstallModules(SchedulerProviderModule::class) @RunWith(AndroidJUnit4::class) class SchedulerProviderTest { @Module @InstallIn(ApplicationComponent::class) abstract class TestSchedulerProviderModule { @Binds abstract fun bindSchedulerProvider( schedulerProvider: TestSchedulerProvider ): SchedulerProviderInterface } }

Slide 48

Slide 48 text

테스트 클래스 Module 재선언 @HiltAndroidTest @UninstallModules(SchedulerProviderModule::class) @RunWith(AndroidJUnit4::class) class SchedulerProviderTest { @Module @InstallIn(ApplicationComponent::class) abstract class TestSchedulerProviderModule { @Binds abstract fun bindSchedulerProvider( schedulerProvider: TestSchedulerProvider ): SchedulerProviderInterface } } @UninstallModules 활용하여 필요한 모듈 재선언

Slide 49

Slide 49 text

테스트 클래스 Module 재선언 @HiltAndroidTest @UninstallModules(SchedulerProviderModule::class) @RunWith(AndroidJUnit4::class) class SchedulerProviderTest { @Module @InstallIn(ApplicationComponent::class) abstract class TestSchedulerProviderModule { @Binds abstract fun bindSchedulerProvider( schedulerProvider: TestSchedulerProvider ): SchedulerProviderInterface } } @UninstallModules 활용하여 필요한 모듈 재선언 프로덕션 코드에 영향주지 않고 개별 테스트 DI 가능!

Slide 50

Slide 50 text

Hilt 살펴보기

Slide 51

Slide 51 text

Component @Module @InstallIn(ActivityComponent::class)

Slide 52

Slide 52 text

Component ० 각 Android 프레임워크 클래스에서 Component를 들고있다 ○ ApplicationComponent, ActivityComponent, FragmentComponent...

Slide 53

Slide 53 text

Component ० 각 Android 프레임워크 클래스에서 Component를 들고있다 ○ ApplicationComponent, ActivityComponent, FragmentComponent... ० Component가 Module로 의존성을 생성하고, @Inject로 요청한 변수에 의존성을 주입한다 @Module @InstallIn(ActivityComponent::class)

Slide 54

Slide 54 text

Component ० 각 Android 프레임워크 클래스에서 Component를 들고있다 ○ ApplicationComponent, ActivityComponent, FragmentComponent... ० Component가 Module로 의존성을 생성하고, @Inject로 요청한 변수에 의존성을 주입한다 Application, Activity 등 프레임워크 클래스에서 Component를 이용해 DI를 수행한다

Slide 55

Slide 55 text

Component Generated Code @Component( modules = { ApplicationContextModule.class, GithubRepositoryModule.class, SchedulerProviderModule.class, ... } ) interface ApplicationComponent { fun inject(application: Application) }

Slide 56

Slide 56 text

Component Generated Code @Component( modules = { ApplicationContextModule.class, GithubRepositoryModule.class, SchedulerProviderModule.class, ... } ) interface ApplicationComponent { fun inject(application: Application) } Component가 Module을 들고있다

Slide 57

Slide 57 text

Component Generated Code @Component( modules = { ApplicationContextModule.class, GithubRepositoryModule.class, SchedulerProviderModule.class, ... } ) interface ApplicationComponent { fun inject(application: Application) } Application에서 inject(this)을 호출하면 들고있는 Component로 DI를 수행한다 Component가 Module을 들고있다

Slide 58

Slide 58 text

Component & Scope Annotation ० 각 Android 프레임워크 클래스에서 Component를 들고있다 ○ ApplicationComponent, ActivityComponent, FragmentComponent... ० Component가 Module로 의존성을 생성하고, @Inject로 요청한 변수에 의존성을 주입한다 ० Component에 맞는 Scope Annotation 매칭

Slide 59

Slide 59 text

Component & Scope Annotation @Module @InstallIn(ApplicationComponent::class) class GithubRepositoryModule { @Singleton @Provides fun bindGithubRepository(): GithubRepository { return GithubRepository() } }

Slide 60

Slide 60 text

Component & Scope Annotation @Module @InstallIn(ApplicationComponent::class) class GithubRepositoryModule { @Singleton @Provides fun bindGithubRepository(): GithubRepository { return GithubRepository() } } ApplicationComponent - Singleton 매칭

Slide 61

Slide 61 text

Component & Scope Annotation @Module @InstallIn(ActivityComponent::class) abstract class SchedulerProviderModule { @ActivityScoped @Binds abstract fun bindSchedulerProvider( schedulerProvider: SchedulerProvider ): SchedulerProviderInterface }

Slide 62

Slide 62 text

Component & Scope Annotation @Module @InstallIn(ActivityComponent::class) abstract class SchedulerProviderModule { @ActivityScoped @Binds abstract fun bindSchedulerProvider( schedulerProvider: SchedulerProvider ): SchedulerProviderInterface } ActivityComponent - ActivityScoped 매칭

Slide 63

Slide 63 text

Component & Scope Annotation @Module @InstallIn(ActivityComponent::class) abstract class SchedulerProviderModule { @ActivityScoped @Binds abstract fun bindSchedulerProvider( schedulerProvider: SchedulerProvider ): SchedulerProviderInterface } ActivityComponent - ActivityScoped 매칭 Scope Annotation 없으면 매번 새로 생성!

Slide 64

Slide 64 text

Component, Scope 작성할때는 알겠는데 각 화면에서 의존성이 어디에서 왔는지 알기 어려워요!

Slide 65

Slide 65 text

의존성 그래프 네비게이션 지원 ० Android Studio 4.1부터 gutter로 Dagger Hilt 의존성 그래프 네비게이션 지원 출처: https://medium.com/androiddevelopers/dagger-navigation-support-in-android-studio-49aa5d149ec9

Slide 66

Slide 66 text

Migrating Dagger to Hilt ० https://dagger.dev/hilt/migration-guide.html

Slide 67

Slide 67 text

Migrating Dagger to Hilt ० https://dagger.dev/hilt/migration-guide.html ○ Component to @EntryPoint ○ Annotate @InstallIn to Modules

Slide 68

Slide 68 text

정리

Slide 69

Slide 69 text

Dagger의 장점과 단점 장점 ० 컴파일 타임에 검증한다. 안전하다. ० 컴파일 타임 Generated Code로 동작하니 런타임 퍼포먼스에 영향을 주지 않는다 단점 ० 학습비용이 높다. 배우기 어렵다 ० 많은 보일러 플레이트 코드가 필요하다

Slide 70

Slide 70 text

Dagger의 강력한 기능은 그대로 가지면서 사용하기 쉬운 더 좋은 DI 라이브러리는 없을까?

Slide 71

Slide 71 text

Hilt는 무엇인가요? Dagger 기반 ० Dagger 기반으로 Google이 발전시킨 DI 라이브러리 ० Dagger의 강력한 기능을 그대로 활용 가능 Jetpack 라이브러리 ० Jetpack에 포함 ० Activity 등 Android 프레임워크 클래스를 위한 보일러 플레이트 코드 삭제. 의존성을 주입하는 DI 본연의 목적에 집중. Dagger의 강력한 기능은 그대로 가지면서 사용하기 쉬운 더 좋은 DI 라이브러리

Slide 72

Slide 72 text

Hilt 사용하기 ० Constructor을 호출하여 의존성을 주입받는 포인트 선언 ○ @Inject ० 의존성을 생성하는 Constructor 호출 포인트 선언 ○ @Inject constructor ○ @Module @Provides @Binds 직관적인 Annotaion만으로 쉽게 DI 가능!

Slide 73

Slide 73 text

Component ० 각 Android 프레임워크 클래스에서 Component를 들고있다 ○ ApplicationComponent, ActivityComponent, FragmentComponent... ० Component가 Module로 의존성을 생성하고, @Inject로 요청한 변수에 의존성을 주입한다 Application, Activity 등 프레임워크 클래스에서 Component를 이용해 DI를 수행한다

Slide 74

Slide 74 text

Reference ० https://developer.android.com/training/dependency-injection/hilt-android ० https://developer.android.com/training/dependency-injection/hilt-jetpack#kotlin ० https://developer.android.com/training/dependency-injection/hilt-testing ० https://medium.com/androiddevelopers/dependency-injection-on-android-with-hilt-67b6031e62d ० https://youtu.be/B56oV3IHMxg ० https://github.com/maryangmin/GDG-Hilt

Slide 75

Slide 75 text

서비스는 커지고 복잡해집니다 코드를 관심사 단위로 분리해야 합니다 의존성을 잘 연결해야 합니다 DI는 필수입니다

Slide 76

Slide 76 text

쉽고 강력한 Hilt로 앱을 더 안정적으로 만드세요 서비스는 커지고 복잡해집니다 코드를 관심사 단위로 분리해야 합니다 의존성을 잘 연결해야 합니다 DI는 필수입니다