Koin < Dagger-Hilt ?????
안드로이드에는 DI 라이브러리로 Dagger, Koin, Hilt 등을 사용하는데
기존 프로젝트에 Koin을 사용하면서 느낀 건 런타임 에러를 잡기다는 거였다..
그래서 기존의 Koin을 Hilt로 변경하기로 결정! 했습니다.
우선 Koin과 Dagger-Hilt의 장단점을 살펴보면
장점
단점
장점
단점
위에서 살펴본 Koin과 Hilt의 장단점을 종합해 보면 배우기만 한다면 Hilt가 더 좋다고 생각된다.
이제 기존의 Koin으로 구현한 코드를 Hilt로 변경해 보면
우선 프로젝트와 앱 모듈단에 각각 의존성을 추가 해줍니다.
buildscript {
ext.hilt_version = '2.35'
dependencies {
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
plugins {
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
}
// Dagger-Hilt
implementation "com.google.dagger:hilt-android:$hilt_version"
kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
class LottoApp: Application() {
override fun onCreate() {
super.onCreate()
startKoin {
androidLogger(Level.NONE)
androidContext(this@LottoApp)
modules(listOf(appModule, viewModelModule, repositoryModule))
}
}
}
@HiltAndroidApp
class LottoApp: Application(){}
위와 같이 Hilt는 Koin처럼 Applicaiton 클래스에 따로 코드를 작성하지 않고 @HiltAndroidApp
어노테이션만 적어주면 된다.
val appModule = module {
single {
Retrofit.Builder()
.baseUrl("https://www.dhlottery.co.kr")
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(LottoAPI::class.java)
}
single {
Room.databaseBuilder(
androidApplication(),
AppDatabase::class.java,
"lotto-app.db")
.build()
}
single { get<AppDatabase>().LottoDao() }
}
val viewModelModule = module {
viewModel { MainViewModel(get()) }
viewModel { GeneNumViewModel() }
viewModel { QRScanViewModel(get()) }
viewModel { SplashViewModel(get()) }
}
val repositoryModule = module {
single { AppRepository(get(),get()) }
}
@Module
@InstallIn(SingletonComponent::class)
object Modules {
@Provides
@Singleton
fun provideApiService(
): LottoAPI {
return Retrofit.Builder()
.baseUrl("https://www.dhlottery.co.kr")
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(LottoAPI::class.java)
}
@Provides
@Singleton
fun provideDatabase(@ApplicationContext context: Context): AppDatabase {
return Room.databaseBuilder(
context,
AppDatabase::class.java,
"lotto-app.db"
)
.build()
}
@Provides
@Singleton
fun provideLottoDao(database: AppDatabase): LottoDao {
return database.LottoDao()
}
@Provides
@Singleton
fun provideRepository(dao: LottoDao, api: LottoAPI): AppRepository {
return AppRepository(dao, api)
}
}
코인처럼 모듈 클래스를 생성하는데 이 부분만 봐도 코인이 더 쉽다는 걸 느낄 수 있다.
Hilt는 클래스 상단에 @Module
어노테이션을 적고 @InstallIn(SingletonComponent::class)
이런식으로 어느 컴포넌트에 적용할 지 정할 수 있습니다.
이제 액티비티와 뷰모델에서의 차이점을 보면
class MainActivity : BaseActivity<ActivityMainBinding,MainViewModel>(R.layout.activity_main) {
override val viewModel: MainViewModel by inject()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
@AndroidEntryPoint
class MainActivity : BaseActivity<ActivityMainBinding,MainViewModel>(R.layout.activity_main) {
override val viewModel: MainViewModel by viewModels() // ViewModel을 주입
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
Hilt는 액티비티 상단에 @AndroidEntryPoint
어노테이션을 사용하는데 Activity, Fragment, View, Service, BroadcastReceiver 같은 안드로이드 컴포넌트에 사용할 수 있는 어노테이션입니다.
Koin은 위에 모듈 클래스에 ViewModel을 선언해 주었기 때문에 따로 뷰모델에 코드를 작성하지 않습니다.
@HiltViewModel
class MainViewModel @Inject constructor(
private val repository: AppRepository
): BaseViewModel() {
...
}
Hilt에서는 뷰모델에 위와 같이 구현하는데 @HiltViewModel
어노테이션을 사용하면 ViewModelProvider.Factory
를 매번 구현하지 않고 사용할 수 있습니다.
@Inject
어노테이션을 사용하여 뷰모델에서 필요한 repository 인스턴스를 주입 받을 수 있습니다.
끝👍