Hilt 라이브러리를 이용해 의존성을 주입하는 경우에 Module에서 abstract class에는 @Binds 어노테이션이, object에는 @Provides 키워드가 붙게 되는데 어떤 이유에서인지 알아보겠습니다.
@Binds? @Provides?
결론부터 말하자면 @Binds와 @Provides의 가장 큰 차이는 어떤 인스턴스를 제공하느냐 입니다.
@Binds 어노테이션은 인터페이스 인스턴스, @Provides 어노테이션은 클래스 인스턴스를 제공합니다.
@Binds
@Binds는 Hilt에게 인터페이스 인스턴스를 제공하기 때문에 주로 Interface로 정의된 레포지토리(Repository)를 인스턴스로 제공합니다.
좀 더 정확히 말하면 인터페이스는 생성자를 정의할 수 없기 때문에 Repository를 상속받는 구현체(RepositoryImpl)를 Hilt에게 제공합니다. (나는 분명 Hilt에게 Repository만 알려줬는데 얘가 어떻게 RepositoryImpl에서 override한 메소드를 쓸 수 있지.. 똑똑하네..?) 라고 생각했는데 Module에서 정의되고 있었기 때문입니다.
@Binds의 반환 타입은 메소드가 제공할 인스턴스 타입, 메소드의 매개변수는 해당 메소드가 제공할 구현체입니다.
아래의 샘플에서 MyRepository는 인터페이스이기 때문에 생성자를 주입할 수 없습니다.
따라서 @Binds 어노테이션을 이용해 abstract 메소드를 정의하고 메소드의 매개변수에 해당 인터페이스의 구현체(MyRepositoryImpl)를 주입합니다. 그리고 반환 타입은 인터페이스 인스턴스를 반환하도록 정의합니다.
@Module
@InstallIn(SingletonComponent::class)
abstract class MyHiltModule {
@Binds
@Singleton
abstract fun bindMyRepository(impl: MyRepositoryImpl): MyRepository
@Binds
@Singleton
abstract fun ...
}
이제 Hilt는 MyHiltModules을 통해 MyRepository의 구현체인 MyRepositoryImpl에 접근할 수 있습니다.
인터페이스와 인터페이스의 구현체가 여러 개인 경우에도 Module에 얼마든지 @Binds 어노테이션을 추가할 수 있습니다.
@Provides
@Provides는 Hilt에 의존성을 주입하려는 인스턴스 클래스가 외부 라이브러리(Retrofit, OkHttp, Room, DataStore 등)를 사용하는 경우 혹은 빌더 패턴으로 인스턴스를 생성하는 경우에 사용합니다.
@Provides의 반환 타입은 메소드가 제공할 인스턴스 타입, 매개변수는 해당 인스턴스의 의존 항목입니다.
대표적으로 Retrofit은 내부적으로 OkHttp에 의존하기 때문에 Retrofit 클래스를 생성하는 메소드의 매개변수로 OkHttp를 넘겨줍니다.
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
private const val BASE_URL = "MY_BASE_URL"
@Provides
@Singleton
fun provideOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder()
.addNetworkInterceptor(HttpLoggingInterceptor())
.build()
}
@Provides
@Singleton
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
return Retrofit.Builder()
.client(okHttpClient)
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
@Provides
@Singleton
fun provideApiService(retrofit: Retrofit): RetrofitService {
return retrofit.create(RetrofitService::class.java)
}
}
Retrofit과 OkHttpClient 클래스 모두 외부 라이브러리이기 때문에 @Provides 어노테이션을 이용해 정의합니다.
이제 Hilt가 외부 라이브러리까지 의존성을 주입받아 사용할 준비가 되었습니다.
Repository를 상속받는 구현체(RepositoryImpl)의 생성자에 @Provides 메소드로 정의했던 RetrofitService를 주입합니다.
class MyRepositoryImpl @Inject constructor(
private val retrofitService: RetrofitService
) : MyRepository {
override fun testMethod() {
retrofitService.test()
}
...
}
ViewModel에 @HiltViewModel을 명시하고, ViewModel의 생성자에 Repository를 주입해 정의한 구현체를 통해 Model에 접근합니다.
다음 포스팅에서 ViewModel에서 Hilt를 다루는 방법을 더 자세히 공부해 보겠습니다.
'Android' 카테고리의 다른 글
[안드로이드] 이미지(ImageView) 테두리의 모서리 둥글게 만들기 (0) | 2023.07.21 |
---|---|
[안드로이드] Databinding으로 View의 visibility 설정하기 (0) | 2023.07.18 |
[안드로이드] 벡터파일(.svg)을 ImageView에 로드하는 방법 feat. Coil (0) | 2023.07.12 |
[안드로이드] Lottie 애니메이션 사용하기 (0) | 2023.06.08 |
[안드로이드] Hilt 의존성 주입 - "error: [Dagger/MissingBinding] android.content.Context cannot be provided without an @Provides-annotated method." 에러 해결방법 (0) | 2023.05.14 |