Hilt는 어떻게 주입하고 동작할까??

수호·2025년 11월 24일

코드를 작성하고 있는 중 Hilt는 어떻게 동작을 하는지 갑자기 엄청난 궁금증이 생겼다. 그래서 현재 코드를 가지고 어떻게 Hilt가 동작하는지 알아보자.

  1. 전체 흐름 요약

ViewModel → UseCase → Repository → ReaderFactory → ReaderStrategy(Payon or Samsung)

  1. UseCase
class CardReaderUseCase @Inject constructor(
    private val repository: CardReaderRepository
) {
    suspend operator fun invoke(type: ReaderType, tag: Tag): Flow<CardResult> {
        return repository.readCard(type, tag)
    }
}

→ Hilt는 생성자에 있는 CardReadRepository를 자동으로 주입해 준다.

→ 여기서 Repository는 실제 구현체가 아닌 Interface Type

  1. Hilt가 Repository 구현체를 찾는 방법
@Module
@InstallIn(SingletonComponent::class)
object ReaderModule {

    @Provides
    @Singleton
    fun provideCardReaderRepository(
        dataSource: CardReaderDataSource
    ): CardReaderRepository = CardReaderRepositoryImpl(dataSource)

}

CardReaderRepository 타입이 필요하면, CardReaderRepositoryImpl(dataSource)를 생성해서 제공해

  1. Repository → DataSource
class CardReaderRepositoryImpl @Inject constructor(
    private val dataSource: CardReaderDataSource
) : CardReaderRepository {
    override suspend fun readCard(type: ReaderType, tag: Tag): Flow<CardResult> {
        return dataSource.readCard(type, tag)
    }
}

→ Hilt는 RepositoryImpl 객체를 만들 때 자동으로 DataSource를 넣어준다.

  1. DataSource → ReaderFactory
class CardReaderDataSource @Inject constructor(
    private val factory: ReaderFactory
) {
    suspend fun readCard(type: ReaderType, tag: Tag): Flow<CardResult> {
        val reader = factory.getReader(type)
        return reader.readCard(tag)
    }
}

→ DataSource을 만들 때 Hilt는 ReaderFactory를 넣어준다.

  1. ReaderFactory → ReaderStrategy 구현체 선택
class ReaderFactory @Inject constructor(
    private val payonReader: ReaderStrategy,
    private val samsungReader: ReaderStrategy
) {
    fun getReader(type: ReaderType): ReaderStrategy = when (type) {
        ReaderType.PAYON -> payonReader
        ReaderType.SAMSUNG_PAY -> samsungReader
    }
}

interface ReaderStrategy {
    suspend fun readCard(tag: Tag): Flow<CardResult>
}
💡

Hilt는 어떻게 PayonReader와 SamsungReader를 구분할까?

class ReaderFactory @Inject constructor(
    @PayonReaderQualifier private val payonReader: ReaderStrategy,
    @SamsungReaderQualifier private val samsungReader: ReaderStrategy
)

→ 보통 각각 @Named 또는 @Qualifier로 구분해야 한다.

→ 현재는 Payon만 있어서 오류가 나지 않지만 SamsungPay까지 있으면 에러가 발생할 것이다.

  1. ReaderStrategy(전략 객체) 실행
class PayonReader @Inject constructor(
    private val nfcAdapter: PayonNfcAdapter,
    private val api: RetrofitPayonApi
) : ReaderStrategy {

    override suspend fun readCard(tag: Tag): Flow<CardResult> = flow {
        ...
    }
}

→ Hilt는 PayonReader를 만들 때 필요한 의존성을 넣어준다.

  1. 최종 결과 Flow 반환

ReaderStrategy → DataSource → Repository → UseCase → ViewModel → UI

🧩 전체 DI 구성도

ViewModel
    ↑
CardReaderUseCase  (Hilt inject)CardReaderRepository (Interface)CardReaderRepositoryImpl  (Hilt @Provides binds)CardReaderDataSource  (Hilt inject)ReaderFactory  (Hilt inject)
    ↑
ReaderStrategy 구현체 2(payonReader, samsungReader)
profile
처음부터 다시 시작!!

0개의 댓글