Android & Kotlin
Hilt 관련 리펙토링 기록 (@binds @Provides 차이 , UsecaseModule)
쉽코기
2023. 1. 8. 15:39
구조설명 (클린 아키텍처)
1. 클린아키텍처 구조에서 Hilt 를 통해 의존성 주입을 사용하고 있었다.
2. 이때 Repository (Interface) 의 주입을 위해 RepoisotyModule 을 만들어 사용했고 @Provides 를 사용해왔었다.
3. 그리고 Usecase 에서 Ropository 와 ErrorHandler 에 의존했고 이에 따라 각각 UsecaseModule, NetowrkModule 을 정의해주어 사용했다.
class GetSomeUseCase(
private val repository: Repository,
private val errorHandler: ErrorHandler
) : BaseUseCase() {...}
@ErrorHandler
@Provides
@Singleton
fun provideErrorHandler(
@HttpClient http: OkHttpClient,
appStorage: AppStorage
): ErrorHandler =
NetErrorHandlerImpl(
http.retrofit(appStorage.getBackBaseUrl())
)
@Provides
@ViewModelScoped
fun provideGetSomeUseCase(
repo: Repository,
@ErrorHandler errorHandler: ErrorHandler
) = CheckUsedAddressUseCase(repo, errorHandler)
@Provides
@Singleton
fun provideRepository(
apiSevice: Apiservice
): Repository =
RepositoryImpl(apiSevice)
@provides 와 @Binds 차이
외부적으로 기능적 차이를 보이지 않지만 사용법에 차이에서 비록한 차이가 존재한다.
1. @Binds / @povides 를 통한 주입 방법 정의 차이
@Module
@InstallIn(SingletonComponent::class)
abstract class RepositoryModule {
@Binds
@Singleton
abstract fun bindRepository(
repository: RepositoryImpl
): Repository
@Module
@InstallIn(SingletonComponent::class)
object RepositoryModule {
@Provides
@Singleton
fun provideRepository(
apiSevice: ApiService
): Repository =
RepositoryImpl(apiSevice)
2. 객체 주입시 사용 방법의 차이
- @binds 의 경우 @Inject constructor 를 붙여줌으로써 해당 부분에서의 의존성 주입을 명시해주고 @provids 의 경우 그렇지 않고 모듈에서 찾아 주입해주게된다.
이런 제약으로 인해 @binds 는 서드파티 라이브러리에 대한 의존성 주입이 불가하기에 해당 경우엔 @provids 를 사용할 수 밖에 없다.
또 아래와 같이 usecaseModule 의 필요성 유무를 결정 짓기도하다. usecase 에서 인터페이스인 Repo 를 의존할 경우 @provids 의 경우 @Inject 키워드를 붙이기 않기때문에 모듈을 반드시 필요로 하지만 @binds 의 경우 @Inject 를 통해 usecase 모듈을 생성을 하지 않아도 된다.
그렇다면 @Provids 로 주입 객체를 정의하고 @Inject constructor 를 붙여준다면 usecaseModule 없이 동작할 수 있을까?
실험결과 이것 또한 가능 했다.
class GetSomeUseCase @Inject constructor(
private val Repository: Repository,
@ErrorHandler private val errorHandler: ErrorHandler
) : BaseUseCase() {
fun execute() =
Repository.getSomeUseCase()
.toNetResult(errorHandler)
}
(@binds 를 통한 객체 생성후 주입의 경우 usecaseModule 불필요)
@Provides
@ViewModelScoped
fun provideGetSomeUseCase(
repo: Repository,
@ErrorHandler errorHandler: ErrorHandler
) =
GetSomeUseCase(repo, errorHandler)
class GetSomeUseCase(
private val Repository: Repository,
private val errorHandler: ErrorHandler
) : BaseUseCase() {
(@provids 의 경우 @Inject Constutor 없을 경우 usecaseModule 필요)