[Android] Hilt @Binds와 @Provides의 차이점

minH_·2024년 5월 16일

Hilt를 사용하여 의존성 주입을 할 때 Module을 정의하여 특정 유형의 인스턴스를 제공하는 방법을 Hilt에게 알려주어야 하는 경우가 있다.

  • 외부 라이브러리의 클래스, 빌더 패턴
  • 인터페이스 형식

이 때 Module에서 사용하는 @Binds, @Provides 어노테이션에 대하여 설명하고자 한다.

@Binds

인터페이스 형식은 왜 Hilt가 바로 제공할 수 없을까?
바로 구현체가 무엇인지 알 수 없기 때문이다. @Binds 어노테이션은 인터페이스의 인스턴스를 제공해야 할 때 사용할 구현을 Hilt에 알려준다.
또한 다음과 같은 특징이 존재한다.

  • @Binds는 추상(abstract) 클래스의 추상(abstract) 메서드에 붙이는 것만 유효하다.
  • @Binds가 붙은 메서드는 반드시 하나의 매개 변수만을 가져야 한다.
  • 추상 클래스에서 구현되어야 하므로, 추상 클래스에 Provider을 넣으려면 static 한 companion object에서만 넣을 수 있다.
  • 인자로 받는 값을 Provide로 제공받지 않아도 생성자 주입을 통해 생성 된다. (인터페이스 구현체에 @Inject constructor() 로 구현채 알려줘야함)
  • 리턴 타입이 인터페이스나 추상 클래스이고, 인자가 이를 구현한 구현체여야 한다. 같은 타입으로 반환되면 주입이 무한 반복되는 cycle이 생기기 때문이다. (리턴 값이 다시 인자로 주입)
@Module
@InstallIn(ActivityComponent::class)
abstract class AnalyticsModule {

  @Binds
  abstract fun bindAnalyticsService(
    analyticsServiceImpl: AnalyticsServiceImpl
  ): AnalyticsService
}

위처럼 AnlayticsService 인터페이스 형식을 반환한다고 알려주고, 파라미터에 제공할 인터페이스의 구현체를 넣어주면 Hilt가 AnalyticsService 형식을 주입해야 할 경우 해당 구현체를 제공한다.

@Provides

인터페이스 뿐 아니라 클래스가 외부 라이브러리에서 제공되므로 클래스를 소유하지 않은 경우(Retrofit, OkHttpClient 또는 Room 데이터베이스와 같은 클래스) 또는 빌더 패턴으로 인스턴스를 생성해야 하는 경우에도 생성자 삽입이 불가능하다. 이 경우는 @Provides 어노테이션을 이용하여 Module에 어떤 방식으로 인스턴스를 제공하는지 정의하여 Hilt에 알려준다.

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

코드에서 차이점이 느껴지는가? @Binds는 구현체를 정의하여 알려주기만 하면 되고, @Provides는 인스턴스가 어떤 방식으로 생성되는지 정의하여 알려주어야 한다.

그럼 @Binds와 @Provides는 아예 다른 역할을 수행하나요?

결론은 아니다.
@Binds는 @Provides의 특수한 형태일 뿐이며, @Provides와 같은 역할을 한다. 다른 점은 Binds에는 여러 제약이 있고, 제약이 많은 만큼 코드를 덜 생성한다는 점이다. 빌드 후 app/build 폴더에 가보면 @Binds 와 @Provides 의 클래스 생성양 차이가 다르다. 따라서 상황에 맞게 생성 하는것을 추천한다.


틀린 부분이 있을 수 있습니다. 댓글 언제나 환영입니다.

참고 포스트:

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

0개의 댓글