[Android] Koin을 Dagger-Hilt로 변환하기

박상군·2022년 5월 31일
0

Dependency Injection

목록 보기
2/5
post-thumbnail

Koin < Dagger-Hilt ?????

Koin사용법

안드로이드에는 DI 라이브러리로 Dagger, Koin, Hilt 등을 사용하는데
기존 프로젝트에 Koin을 사용하면서 느낀 건 런타임 에러를 잡기다는 거였다..

그래서 기존의 Koin을 Hilt로 변경하기로 결정! 했습니다.

우선 Koin과 Dagger-Hilt의 장단점을 살펴보면

Koin

장점

  • 러닝커브가 낮다 (쉽게 배울 수 있음)
  • Kotlin에 최적화 되어 있음

단점

  • 런타임에 인스턴스를 동적으로 주입하기 때문에 런타임 퍼포먼스가 떨어짐
  • 런타임 에러가 발생할 수 있음

Dagger-Hilt

장점

  • Dagger2를 기반으로 만들어졌기 때문에 기존에 Dagger2를 사용하던 분들이라면 쉽게 적응 가능
  • 컴파일할 때 에러 검출 가능

단점

  • 컴파일 타임에 주입하기 때문에 컴파일 시간이 오래 걸림
  • Dagger2에 비교하면 쉽지만 Koin과 비교하면 러닝커브가 높음

위에서 살펴본 Koin과 Hilt의 장단점을 종합해 보면 배우기만 한다면 Hilt가 더 좋다고 생각된다.


이제 기존의 Koin으로 구현한 코드를 Hilt로 변경해 보면

1. 의존성 추가

우선 프로젝트와 앱 모듈단에 각각 의존성을 추가 해줍니다.

Project

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
    }
}

AppModule

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"

2. 구현

Application

Koin

class LottoApp: Application() {
   override fun onCreate() {
        super.onCreate()
        startKoin {
            androidLogger(Level.NONE)
            androidContext(this@LottoApp)
            modules(listOf(appModule, viewModelModule, repositoryModule))
        }
    }

}

Hilt

@HiltAndroidApp
class LottoApp: Application(){}

위와 같이 Hilt는 Koin처럼 Applicaiton 클래스에 따로 코드를 작성하지 않고 @HiltAndroidApp 어노테이션만 적어주면 된다.

Module

Koin

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()) }
    }

Hilt

@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) 이런식으로 어느 컴포넌트에 적용할 지 정할 수 있습니다.

이제 액티비티와 뷰모델에서의 차이점을 보면

Activity

Koin

class MainActivity : BaseActivity<ActivityMainBinding,MainViewModel>(R.layout.activity_main) {
    override val viewModel: MainViewModel by inject()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

    }

Hilt

@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 같은 안드로이드 컴포넌트에 사용할 수 있는 어노테이션입니다.

ViewModel

Koin

Koin은 위에 모듈 클래스에 ViewModel을 선언해 주었기 때문에 따로 뷰모델에 코드를 작성하지 않습니다.

Hilt

@HiltViewModel
class MainViewModel @Inject constructor(
private val repository: AppRepository
): BaseViewModel() {
...
}

Hilt에서는 뷰모델에 위와 같이 구현하는데 @HiltViewModel 어노테이션을 사용하면 ViewModelProvider.Factory를 매번 구현하지 않고 사용할 수 있습니다.
@Inject 어노테이션을 사용하여 뷰모델에서 필요한 repository 인스턴스를 주입 받을 수 있습니다.


마무리

이렇게 기존에 구현한 Koin을 Hilt로 변경했는데 확실히 컴파일 타임에 에러를 잡을 수 있어서 좋은 것 같습니다.

하지만 컴파일 시간이 더 오래 걸린다는 거...

끝👍

0개의 댓글