https://dagger.dev/hilt/components


5.1 Core APIs – Component

Note : 다음 나올 내용은 컴포넌트, 모듈, 스코프 그리고 바인딩을 포함하는 Dagger에 대한 기초적인 내용을 알고 있음을 가정한다.

컴포넌트 계층

기존 사용하던 Dagger와 다르게 Hilt사용자는 Dagger 컴포넌트를 직접적으로 정의하거나 인스턴스화 할 필요가 없어졌다. 대신에 Hilt는 이미 정의된 컴포넌트를 통해 생성되는 클래스들을 제공하고 있다. Hilt는 안드로이드 Application의 다양한 생명주기에 자동으로 통합되는 내장 컴포넌트 세트를 해당 스코프 어노테이션과 함께 제공한다. 아래에 있는 다이어그램은 표준 Hilt 컴포넌트 계층을 보여주고 있다. 각 컴포넌트에 위에 달린 어노테이션은 컴포넌트 바인딩의 생명주기를 지정하는 데 사용된다. 각 컴포넌트 아래에 있는 화살표는 하위 컴포넌트를 가르키고 있다. 보통 하위 컴포넌트의 바인딩은 상위 컴포넌트의 바인딩이 가지고 있는 의존성들을 가질 수 있다.

Note : @InstallIn이 달린 모듈의 바인딩에 스코프가 지정될 때는 반드시 모듈이 설치되는 컴포넌트의 스코프와 일치해야 한다. 예를 들면, @InstallIn(ActivityComponent.class) 모듈은 @ActivityScoped만 사용할 수 있다.

Hilt Component Hierarchy

컴포넌트 멤버 주입

앞에서 다룬 @AndroidEntryPoint 섹션에서는 안드로이드 클래스에 멤버 주입을 하는 방법에 대해서 다뤘다. Hilt 컴포넌트들은 각각 안드로이드 클래스에 맞는 의존성 주입을 해야 할 의무가 있다. 다음에 나올 표가 안드로이드 클래스에 적합한 Hilt 컴포넌트를 보여준다.

Component Injector for
ApplicationComponent Application
ActivityRetainedComponent ViewModel 
ActivityComponent Activity
FragmentComponent Fragment
ViewComponent View
ViewWithFragmentComponent View with @WithFragmentBindings
ServiceComponent Service

컴포넌트의 생애

컴포넌트의 수명은 다음 두가지 관점에서 볼 때 바인딩의 수명과 관련되기 때문에 중요하다.

  1. 컴포넌트가 생성되고 종료될 때, 해당 스코프 어노테이션이 지정된 바인딩 또한 수명을 함께한다.
  2. 컴포넌트 수명은 멤버 주입된 값들이 사용될 수 있는 시기를 나타낸다. (@Inject 필드를 사용할 때 null이면 안된다.)

컴포넌트의 수명은 일반적으로 안드로이드 클래스에 대응하는 인스턴스 생성과 소멸을 따라간다. 다음 표는 스코프 어노테이션과 각 컴포넌트에 맞는 수명을 목록으로 보여주고 있다.

Component Scope Created at Destroyed at
ApplicationComponent @Singleton Application#onCreate() Application#onDestroy()
ActivityRetainedComponent @ActivityRetainedScope Activity#onCreate() Activity#onDestroy()
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()

스코프 바인딩 vs 비 스코프 바인딩

기본적으로 모든 Dagger의 바인딩은 스코프 어노테이션이 없는 비 스코프 바인딩이다. 이는 각 바인딩이 요청될 때마다 Dagger는 새로운 인스턴스를 생성하는 것을 의미 한다.

그러나 Dagger는 컴포넌트에 스코프 어노테이션을 지정할 수 있다.(바로 앞에 나온 표의 스코프 어노테이션을 살펴보자). 스코프가 지정된 컴포넌트에서 해당 스코프 바인딩은 컴포넌트 인스턴스당 한번만 생성되고, 해당 바인딩에 대한 모든 요청에 동일한 인스턴스를 제공한다.

예를 들면 다음 코드와 같다.

// 스코프가 지정되지 않은 이 바인딩은
// 각 바인딩의 요청에 대해 새로운 인스턴스를 제공하게 된다.
class UnscopedBinding @Inject constructor() {...}

// 스코프가 지정된 이 바인딩은
// 이 바인딩에 대한 동일한 컴포넌트 인스턴스는 각기 다른 요청에 대해 동일한 인스턴스를 제공한다.
// @FragmentScoped로 지정되었기 때문에 동일한 프레그먼트의 요청에 대해 동일한 인스턴스를 제공하게 된다.
@FragmentScoped
class ScopedBinding @Inject constructor() {...}
Warning : 일반적으로 오해하는 부분이 @FragmentScoped가 지정된 바인딩이 모든 프레그먼트 인스턴스에 대해 동일한 바인딩 인스턴스를 공유할 것이라고 기대하는 점이다. 하지만 실제로는 그렇지는 않고 각 프레그먼트 인스턴스는 새로운 프레그먼트 컴포넌트 인스턴스를 얻기 때문에 각기 다른 프레그먼트 인스턴스는 각자만의 스코프 된 바인딩을 얻게 된다.

모듈에서 스코프 어노테이션 사용하기

이전 섹션에서 생성자 @Inject의 바인딩에 대해 어떻게 스코프 어노테이션을 사용하는지 살펴보았다. 하지만 이 방법 말고도 모듈에서 바인딩에 대해 비슷한 방법으로 스코프 어노테이션을 사용할 수 있다.

@Module
@InstallIn(FragmentComponent.class)
object FooModule {
    // 이 바인딩은 스코프 어노테이션이 사용되지 않았다.
    @Provides
    fun provideUnscopedBinding() = UnscopedBinding()

    // 이 바인딩은 스코프 어노테이션이 사용되었다.
    @Provides
    @FragmentScoped
    fun provideScopedBinding() = ScopedBinding()
}
Warning : 일반적으로 오해하는 부분이 모듈내에 선언된 모든 바인딩이 모듈이 설치되는 컴포넌트와 수명을 함께 한다고 생각하는 것이다. 하지만 그렇지 않고, 단지 스코프 어노테이션이 지정된 바인딩 선언만 해당 컴포넌트와 수명을 함께하여 각 바인딩 요청들에 대해 동일한 인스턴스를 제공한다.

스코프 어노테이션은 언제 사용할까?

바인딩에 대해 스코프 어노테이션을 지정하는 것은 코드 생성 크기 그리고 런타임 성능에 영향을 미치므로 가능한 스코프 어노테이션을 조금만 사용하는 것이 좋다. 바인딩에 대해 스코프를 어노테이션을 사용해야 하는지 결정하는 일반적인 규칙은 동일한 인스턴스를 보장해야 할 만큼 코드의 정확성이 필요한 경우다. 성능상의 이유로만 스코프 어노테이션을 사용해야 한다면, 먼저 성능이 문제인지 확인한 뒤 표준 Hilt 컴포넌트 스코프 어노테이션 대신 @Reusable을 사용하는 것이 좋다.

컴포넌트가 제공하는 기본 바인딩

각 Hilt 컴포넌트는 기본 바인딩 세트와 함께 사용자가 정의한 바인딩들을 의존성 주입하게 된다.

Component Default Bindings
ApplicationComponent Application
ActivityRetainedComponent Application
ActivityComponent Application, Activity
FragmentComponent Application, Activity, Fragment
ViewComponent Application, Activity, View
ViewWithFragmentComponent Application, Activity, Fragment, View
ServiceComponent Application, Service

  1. 구성 변경에 의해 Activity는 재생성 될 수 있으므로 ActivityRetainedComponent 에서는 Activity의 인스턴스는 기본 바인딩으로 제공하지 않고 있다.
  2. Application 바인딩은 @ApplicationContext Context 또는 Application 으로 요청하면 제공 받을 수 있다.
카테고리: Dagger2

0개의 댓글

답글 남기기

Avatar placeholder

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.