Android Hilt

Hilt는 Android 앱 개발에서 의존성 주입(Dependency Injection, DI)을 쉽고 효율적으로 구현하기 위한 Jetpack 라이브러리입니다.

Dagger 기반으로 만들어졌으며, Android 환경에 최적화되어 있습니다.

의존성 주입 (DI) 이란?

  • 객체 간의 의존 관계를 직접 생성하지 않고 외부에서 주입하는 방식입니다.
  • 클래스 간의 결합도를 낮추고 유연성, 재사용성, 테스트 용이성을 높이는 데 도움을 줍니다.

Hilt의 장점

  1. Android 환경에 최적화:
    • Activity, Fragment, View, ViewModel 등 Android 컴포넌트의 생명주기를 고려하여 의존성을 관리합니다.
    • 각 컴포넌트에 필요한 의존성을 자동으로 주입하여 보일러플레이트 코드를 줄여줍니다.
  2. Dagger 기반:
    • Dagger의 강력한 기능을 활용하면서도 더욱 간편하게 사용할 수 있습니다.
    • Hilt는 Dagger의 복잡한 설정을 대신 처리해주므로, 개발자는 핵심 로직에 집중할 수 있습니다.
  3. 표준화된 방법:
    • Hilt는 Android 앱에서 DI를 구현하는 표준적인 방법을 제공합니다.
    • 따라서 다른 개발자와의 협업이 용이하고, 코드의 일관성을 유지할 수 있습니다.

Hilt 주요 구성 요소

  • @HiltAndroidApp: 애플리케이션 클래스에 사용하여 Hilt를 활성화합니다.

  • @AndroidEntryPoint: Activity, Fragment, View, Service, BroadcastReceiver 등 Android 컴포넌트에 사용하여 Hilt가 의존성을 주입하도록 합니다.

  • @Inject: 의존성을 주입받을 필드 또는 생성자에 사용합니다.

  • @Module: 의존성을 제공하는 클래스에 사용합니다.

  • @Provides: @Module 클래스 내에서 의존성 객체를 생성하는 메서드에 사용합니다.

  • @Binds: @Module 클래스 내에서 인터페이스 구현체바인딩하는 메서드에 사용합니다.

  • @Singleton, @ActivityScoped, @FragmentScoped, @ViewScoped 등: 의존성 객체생명주기를 지정하는 어노테이션입니다.

Hilt 사용 방법

  1. 의존성 추가: build.gradle 파일에 Hilt 의존성을 추가합니다.
  2. 애플리케이션 클래스에 @HiltAndroidApp 추가: 애플리케이션 클래스에 @HiltAndroidApp 어노테이션을 추가하여 Hilt를 활성화합니다.
  3. 컴포넌트에 @AndroidEntryPoint 추가: 의존성을 주입받을 Activity, Fragment 등의 컴포넌트에 @AndroidEntryPoint 어노테이션을 추가합니다.
  4. @Inject 어노테이션 사용: 의존성을 주입받을 필드 또는 생성자@Inject 어노테이션을 추가합니다.
  5. @Module 및 @Provides/@Binds 어노테이션 사용: 의존성을 제공하는 클래스에 @Module 어노테이션을 추가하고, @Provides 또는 @Binds 어노테이션을 사용하여 의존성 객체를 생성하거나 바인딩합니다.

의존성 주입 컴포넌트

  • Container(Component)외부에서 클래스의 인스턴스를 생성하는 공간

  • Module 는 클래스의 인스턴스들을 모아놓은 공간으로 Container에 의존성을 제공

    • Container 안에 Module이 존재

    • 기존 IOC에는 Module이라는 개념이 없지만, Dagger에서는 특정 Container(Component)가 제공해야 하는 의존성이 Module 단위로 관리되어야 한다는 것이 편하다는 것을 고려하여 Module을 통해 클래스의 인스턴스들을 모듈 단위로 관리한다.

  • Provider(Provides)

    • Provider은 인스턴스를 제공(Provide) 하는 역할을 한다. 즉, 주입부의 인스턴스를 제공하는 것이 바로 Provider이다.

image-20240519132425310.png

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
@Component(modules = [StringModule::class])
interface StringComponent{
    fun injectString(student: Student)
}

@Module
class StringModule {
    @Provides
    fun providesString() = "StringA"
}

class Student(){
    @Inject lateinit var name: String
}
  • Dagger에서는 컴파일 타임에 Component Interface의 Annotation을 읽어 java(generated) 폴더에 다음과 같은 클래스들을 생성해준다.

  • 즉, 우리가 구현하는 Component는 Interface이며, Dagger에서 해당 인터페이스(interface StringComponent)를 구현하는 클래스를 자동 생성(class DaggerStringComponen)해주며

    • 해당 클래스를 통해 Provider에 의해 제공되는 변수를 Inject 할 수 있다.

image-20240519132858914.png

Hilt에서 @Provides와 @Binds의 차이

@Inject Annotation을 붙이는 방법말고,

Module을 이용해서 Hilt에게 원하는 Dependency를 생성하는 방법을 알려줄 수 있습니다.

특히, interface외부 라이브러리의 객체처럼, Hilt가 어떻게 객체를 생성해야할지 모르는 경우에는 꼭 필요한 방법입니다.

  • Provides Annnotation과 Binds Annotation을 이용하는 것 입니다
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
@HiltAndroidApp
class MyApplication : Application() { ... }

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    @Inject lateinit var analyticsAdapter: AnalyticsAdapter
    // ...
}

@Module
@InstallIn(SingletonComponent::class)
object AnalyticsModule {
    
    @Provides
    @Singleton
    fun provideAnalyticsAdapter(
        // ...
    ): AnalyticsAdapter {  // ...
    }
}

@Provides

  • 주입할 객체를 직접 생성하는 메서드에 사용됩니다.

  • 객체 생성 로직을 메서드 안에 구현하여 유연하게 의존성을 제공할 수 있습니다.

  • 생성자 주입이 어렵거나 불가능한 경우(예: 외부 라이브러리 클래스, 복잡한 생성 로직), 팩토리 메서드를 통해 객체를 생성해야 하는 경우에 유용합니다.

@Binds

  • 인터페이스의 구현체Hilt에게 알려주는 메서드에 사용됩니다.
  • 추상 메서드로 선언되어야 하며, 반환 타입은 인터페이스, 메서드 매개변수는 해당 인터페이스를 구현하는 클래스여야 합니다.
  • 런타임 성능에 최적화되어 있으며, @Provides보다 더 간결하게 코드를 작성할 수 있습니다.
특징@Provides@Binds
객체 생성 방식메서드 내부에서 직접 생성인터페이스 구현체를 Hilt에 알려줌
사용 시점생성자 주입이 어렵거나 복잡한 객체 생성 시인터페이스와 구현체 관계가 명확할 때
성능@Binds에 비해 약간 느림@Provides에 비해 빠름
코드 간결성@Binds에 비해 코드가 길어질 수 있음@Provides에 비해 간결함