Skip to content

Latest commit

 

History

History
172 lines (115 loc) · 5.45 KB

File metadata and controls

172 lines (115 loc) · 5.45 KB

DI란?

DI(Dependency Injection)는 객체가 자신의 의존성을 직접 생성하지 않고 외부에서 주입받도록 하여, 객체 생성 책임을 분리하고 제어의 역전(IoC)을 달성하는 방식이다.

이를 통해

  • 객체 간 결합도를 낮추고
  • 테스트 용이성을 높이며
  • 구현 교체가 쉬운 구조를 만들 수 있다.

Hilt를 왜 쓰는가?

Hilt는 Dagger 기반의 Android DI 라이브러리로, 복잡한 컴포넌트 설정을 자동화하고 Android 생명주기에 맞는 DI 구조를 제공한다.

장점:

  • Dagger의 보일러플레이트 제거
  • Android 생명주기(Activity, Fragment 등)에 맞는 표준 컴포넌트 제공
  • 컴파일 타임 검증 + 런타임 성능 확보

즉, 개발자가 DI 그래프를 직접 구성하지 않아도 안정적으로 의존성 관리가 가능하다.


Hilt 핵심 개념

Component (의존성 컨테이너)

Hilt는 Android 생명주기에 맞는 DI 컨테이너를 제공한다.

  • SingletonComponent → 앱 전체 생명주기
  • ActivityRetainedComponent → 구성 변경에도 유지
  • ActivityComponent / FragmentComponent → 화면 단위
  • ViewModelComponent → ViewModel 범위

Scope

Scope는 동일한 컴포넌트 내에서 같은 인스턴스를 재사용하도록 보장한다.

  • @Singleton, @ActivityScoped, @ViewModelScoped 등등

Hilt Annotation

@HiltAndroidApp

@HiltAndroidApp
class MyApp : Application()

Application 클래스에 붙인다. 이 어노테이션을 기준으로 Hilt의 애플리케이션 레벨 컴포넌트가 생성되고, 앱 전체 DI 그래프의 시작점이 된다.

애플리케이션 레벨 컴포넌트:

  • 앱이 살아있는 동안 유지되는 DI 컨테이너. HiltAndroidApp 어노테이션이 붙으면 Hilt가 내부적으로 DaggerMyApp_HiltComponents_SingletonC같은 클래스 생성

@AndroidEntryPoint

Activity, Fragment, View, Service 등 Android 프레임워크 클래스에 붙여서 해당 클래스가 Hilt로부터 의존성을 주입받을 수 있게 한다. Hilt는 Android 클래스별 컨테이너( Component)를 제공하므로, 주입 대상 Android 컴포넌트에는 entry point가 필요하다.

@AndroidEntryPoint
class MainActivity : AppCompatActivity()

@Inject

생성자에 붙이면 생성자 주입이고, 필드에 붙이면 필드 주입이다. Android 공식 가이드에서는 가능하면 생성자 주입을 우선하라고 권장되어 있다. 테스트가 쉽고, 객체 생성 시점에 필요한 의존성이 보장되기 때문.

class UserRepository @Inject constructor(
    private val api: UserApi,
)

@Module

“이 클래스 안에는 DI 설정이 들어있다”는 선언. "이 클래스는 DI 그래프 구성에 사용된다”라는 마커.

자체로는 아무 객체도 만들지 않는다.

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

@InstallIn

이 모듈이 어느 Hilt 컴포넌트에 설치될지 지정한다. 즉, 어느 생명주기 범위에서 관리할지 결정하는 어노테이션이다. Hilt는 Android 클래스 생명주기에 맞는 컴포넌트를 제공하고, 모듈은 그 중 어디에 속할지 명시해야 한다.

  • SingletonComponent : 앱 전체 생명주기와 함께 감
  • ActivityRetainedComponent: 구성 변경에도 유지되는 Activity 관련 범위
  • ActivityComponent / FragmentComponent: 해당 Android 컴포넌트 생명주기 범위
  • ViewModelComponent: ViewModel 생명주기 범위

@Provides

객체 생성 로직을 직접 작성해야 할 때 사용한다. 주로 외부 라이브러리 클래스처럼 생성자를 수정할 수 없거나 Builder 패턴처럼 생성 과정에 추가 로직이 필요한 경우에 적합하다.

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

    @Provides
    fun provideRetrofit(): Retrofit {
        return Retrofit.Builder()
            .baseUrl(BuildConfig.BASE_URL)
            .build()
    }
}

@Binds

인터페이스와 구현체를 연결할 때 사용한다. 구현체 생성 자체는 @Inject constructor()로 가능하고, “이 인터페이스 요청 시 이 구현체를 써라”만 선언하면 되는 경우 적합하다. 공식 문서도 인터페이스 바인딩에는 @Binds 사용을 권장한다.

interface LoginRepository

class LoginRepositoryImpl @Inject constructor(
    private val api: LoginApi,
) : LoginRepository

@Module
@InstallIn(SingletonComponent::class)
abstract class RepositoryModule {

    @Binds
    abstract fun bindLoginRepository(
        impl: LoginRepositoryImpl,
    ): LoginRepository
}

@HiltViewModel

Hilt로 ViewModel 생성자를 주입받게 할 때 사용한다. @HiltViewModel이 붙은 ViewModel은 constructor injection을 통해 의존성을 받을 수 있고, SavedStateHandle도 함께 주입 가능하다.

@HiltViewModel
class LoginViewModel @Inject constructor(
    private val repository: LoginRepository,
    private val savedStateHandle: SavedStateHandle,
) : ViewModel()

정리

Hilt는 Android 생명주기 기반 DI 프레임워크이다.

  1. Component: 의존성을 어디서 관리할지 정하는 DI 컨테이너
  2. Scope: 그 객체를 얼마나 재사용할지 정하는 규칙
  3. Injection 방식: 의존성 주입을 어떻게 할지 정하는 방식

참고 자료

https://developer.android.com/training/dependency-injection/hilt-android?hl=ko

https://github.com/google/dagger