wanna be dev 🧑‍💻

Cool 하고 Sick한 개발자가 되고 싶은 uzun입니다

A.K.A. Kick-snare, hyjhyj0901, h_uz99 solvedac-logo

Android/Study

⚔️ Dagger Hilt 안드로이드 DI 라이브러리 알아보기

Kick_snare 2022. 8. 20. 20:24
728x90

Dependency Injection이란?

https://uzun.dev/133

 

Dependency Injection 의존성 주입이란?

DI 의존성 주입이란? 하나의 객체가 다른 객체의 의존성을 제공하는 기술. 외부에서 의존 객체를 생성하여 넘겨주는 기법을 의미한다. 일반적인 객체 생성은 클래스 안에서 사용할 객체를 생성

uzun.dev

의존성 주입의 개념을 모른다면 먼저 위 포스팅을 읽어 보자.

Dependency Injection in Android

의존성 주입은 최근 안드로이드 개발환경에 있어서 가장 주목받는 디자인패턴 중 하나이다. 각종 컴포넌트 간 의존성이 상당히 강한 안드로이드 프레임 워크에서 클래스간 의존도를 낮추는 것은 매우 중요하다.

인스턴스를 클래스 외부에서 주입하기 위해서 인스턴스에 대한 전반적인 생명주기의 관리가 필요하다. 프로젝트의 규모가 커질수록 의존성 인스턴스들을 일일히 관리하는 것은 많은 리소스를 필요로 한다. 그래서 이를 전반적으로 관리하기 위해 Google이 주도적으로 밀어주는 Dagger2 를 사용했었다.

Dagger는 안드로이드와 크게 상관관계가 없지만 인기를 끌고 사용되었고 보일러 플레이트를 줄여주는 Dagger-Android도 함께 지원주었다. 하지만 Dagger는 높은 러닝커브를 가지며 초기 DI 환경을 구축하는데 오히려 매뉴얼한 방법보다 리소스가 많이 들 수 있다. 이러한 이유로 Kotlin 언어적 특성을 활용하여 러닝커브가 상대적으로 낮은 Koin 또한 많은 인기를 얻고 있다.

Koin은 사용이 간결하지만 엄밀하게 DI와는 다른 개념이며, 결과적으로 프로젝트의 규모가 커질 수록 컴파일 타임에 많은 일을 처리하는 Dagger에 비해 런타임 퍼포먼스가 떨어진다고 한다. 이에 Google은 Dagger-Android 보다 초기 구축 비용을 훨씬 절감시키고, 안드로이드 프레임워크 환경에서 강력한 Dagger Hilt를 발표하게 되었다.

Dagger Hilt

Hilt는 2020년 6월 구글에서 발표한 안드로이드 전용 DI 라이브러리이다. Dagger2 기반으로 안드로이드 프레임워크에서 표준적으로 사용되는 DI 컴포넌트와 scope를 기본적으로 제공하여, 초기 DI 구축 비용을 절감한다. 이에 따라 보일러 플레이트를 대폭 줄이고 가독성과 유지보수 면에서 이득을 취할 수 있다. 또한 Jetpack의 ViewModel에 대한 DI도 큰 비용없이 구현가능하다.

SetUp

classpath 'com.google.dagger:hilt-android-gradle-plugin:$version'
apply plugin: 'kotlin-kapt'
apply plugin: 'dagger.hilt.android.plugin'

...

implementation "com.google.dagger:hilt-android:2.28.1"
kapt "com.google.dagger:hilt-android-compiler:2.28.1"

Hilt Application

@HiltAndroidApp 어노테이션을 사용하여 컴파일 타임 시 표준 컴포넌트 빌드에 필요한 클래스를 초기화

@HiltAndroidApp
class SomeApplication : Application()

Hilt 셋업을 위해 필수적으로 요구되는 과정

Component Hierachy

Hilt에서는

  • 안드로이드 환경에서 표준적으로 사용되는 컴포넌트를 기본적으로 제공
  • 내부적으로 제공하는 컴포넌트들의 전반적인 라이프 사이클 또한 자동으로 관리

위는 Hilt에서 제공하는 표준 컴포넌트 계층 구조이다.

Component Scope Created at Destroyed at

SingletonComponent @Singleton Application#onCreate() Application#onDestroy()
ActivityRetainedComponent @ActivityRetainedScoped Activity#onCreate()1 Activity#onDestroy()1
ViewModelComponent @ViewModelScoped ViewModel created ViewModel destroyed
ActivityComponent @ActivityScoped Activity#onCreate() Activity#onDestroy()
FragmentComponent @FragmentScoped Fragment#onAttach() Fragment#onDestroy()
ViewComponent @ViewScoped View#super() View destroyed
ViewWithFragmentComponent @ViewScoped View#super() View destroyed
ServiceComponent @ServiceScoped Service#onCreate() Service#onDestroy()

각 컴포넌트는 자신만의 Lifetime을 가진다. 위는 각 컴포넌트 들의 관련 Scope의 생성 및 파괴 시점이다.

  • ApplicationComponent - Application 전체의 생명주기를 lifetime으로 갖습니다. Application이 생성되는(onCreate) 시점에 함께 생성되고, Application이 파괴되는(onDestroy) 시점에 함께 파괴됩니다.
  • ActivityRetainedComponent - ApplicationComponent의 하위 컴포넌트로써, Activity의 생명주기를 lifetime으로 갖습니다. 다만, Activity의 configuration change(디바이스 화면전환 등) 시에는 파괴되지 않고 유지됩니다.
  • ActivityComponent - ActivityRetainedComponen의 하위 컴포넌트로써, Activity의 생명주기를 lifetime으로 갖습니다. Activity가 생성되는(onCreate) 시점에 함께 생성되고, Activity가 파괴되는(onDestroy) 시점에 함께 파괴됩니다.
  • FragmentComponent - ActivityComponent의 하위 컴포넌트로써, Fragment의 생명주기를 lifetime으로 갖습니다. Fragment가 Activity에 붙는순간(onAttach) 시점에 함께 함께 생성되고, Fragment가 파괴되는(onDestroy) 시점에 함께 파괴됩니다.
  • ViewComponent - ActivityComponent의 하위 컴포넌트로써, View의 생명주기를 lifetime으로 갖습니다. View가 생성되는 시점에 함께 생성되고, 파괴되는 시점에 함께 파괴됩니다.
  • ViewWithFragmentComponent - FragmentComponent의 하위 컴포넌트로써, Fragment의 view 생명주기를 lifetime으로 갖습니다. View가 생성되는 시점에 함께 생성되고, 파괴되는 시점에 함께 파괴됩니다.
  • ServiceComponent - ApplicationComponent의 하위 컴포넌트로써, Service의 생명주기를 lifetime으로 갖습니다. Service가 생성되는(onCreate) 시점에 함께 생성되고, Service가 파괴되는(onDestroy) 시점에 함께 파괴됩니다.

Eum, Jaewoong (dove@hpcnt.com) 님의 포스팅 참고

위와 같은 표준 컴포넌트의 scope는 Hilt에서 제공하고 있으며, 새로운 컴포넌트는 @DefineComponent 어노테이션을 사용하여 사용자 정의가 가능하다.

Hilt Modules

  • Hilt는 @InstallIn 어노테이션을 사용하여 표준 컴포넌트에 모듈들을 설치할 수 있다
  • Hilt가 제공하는 모든 규칙은 @InstallIn 어노테이션을 사용하여 어떤 컴포넌트에 install할지 지정
@Module
@InstallIn(ApplicationComponent::class)
object class FooModule {
  // @InstallIn(ApplicationComponent.class) module providers have access to
  // the Application binding.
  @Provides
  fun provideBar(app: Application): Bar {...}
}
  • FooModule이라는 모듈을 ApplicationComponent에 install
  • ApplicationComponent에서 제공해주는 Application 클래스를 내부적으로 활용

AndroidEntryPoint

  • 객체를 주입할 대상에서 @AndroidEntryPoint 를 추가하여 member injection을 수행할 수 있다.
    • Activity
    • Fragment
    • View
    • Service
    • BroadcastReceiver
@AndroidEntryPoint
class MyActivity : MyBaseActivity() {
  // Bindings in ApplicationComponent or ActivityComponent
  @Inject lateinit var bar: Bar

  override fun onCreate(savedInstanceState: Bundle?) {
    // Injection happens in super.onCreate().
    super.onCreate()

    // Do something with bar ...
  }
}

MainActivity에 Bar 객체를 주입하는 예제 코드

 

참고

 

Dagger Hilt로 안드로이드 의존성 주입 시작하기

Dagger Hilt에 대해 알아보고 안드로이드 프로젝트에 적용하는 방법을 소개합니다.

hyperconnect.github.io

 

728x90