[Android] Hilt @Binds와 @Provides

안세홍·2024년 11월 18일
post-thumbnail

Hilt 라이브러리를 이용해 의존성을 주입하는 경우에 Module에서

abstract class에는 @Binds 어노테이션이,

object에는 @Provides 키워드가 붙게 되는데 어떤 이유에서인지 알아보겠습니다.

작성한 예시 코드를 보여드리며 설명하겠습니다.

1. DataModule (@Binds 사용)

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

    @Binds
    abstract fun bindsDataStoreDataSource(
        dataStoreDataSource: DataStoreDataSourceImpl
    ): DataStoreDataSource

    @Binds
    abstract fun bindsDataStoreRepository(
        dataStoreRepository: DataStoreRepositoryImpl
    ): DataStoreRepository

    @Binds
    abstract fun bindsUserDataSource(
        authDataSource: UserDataSourceImpl
    ): UserDataSource

    @Binds
    abstract fun bindsUserRepository(
        authRepository: UserRepositoryImpl
    ): UserRepository
    ...
}

설명

  • @Binds인터페이스와 구현체 간의 관계를 바인딩하기 위해 사용됩니다.
  • 위 코드에서 DataStoreDataSource, DataStoreRepository, UserDataSource, UserRepository인터페이스, 그리고 DataStoreDataSourceImpl, DataStoreRepositoryImpl, UserDataSourceImpl, UserRepositoryImpl구현체입니다.
  • 추상 메서드를 통해, Hilt는 자동으로 인터페이스의 구현체를 의존성 주입에 사용할 수 있도록 설정합니다.

특징

  1. 단순한 인터페이스-구현체 바인딩:
    • 인터페이스와 구현체만 연결하면 되므로 객체 생성 로직이 필요하지 않습니다.
    • Hilt가 직접 생성자를 호출해 필요한 객체를 생성합니다.
  2. 효율적:
    • 구현체의 기본 생성자가 있거나, 생성자 주입(@Inject)을 이미 정의한 경우 사용할 수 있습니다.
  3. 추상 클래스만 허용:
    • @Binds를 사용하는 모듈은 반드시 abstract 키워드를 사용해야 합니다.

장점

  • 코드가 간결하며, 불필요한 생성 로직이 없습니다.
  • 성능적으로 더 효율적입니다.

2. NetworkModule (@Provides 사용)

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

    @Provides
    @Singleton
    fun providesOkHttpClient(
        @ApplicationContext context: Context,
        tokenProvider: () -> String
    ): OkHttpClient {
        val builder = OkHttpClient.Builder()
            .addInterceptor(AuthInterceptor(tokenProvider))
            .addNetworkInterceptor(
                HttpLoggingInterceptor().apply {
                    if (BuildConfig.DEBUG) {
                        level = HttpLoggingInterceptor.Level.BODY
                    }
                }
            )

        if (BuildConfig.DEBUG) {
            builder.addNetworkInterceptor(ChuckerInterceptor(context))
        }

        return builder.build()
    }

    @Provides
    @Singleton
    fun providesRetrofit(okHttpClient: OkHttpClient): Retrofit =
        Retrofit.Builder()
            .client(okHttpClient)
            .baseUrl(BuildConfig.BASE_URL)
            .addCallAdapterFactory(ApiResultCallAdapterFactory())
            .addConverterFactory(GsonConverterFactory.create())
            .build()

    @Provides
    @Singleton
    fun providesUserService(retrofit: Retrofit): UserService =
        retrofit.create(UserService::class.java)
    ...
}

설명

  • @Provides는 복잡한 로직을 포함한 객체를 생성하거나, 외부 라이브러리에서 제공하는 객체를 의존성으로 주입해야 할 때 사용됩니다.
  • 예를 들어, OkHttpClient, Retrofit, 그리고 UserService 는 간단한 생성자 호출만으로 생성되지 않으며, 추가 설정이나 생성 과정이 필요합니다.

특징

  1. 구체적인 생성 로직 포함 가능:
    • OkHttpClient의 경우, Interceptor, Logging 등을 추가로 설정하며 객체를 생성합니다.
    • RetrofitOkHttpClient와 함께 특정 설정을 추가해 객체를 만듭니다.
  2. 외부 라이브러리와 연동:
    • Retrofit, OkHttp 등 외부 라이브러리 객체를 의존성 주입에 사용할 때 활용됩니다.
  3. 구현된 메서드 사용:
    • 반드시 객체를 반환하는 구체적인 메서드로 작성되어야 합니다.

장점

  • 복잡한 객체 생성 과정이 포함된 경우 적합합니다.
  • 외부 의존성이나 동적으로 값을 가져와야 하는 경우에 유연합니다.

3. 두 예제의 차이점 비교

특징DataModule (@Binds)NetworkModule (@Provides)
사용 목적인터페이스와 구현체 연결복잡한 로직을 포함한 객체 생성
메서드 타입추상 메서드 (abstract method)구체적 메서드 (concrete method)
코드 구조단순하며, 구현체와 인터페이스를 연결하는 데 적합복잡한 로직이나 외부 라이브러리 객체 생성을 지원
객체 생성 방식Hilt가 생성자를 호출해 자동으로 생성메서드 내에서 객체 생성 로직을 직접 작성
성능 및 효율성더 효율적 (추가 로직이 없음)약간의 오버헤드가 발생
사용 사례Repository, DataSource 같은 단순 구현체와 인터페이스의 연결OkHttpClient, Retrofit, 외부 라이브러리 객체 생성

4. 언제 어떤 것을 사용할까?

  • @Binds:
    • 좀 더 정확히 말하면 인터페이스는 생성자를 정의할 수 없기 때문에 DataStoreRepository를 상속받는 구현체(DataStoreRepositoryImpl)를 Hilt에게 제공합니다. (Hilt 에게 DataStoreRepository 만 알려줘도 DataStoreRepositoryImpl 에서 override한 메소드를 쓸 수 있는 이유는 Module에서 정의되고 있었기 때문입니다.)
    • 예: Repository, DataSource 같은 내부 객체 간의 바인딩.
  • @Provides:
    • @Provides는 Hilt에 의존성을 주입하려는 인스턴스 클래스가 외부 라이브러리(Retrofit, OkHttp, Room, DataStore 등)를 사용하는 경우 혹은 빌더 패턴으로 인스턴스를 생성하는 경우에 사용합니다.
    • 예: Retrofit, OkHttpClient 같은 네트워크 관련 객체 생성.

참고

https://develop-oj.tistory.com/69

profile
나만의 개발 일기

0개의 댓글