Android/Hilt

[Android] Hilt와 Koin의 비교

태크민 2024. 11. 11. 23:53

Hilt와 Koin의 비교

Dagger2의 경우 적용을 위해 환경을 세팅하는 과정이나 학습에 있어서 러닝커브가 크다. 이로 인해 Dagger2 보다 러닝 커브는 낮지만 Dagger를 기반으로 만들어진 Hilt를 많이 사용하고 있다. 또한 안드로이드 개발시 Kotlin 만을 사용한 프로젝트들이 많아지고 있어 Koin 또한 많이 사용하고 있다.

 

그래서! Hilt와 Koin 중 어떤 것을 프로젝트에 사용하는 것이 더 좋을지 이 둘의 장단점과 차이에 대해 비교해보자.

 

먼저, 각각의 장단점에 대해 알아보자.

 


Koin

장점

  • Dagger2나 Hilt 에 비해 러닝커브가 낮다.
  • Koin의 경우 Kotlin DSL로 만들어져 Kotlin 개발 환경에 도입이 쉽다.
  • 어노테이션을 사용하지 않아 컴파일 시간이 단축됨. → 어노테이션 프로세서의 경우 컴파일시간에 발생하기 때문에!!

단점

  • 런타임 시에 서비스 인스턴스를 동적으로 주입해주기 때문에 런타임 성능이 떨어진다.
  • 마찬가지로 런타임시에 주입이 이루어져, 언제 오류가 날지 정확하게 알 수 없어 앱의 안정성이 떨어진다.
  • 자바의 리플렉션을 이용하기 때문에 성능 상 좋지 않음. → 리플렉션은 속도를 저하시키기 때문에 구글에서 권장하지 않음.
  • koin.get() 사용시 의존 관계 파악이 어려워 멀티 모듈 사용시 문제가 된다.
    • koin.get()을 사용하는 경우 해당 인스턴스가 언제 생성되었는지 파악하기 어려움, 어디서 어떻게 주입되는지 몰라 인스턴스 주입이 필요한 클래스가 많아지면 런타임시 객체 주입에 대한 검증이 어려워짐 -> 각 사용처에 적합한 Koin모듈을 생성해 주입함으로써 어디서 어디로 주입되었는지 유추가능

Koin에서 koin.get() 사용 시 발생하는 문제와 해결 방법

1️⃣ koin.get<T>()을 직접 사용하는 경우 (문제점이 있는 방식)

Koin에서 koin.get<T>()을 사용하면, 언제, 어디서 해당 객체가 생성되고 주입되는지 추적하기 어려운 문제가 발생할 수 있다.

class SearchUserViewModel : ViewModel() {
    private val getUserUseCase: GetUserUseCase = koin.get()

    fun searchUser(phoneNumber: String) {
        getUserUseCase(phoneNumber)
    }
}

 

문제점

  • 인스턴스 생성 시점을 파악하기 어려움
    • koin.get<T>()을 호출하면 Koin이 의존성을 자동으로 주입하지만,
      해당 객체가 언제 생성되었는지 명확하지 않음
  • 어디서 어떻게 주입되는지 모호함
    • koin.get()을 여러 곳에서 사용하면, 어떤 클래스가 특정 객체를 사용하는지 추적하기 어려움
    • 유지보수 시, 어떤 객체가 어떤 곳에서 필요로 하는지 한눈에 파악하기 어려움
  • 런타임 시 객체 주입 검증이 어려움
    • koin.get()은 런타임 시점에서 의존성을 주입하므로,
      컴파일 타임에서는 의존성 주입 오류를 검출할 수 없음

2️⃣ `Koin 모듈을 활용하여 명확하게 주입하는 경우 (권장 방식)

koin.get()을 직접 사용하지 않고, 각 사용처에 적합한 Koin 모듈을 명확하게 정의하여 주입하는 방식으로 해결할 수 있다.

 

(1) Koin 모듈을 정의

val appModule = module {
    single { UserRepository() }
    factory { GetUserUseCase(get()) } // UserRepository를 주입받아 사용
}

val viewModelModule = module {
    viewModel { SearchUserViewModel(get()) }
}

 

(2) 생성자 주입을 활용한 SearchUserViewModel

class SearchUserViewModel(private val getUserUseCase: GetUserUseCase) : ViewModel() {

    fun searchUser(phoneNumber: String) {
        getUserUseCase(phoneNumber)
    }
}

 

(3) ViewModel을 Koin에서 주입받아 사용

class SearchUserActivity : AppCompatActivity() {
    private val searchUserViewModel: SearchUserViewModel by viewModel()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // ViewModel을 사용하여 기능 실행
        searchUserViewModel.searchUser("010-1234-5678")
    }
}

 

두 방식 비교

  koin.get() Koin 모듈을 활용한 주입 
객체 생성 시점 koin.get()이 호출될 때 생성됨 (명확하지 않음) Koin이 관리하는 모듈에서 생성됨 (예측 가능)
의존성 관리 의존성이 어디서 관리되는지 알기 어려움 Koin 모듈에서 어떤 객체를 제공하는지 명확하게 정의됨
코드 유지보수성 사용 클래스마다 koin.get()을 호출해야 함 생성자 주입 방식으로 모듈만 수정하면 유지보수 쉬움
런타임 오류 가능성 koin.get<T>()으로 직접 주입 시, 해당 객체가 Koin에 정의되지 않았을 경우 런타임 오류 발생 Koin 모듈에서 의존성을 관리하므로 안정적인 주입 가능
테스트 용이성 직접 주입하므로 Mock 객체를 대체하기 어려움 생성자 주입이므로 테스트 시 Mock 객체를 쉽게 대체 가능

 


Hilt

장점

  • Dagger에 비해 적은 코드
    • Hilt는 Dagger와 달리 모듈 하나만 생성하면 되고, Component는 안드로이드 클래스 단위로 사전에 생성되어 있어 Annotation으로 쉽게 설정할 수 있다.
  • 안드로이드 클래스 최적화
    • Application, Activity, Fragment, ViewModel, View, Service 등 안드로이드 클래스에 최적화 되어 있음.
  • LifeCycle 관리
    • 안드로이드 클래스에 최적화된 Hilt는 각 클래스의 생명주기에 따라 구성요소의 인스턴스를 생성 및 제거 해줌.
  • 컴파일 타임에 의존성 주입에 대한 검증을 수행하고, 런타임에는 필요한 시점에 주입이 이뤄지므로, 주입 오류를 사전에 방지할 수 있다.

단점

  • 러닝커브가 Koin에 비해 높다. 써야 할 것이나 배워야 할 것이 훨씬 많음!
  • KAPT를 통해 Java Stub 파일을 생성해 주입을 위한 보일러플레이트 코드를 생성한다. 또한 빌드 시간이 늘어난다.

비교 정리

  • Koin: Runtime 시 주입을 하는 방식인 Service Locator 패턴을 사용함.
  • Hilt: 컴파일 타임에 주입에 대한 검증을 마치고, 어플리케이션 시작 시 주입 시점 단 한번에 반영되는 형태로 구성되어 있음.

 

 

 

위의 그림과 같이 Hilt의 경우 Compile 타임에 주입해주고, Koin의 경우 Runtime에 주입해준다.

주입 시간에 따른 차이로 Koin의 경우 잘못된 코드로 작성했을 경우, 오류를 잡기가 힘들고 오류를 놓쳤을 때의 불안함이 있다. 하지만 Hilt에 경우 초기부터 오류가 발생하기 때문에 정확하게 오류를 잡아갈 수 있다.

 

결국 Koin이 Hilt보다 러닝커브가 낮고, 코드도 간단하기 때문에 프로젝트 크기 등을 고려하여 유동적으로 어떤 것을 사용할지 결정하는 것이 좋아보인다!

 


참고자료


https://goodbegunishalfdone.tistory.com/entry/Android-DI-Hilt-vs-koin-%EB%B9%84%EA%B5%90-1

 

[Android] DI - Hilt vs koin 비교 (1)

최근에 새로운 프로젝트를 진행하면서 잠시 놓고 있었던 안드로이드를 계속해서 복습하고 있다. 오늘은 Hilt에 대해 복습하기 전에 왜 내가 koin에서 Hilt로 옮겼는지에 대해 기억을 더듬어봤고,

goodbegunishalfdone.tistory.com

https://blog.banksalad.com/tech/migrate-from-koin-to-hilt/

 

뱅크샐러드 안드로이드 앱에서 Koin 걷어내고 Hilt로 마이그레이션하기 | 뱅크샐러드

blog.banksalad.com