본문 바로가기
android

Hilt

by liz_devel 2022. 10. 19.

hilt는 의존성 주입 라이브러리 중에 하나이다.

의존성 주입 라이브러리에는 잘 알려진 것으로 Dagger, Hilt, koin이 있다.

Dagger, Hilt, koin를 비교해 보자면 다음과 같다.

 

  Dagger Hilt koin
난이도(러닝 커브) 하(복잡한 설정이나 어노테이션 없이 사용 가능)
에러 발견 시점 컴파일 시 컴파일 시 런타임 시
호환 자바, 코틀린 자바, 코틀린 코틀린
  개발자가 직접 컴포넌트를 생성해야 함 > 보일러 플레이트가 많아짐 표준화된 컴포넌트, 스코프 제공  

 

왜 의존성 주입 라이브러리를 사용해야 하는가?

  • 코드의 가독성과 재사용성
  • 클래스간 결합도를 느슨하게 함으로써 테스트 용이

 

Hilt 사용법

힐트 라이브러리를 buil.gradle에 설정해줍니다.

다음으로 Application에 @HiltAndroidApp 어노테이션을 적어줘야 힐트를 사용할 수 있습니다.

Dagger에서는 개발자가 직접 컴포넌트를 생성해서 Application에 종속성을 부여해주지만 이 부분을 Hit에서는 바이트 변환을 통해 @HiltAndroidApp 어노테이션을 간단하게 붙이는 것만으로 종속성을 부여해줍니다. Application에 붙여줌으로써 Application의 수명주기와 종속성을 부여받습니다.

 

위 과정을 해주었으면 힐트를 사용할 준비가 되었습니다.이어서 Hilt에서 사용되는 어노테이션에 대한 설명을 적도록 하겠습니다.

 

@AndroidEntryPoint이 어노테이션은 안드로이드 클래스인 Activity, Fragment, View, Service, BroadCastReceiver에서만 사용이 가능하며 다른 클래스는 @EntryPoint라는 어노테이션을 사용합니다. @AndroidEntryPoint를 사용할 경우 위에서 @HiltAndroidApp 설정한 Application에 대한 종속성을 부여받고 접근할 수 있습니다.

 

@Inject

@Inject는 생성자 주입과 필드 주입 방법이 있습니다. 생성자 주입 방법은 classMainActivity @Inject constructor ( val userRepository : UserRepository) < 이런 식으로 사용합니다.  필드 주입 방법은 hilt가 onCreat() 시점에 주입되기 때문에 변수명 앞에 lateinit var 를 붙여 @Inject lateinit var 변수명처럼 적어줍니다.

 

@Module

모듈이란 생성자나 메소드에 추상적인 것을 주입하려고 할 때 객체로 만들어 주입하기 위해 필요한 어노테이션입니다.

@Module 어노테이션은 항상 @InstallIn 어노테이션과 함께 써 줘야 합니다. @InstallIn(SingletoneComponent::class)처럼

괄호 안에는 해당 모듈을 설치할 컴포넌트를 적어줍니다.

 

@Provides 와 @Binds

둘의 큰 차이는 프로젝트가 소유하지 않은 외부 라이브러리의 클래스일 경우 @Provides 어노테이션을 사용하고 그 외에는 @Binds를 사용합니다.

 

사용법

1. 종속 항목 추가

build.gradle에 다음과 같이 추가 후 sync now를 해줍니다.

 

참고: Hilt와 데이터 결합을 모두 사용하는 프로젝트에는 Android 스튜디오 4.0 이상이 필요합니다.

 

2. Hilt 애플리케이션 클래스

Hilt를 사용하는 모든 앱은 @HiltAndroidApp으로 주석이 지정된 Application 클래스를 포함해야 합니다. (반드시필요!)

* 앱의 수명 주기에 연결된 컨테이너를 추가하기 위함

@HiltAndroidApp은 애플리케이션 수준 종속 항목 컨테이너 역할을 하는 애플리케이션의 기본 클래스를 비롯하여 Hilt의 코드 생성을 트리거합니다. 이 생성된 Hilt 구성 요소는 Application개체의 수명 주기에 연결되고 종속성을 제공

*트리거란?  데이터베이스에 어떠한 일이 일어나면 자동으로 실행되는 개체를 의미합니다.

 

 

3. Android 클래스에 종속 항목 삽입

Application 클래스에 Hilt를 설정하고 애플리케이션 수준 구성요소를 사용할 수 있게 되면 Hilt는 @AndroidEntryPoint 주석이 있는 다른 Android 클래스에 종속 항목을 제공할 수 있습니다.

@AndroidEntryPoint 주석을 달면 Android 클래스 수명 주기를 따르는 종속 항목 컨테이너가 생성됩니다.

Hilt는 현재 다음 Android 클래스를 지원합니다.

  • Application(@HiltAndroidApp을 사용)
  • ViewModel(@HiltViewModel을 사용)
  • Activity
  • Fragment
  • View
  • Service
  • BroadcastReceiver

Android 클래스에 @AndroidEntryPoint로 주석을 지정하면 이 클래스에 종속된 Android 클래스에도 주석을 지정해야 합니다. 예를 들어 fragment에 주석을 지정하면 이 fragment를 사용하는 activity에도 주석을 지정해야 합니다.

 

 

4. Hilt 결합 정의

다음과 같이 클래스의 생성자에서 @Inject 주석을 사용하여 클래스의 인스턴스를 제공하는 방법을 Hilt에 알려줍니다.

유형의 인스턴스 제공 방법을 Hilt에 알리려면 삽입하려는 클래스의 생성자에 @Inject 주석을 추가하세요.

 

5. Hilt 모듈

Hilt 모듈은 @Module과 @InstallIn 주석이 달린 클래스

 @Module은 Hilt에 모듈임을 알려 주고 @InstallIn은 어느 컨테이너에서 Hilt 구성요소를 지정하여 결합을 사용할 수 있는지 Hilt에 알려 줍니다. 

 

Hilt 모듈은 인터페이스를 삽입할 수 없을 때,

외부 라이브러리의 클래스를 소유하지 않아서 생성자를 삽입할 수 없을 때,

사용하여 Hilt에 결합 정보를 제공할 수 있습니다.

 

6. 인스턴스 삽입

인스턴스 삽입 방법에는 @Binds@Provides가 있습니다.

제가 생각했을 때 어떤 것을 사용할지는 '클래스를 소유하고 있냐, 없냐' 에 따라 사용한다고 생각합니다.

클래스를 가지고 있는 인터페이스의 인스턴스를 제공할 경우 @Binds를 사용합니다.

클래스가 외부 라이브러리에서 제공되기 때문에 클래스를 소유하지 않은 경우(RetrofitOkHttpClient 또는 Room 데이터베이스와 같은 클래스) 또는 빌더 패턴으로 인스턴스를 생성해야 하는 경우에는 @Provides를 사용합니다.

위 사진에서 @Provides를 적용한 이유: 클래스가 외부 라이브러리에서 제공되므로 클래스를 소유하지 않은 경우라서

 

7. 인스턴스 범위를 컨테이너로 지정

주석을 사용하여 인스턴스의 범위를 컨테이너로 지정할 수 있습니다. Hilt는 수명 주기가 다른 여러 컨테이너를 생성할 수 있으므로 이러한 컨테이너로 범위가 지정된 다양한 주석이 있습니다. 인스턴스 범위를 애플리케이션 컨테이너로 지정하는 주석은 @Singleton입니다. 이 주석을 사용하면 유형이 다른 유형의 종속 항목으로 사용되는지 또는 삽입된 필드여야 하는지와 관계없이 애플리케이션 컨테이너에서 항상 같은 인스턴스를 제공합니다.

* Hilt는 @Inject를 요청할 때마다 새 인스턴스를 생성합니다. 동일한 인스턴스를 원할 경우는 Component 별로 정의된 Scope을 지정해 줘야 합니다.

 

 

===================== 위 내용 까지가 실제 개인 프로젝트에 적용한 어노테이션입니다 ======================

 ======================== 하단 내용부터는 공식 문서 예제 그대로 추가하였습니다. =======================

 

동일한 유형에 대해 여러 결합 제공

종속 항목과 동일한 유형의 다양한 구현을 제공하는 Hilt가 필요한 경우에는 Hilt에 여러 결합을 제공해야 합니다. 한정자를 사용하여 동일한 유형에 대해 여러 결합을 정의할 수 있습니다.

한정자는 특정 유형에 대해 여러 결합이 정의되어 있을 때 그 유형의 특정 결합을 식별하는 데 사용하는 주석입니다.

다음 예를 생각해 보세요. AnalyticsService 호출을 가로채야 한다면 인터셉터와 함께 OkHttpClient 객체를 사용할 수 있습니다. 다른 서비스에서는 호출을 다른 방식으로 가로채야 할 수도 있습니다. 이 경우에는 서로 다른 두 가지 OkHttpClient 구현을 제공하는 방법을 Hilt에 알려야 합니다.

먼저 다음과 같이 @Binds 또는 @Provides 메서드에 주석을 지정하는 데 사용할 한정자를 정의합니다.

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class AuthInterceptorOkHttpClient

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class OtherInterceptorOkHttpClient

 

그런 다음, Hilt는 각 한정자와 일치하는 유형의 인스턴스를 제공하는 방법을 알아야 합니다. 이 경우에는 @Provides와 함께 Hilt 모듈을 사용할 수 있습니다. 두 메서드 모두 동일한 반환 유형을 갖지만 한정자는 다음과 같이 두 가지의 서로 다른 결합으로 메서드에 라벨을 지정합니다.

@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {

  @AuthInterceptorOkHttpClient
  @Provides
  fun provideAuthInterceptorOkHttpClient(
    authInterceptor: AuthInterceptor
  ): OkHttpClient {
      return OkHttpClient.Builder()
               .addInterceptor(authInterceptor)
               .build()
  }

  @OtherInterceptorOkHttpClient
  @Provides
  fun provideOtherInterceptorOkHttpClient(
    otherInterceptor: OtherInterceptor
  ): OkHttpClient {
      return OkHttpClient.Builder()
               .addInterceptor(otherInterceptor)
               .build()
  }
}

다음과 같이 필드 또는 매개변수에 해당 한정자로 주석을 지정하여 필요한 특정 유형을 삽입할 수 있습니다.

 

// As a dependency of another class.
@Module
@InstallIn(ActivityComponent::class)
object AnalyticsModule {

  @Provides
  fun provideAnalyticsService(
    @AuthInterceptorOkHttpClient okHttpClient: OkHttpClient
  ): AnalyticsService {
      return Retrofit.Builder()
               .baseUrl("https://example.com")
               .client(okHttpClient)
               .build()
               .create(AnalyticsService::class.java)
  }
}

// As a dependency of a constructor-injected class.
class ExampleServiceImpl @Inject constructor(
  @AuthInterceptorOkHttpClient private val okHttpClient: OkHttpClient
) : ...

// At field injection.
@AndroidEntryPoint
class ExampleActivity: AppCompatActivity() {

  @AuthInterceptorOkHttpClient
  @Inject lateinit var okHttpClient: OkHttpClient
}

 

한정자를 유형에 추가한다면 그 종속 항목을 제공하는 가능한 모든 방법에 한정자를 추가하는 것이 좋습니다. 기본 또는 일반 구현을 한정자 없이 그대로 두면 오류가 발생하기 쉬우며 Hilt가 잘못된 종속 항목을 삽입할 수 있습니다.

Hilt의 사전 정의된 한정자

Hilt는 몇 가지 사전 정의된 한정자를 제공합니다. 예를 들어 애플리케이션 또는 활동의 Context 클래스가 필요할 수 있으므로 Hilt는 @ApplicationContext 및 @ActivityContext 한정자를 제공합니다.

예의 AnalyticsAdapter 클래스에 활동 컨텍스트가 필요하다고 가정해 보겠습니다. 다음 코드는 AnalyticsAdapter에 활동 컨텍스트를 제공하는 방법을 보여줍니다.

 

class AnalyticsAdapter @Inject constructor(
    @ActivityContext private val context: Context,
    private val service: AnalyticsService
) { ... }

 

Android 클래스용으로 생성된 구성요소

필드 삽입을 실행할 수 있는 각 Android 클래스마다 @InstallIn 주석에 참조할 수 있는 관련 Hilt 구성요소가 있습니다. 각 Hilt 구성요소는 해당 Android 클래스에 결합을 삽입해야 합니다.

이전 예에서는 Hilt 모듈에서 ActivityComponent를 사용하는 방법을 보여주었습니다.

Hilt는 다음 구성요소를 제공합니다.

 

Hilt 구성요소인젝터 대상

Hilt 구성요소 인젝터 대상
SingletonComponent Application
ActivityRetainedComponent N/A
ViewModelComponent ViewModel
ActivityComponent Activity
FragmentComponent Fragment
ViewComponent View
ViewWithFragmentComponent @WithFragmentBindings 주석이 지정된 View
ServiceComponent Service

 

구성요소 전체 기간

Hilt는 해당 Android 클래스의 수명 주기에 따라 생성된 구성요소 클래스의 인스턴스를 자동으로 만들고 제거합니다.

생성된 구성요소 생성 위치 소멸 위치
SingletonComponent Application#onCreate() Application 소멸됨
ActivityRetainedComponent Activity#onCreate() Activity#onDestroy()
ViewModelComponent ViewModel 생성됨 ViewModel 소멸됨
ActivityComponent Activity#onCreate() Activity#onDestroy()
FragmentComponent Fragment#onAttach() Fragment#onDestroy()
ViewComponent View#super() View 소멸됨
ViewWithFragmentComponent View#super() View 소멸됨
ServiceComponent Service#onCreate() Service#onDestroy()

 

구성요소 범위

Hilt는 구성요소로 범위 지정도 가능합니다.

Android 클래스 생성된 구성요소 범위
Application SingletonComponent @Singleton
Activity ActivityRetainedComponent @ActivityRetainedScoped
ViewModel ViewModelComponent @ViewModelScoped
Activity ActivityComponent @ActivityScoped
Fragment FragmentComponent @FragmentScoped
View ViewComponent @ViewScoped
@WithFragmentBindings 주석이 지정된 View ViewWithFragmentComponent @ViewScoped
Service ServiceComponent @ServiceScoped

 

 

참고: 공식 문서-  https://developer.android.com/training/dependency-injection/hilt-android?hl=ko#kotlin

https://developer.android.com/codelabs/android-hilt?hl=ko#0

반응형

'android' 카테고리의 다른 글

[Kotlin] Geckoview tutorial  (0) 2022.12.19
디자인 패턴 MVC, MVP, MVVM  (0) 2022.11.10
startActivityForResult() deprecated  (0) 2022.10.06
NestedScrollView 중첩 스크롤뷰  (0) 2022.09.23
클린 아키텍처란? SOLID 원칙  (1) 2022.08.16