최근 기존 프로젝트 코드리뷰를 받고 수정해야 할 점중 하나인 Hilt에 대해 이야기 해보려고한다.
기존에 Hilt 를 사용할때 모듈은 무조건 Provides 로만 사용하였는데 이러한 부분이 잘못되었다고 리뷰를 받아 그 차이가 뭐고 왜 Binds 와 Provides를 각각 알맞게 사용해야 하는지에 대해 알아보았다.
우선 힐트에서는 객체를 주입해주기 위해서는 객체를 제공하는 방법을 알려주어야 하는데 해당 방법으로는 Provides 와 Bind 가 있다.
Bind와 Provides 의 차이가 뭘까
class ProductRepositoryImpl @Inject constructor(
private val supabaseClient: SupabaseClient,
) : ProductRepository { ... }
@InstallIn(SingletonComponent::class)
@Module
abstract class RepositoryModule {
@Binds
abstract fun bindProductRepository(impl: ProductRepositoryImpl): ProductRepository
}
위 코드에서는 @Binds 를 사용하는 방법에 대해서 설명한다.
인터페이스를 구현한 객체에 constructor-inject 를 해주고 Binds 모듈을 만들어 인터페이스와 구현체간의 관계를 설정한다.
이는 ProductRepository 를 필요로 하는곳에 주입을 할때 ProductRepositoryImpl 를 주입해 주어라 라는 의미로 볼 수 있다.
그런데 몇몇 라이브러리 ( Retrofit, Room, Firebase) 는 보통 빌더나 팩토리 메서드를 통해 객체를 생성하거나 가져온다. 이러한 경우에는 @Binds 를 사용할 수 없어 Provides 를 사용하여 함수에 정의한 객체를 내려주는 것이다.
@InstallIn(SingletonComponent::class)
@Module
object SupaModule {
@Singleton
@Provides
fun provideSupabase(): SupabaseClient {
return createSupabaseClient(
supabaseUrl = "https://uirmrhztunnnrslrzuad.supabase.co",
supabaseKey = "...",
) {
install(Postgrest)
}
}
}
위 코드에서는 @Provides 를 사용하는 방법에 대해서 설명한다.
provide 함수를 만들고 그 안에서 SupabaseClient 를 생성한 것을 리턴해준다.
이는 SupabaseClient 를 필요로 하는곳에 함수에서 정의한것을 주입해 주어라 라는 의미로 볼 수 있다.
❓ 둘다 비슷비슷한데 편하게 Provides 로 다 통일 하면 안될까?
성능적인 측면에서는 @Binds가 일반적으로 더 효율적이며, 특히 큰 규모의 애플리케이션에서는 더 두드러질 수 있다.
코드를 작성 후 빌드를 돌린 뒤 app/build 폴더에 가보면 @Binds 와 @Provide 의 클래스 생성양 차이가 다르다.
그 말인 즉슨, kapt 가 할 일이 줄어들고 그만큼 빌드타임이 줄어든다는것이다.
이러한 부분 차이 때문에 가능한 @Binds 와 @Provide 를 각 상황에 맞추어 생성을 하는것을 추천한다.

위 사진은 테스트로 만들어본 프로젝트에서 hilt 의 codegen 파일들이다.
bind 를 사용한 RepostiroyModule 의 경우 별도의 Factory 생성되지 않은 모습을 볼 수 있다.