본문 바로가기

Android/DI

[Hilt] Hilt를 사용하여 의존성 주입을 해보자.

728x90

처음 학습하면서 작성한 글입니다. 필요시 추후 내용을 수정할 예정입니다.

틀린 부분이 있으면 언제든 지적해주면 감사하겠습니다 :)


안드로이드에서 사용하는 DI 관련 Library인 Dagger2, Koin, Hilt 중 마지막 Hilt에 대한 정리를 해보려고 한다.

Dagger2 기반으로 만들어졌기 때문인지 Dagger2 와 비슷한 부분이 보이지만 확실히 Dagger2 보다는 러닝 커브가 높지 않았던 것 같다.

 

DI가 무엇인지, 왜 사용하는지에 대한 설명은 이전 Dagger2에 대한 글을 작성할 때 작성했으므로 링크를 추가하고 넘어가도록 하겠다.

https://heegs.tistory.com/78?category=916858 

 

[Dagger2] Dagger2를 사용하여 의존성 주입을 해보자

처음 학습하면서 작성한 글입니다. 필요시 추후 내용을 수정할 예정입니다. 틀린 부분이 있으면 언제든 지적해주면 감사하겠습니다 :) 가장 대중적으로 사용하는 DI 인 Dagger2의 기본적인 사용 방

heegs.tistory.com


우선,

Hilt 란 무엇인가?

Dagger 기반으로 빌드 된 Jetpack 권장 DI Library

 

Hilt는 Dagger 기반으로 만들어진 DI 라이브러리이다.

Developers에서 확인해 보면,

 

Dagger가 제공하는 컴파일 타임 정확성, 런타임 성능, 확장성 및 Android 스튜디오 지원의 이점을 누리기 위하여 DI 라이브러리인 Dagger를 기반으로 빌드되었다.

 

라고 한다.

즉, Dagger를 기반으로 Dagger에 있는 다양한 장점들을 가져오고 단점들을 보완해서 만든 Library가 Hilt라는 것이다.

 

기반이 되는 라이브러리가 Dagger이기 때문에 Hilt의 사용 방법은 어느정도 Dagger와 비슷한 부분은 존재한다.

하지만, 러닝커브가 높다는 Dagger의 단점도 보완이 어느정도 되어 Hilt는 Dagger보다 더 편리하고 DI를 사용할 수 있게 되어있다.


이제,

Hilt를 적용하고 사용해보자.

Hilt의 경우 단순하게 라이브러리만 추가해주는 것이 아니라, 추가적인 작업이 필요하다.

 

ext.hilt_version = '2.28-alpha'
dependencies {
	...
    
    classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
}

 

Project 범위의 gradle 에서 다음과 같이 추가하여 classPath를 잡아준다.

 

plugins {
    ...
    id 'kotlin-kapt'
    id 'dagger.hilt.android.plugin'
}
dependencies {
	...
    
	implementation "com.google.dagger:hilt-android:$hilt_version"
	kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
}

 

Module 범위의 gradle 에서는 다음과 같이 추가를 해준다. Plugin도 함께 추가를 해주어야 한다.

 

이렇게 gradle에 설정을 한 후에 사용이 가능하다.


Hilt를 적용하려면,

Koin을 설정할 때와 마찬가지로 Application 단에 @HiltAndroidApp 어노테이션 선언을 해주어야 한다.

필자는 이전 DI를 적용하기위해 사용했던 예제를 사용하여 적용을 하고있기 때문에 클래스 이름은 참고하지 않았으면 한다.

 

 

기존에 사용중이었던 Application 클래스인 KoinAppplication class에 어노테이션을 추가해 주었다.

 

@HiltAndroidApp을 선언해 주었으면,

Hilt를 통해 의존성을 주입 받을 부분에서 @AndroidEntryPoint 어노테이션을 추가해 주도록 하자.

 

@AndroidEntryPoint
class HiltActivity : AppCompatActivity()

 

필자는 새롭게 Hilt를 주입받을 Activity를 선언하여서 @AndroidEntryPoint 어노테이션을 추가해 주었다.

 

주입 받을 위치도 설정을 해주었으니 다음으로는

의존성을 주입할 클래스를 생성해 보자.

 

 

필자는 2개의 클래스를 사용하여 Hilt를 사용해 볼 생각이다.

우선, 보면 알 수 있듯이 @Inject constructor를 사용하여 Hilt를 통해 생성자를 주입시켜 준다.

Hilt를 사용하여 의존성을 주입할 클래스는 이처럼 생성성자를 선언해서 사용해야 하며, 필요한 인자는 내부에 다른 클래스와 동일하게 매개변수로 받아주도록 선언하면 된다.

 

또한, HiltDataSource에 선언한 것 처럼 @Singleton 어노테이션을 사용해 Singleton으로 선언할 수 있다.

 

이렇게 Hilt를 사용하여 주입할 클래스를 만들어 주었으면 그냥 사용하면 되는데,

이전 Dagger2를 사용했을 떄와의 큰 차이가 이 부분인 것 같다.

 

Dagger2를 사용할 때는 Module과 Component를 만들어서 주입시켜주도록 하였는데,

Hilt를 사용할 때는 모든 부분에 대하여 Module을 생성하여 주입시켜줄 필요가 없다.

Hilt에서 Module을 통해 주입하는 경우는, 여러가지 상황으로 인하여 생성자를 주입시킬 수 없는 경우이다.

즉, Interface와 같은 추상적인 클래스 일 때, 외부 라이브러리의 클래스 같이 직접 소유하고 있지 않을 때와 같은 경우에만 Module을 사용하여 주입시켜주면 된다.

이 말은 Retrofit, Room과 같은 것을 사용할 때는 해당 Module 어노테이션을 사용하여 주입을 직접 해주어야 한다는 것이다.

 

필자는 이러한 사실을 정확히 확인하지 않고 Dagger2와 동일하겠지 라는 생각으로 이와 같은 모듈을 생성해 보았다.

 

 

Dagger2와 동일하게 Module, Provides 어노테이션을 선언하여 사용하는 것은 같지만, @InstallIn 이라는 새로운 어노테이션이 있는 것을 볼 수 있다.

Hilt는 Dagger와 다르게 @InstallIn 어노테이션을 사용하여 주입할 클래스를 Hilt에 알려주어야 한다.

여기서 작성되는 Component는 여러개의 종류가 있는데, 우선은 가장 기본적으로 모든 곳에서 사용이 가능함을 의미하는 ActivityComponet를 넣어주었다.

 

이렇게 만들어 두어도 필자가 사용하기 위해 만들었던 클래스는 모두 생성자 주입이 가능한 클래스 이므로,

해당 Module은 사용하지 않으므로 제거해도 무관하다.

 

 

@AndroidEntryPoint 어노테이션을 선언한 HiltActivity에서 다음과 같이 선언을 해준다.

Dagger와 동일하게 @Inject 어노테이션을 사용하여 해당 객체를 주입받아서 사용하면 된다.

 

 

 

정상적으로 주입받아서 사용이 가능한 것을 볼 수 있다.

 


하지만, Dagger에서도 그랬다시피

일반적인 클래스를 주입받아서 사용하는 것 보다, ViewModel을 주입하여 사용하는 것이 더 중요할 것이다.

 

따라서,

Hilt를 사용하여 ViewModel을 주입시켜 보도록 하자.

 

// Hilt ViewModel 사용을 위해 추가.
implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03'
kapt 'androidx.hilt:hilt-compiler:1.0.0'

 

ViewModel에 대한 의존성 주입을 사용하기 위해서는 gradle에 다음과 같은 라이브러리를 추가해 주어야 한다.

 

해당 작업을 한 후 sync를 누르게 되면

문제가 발생한다.

 

@DefineComponent dagger.hilt.components.SingletonComponent is missing a parent declaration.
Please declare the parent, for example: @DefineComponent(parent = ApplicationComponent.class)
[Hilt] Processing did not complete. See error above for details.error: [Hilt]

 

이상한 오류가 발생한다.

 

여러모로 찾아보니, 기존에 작성해두었던 Hilt의 버전이 문제가 있었던 것 같다.

 

ext.hilt_version = '2.36'

 

Hilt의 버전을 2.36으로 변경 한 후에 sync를 돌리면 정상적으로 적용이 되는 것을 확인할 수 있다.

developers 문서와 Hilt 사용 가이드에서도 2.28-alpha로 선언하여 사용했기 때문에 이것이 문제가 될 것이라고는 생각을 못했는데 말이다.

 

ViewModel을 적용하기 위한 설정을 완료했으니, ViewModel을 생성해 보자.

 

 

@ViewModelInject을 사용하여 생성자를 주입해주면 된다고 하는데, 

@ViewModelInject가 Deprecated되어 있다.

 

그래서 어느것으로 사용해야 하는지 다시한번 찾아보았다.

 

 

@HiltViewModel 어노테이션이 생성되고, @ViewModelInject 어노테이션 대신 다른 클래스와 마찬가지로 @Inject 어노테이션을 사용하도록 변경 되었다.

@Inject 하는 부분을 통일시킨것은 정말 좋은 변경인것 같다.

 

이 ViewModel도 마찬가지로, 생성자를 직접 주입할 수 없는 것이 아니기 때문에 Module은 작성하지 않아도 된다.

 

그러면 다시 HiltActivity로 이동하여 ViewModel을 주입 받아서 사용해보도록 하자.

 

private val viewModel: HiltViewModel by viewModels()

 

viewModel을 주입받을 때는 @Inject 어노테이션을 사용하는 것이 아니라 Koin과 비슷하게 by viewModels()를 선언해준다.

 

Logger.d("-viewModel-\n${viewModel.vmText()}\n${viewModel.vmValue()}")

 

이처럼 로그를 찍어서 사용이 가능한지 확인해보자.

 

 

정상적으로 사용이 가능함을 확인할 수 있다.


Hilt를 해당 예제를 사용하여 적용하면서 한가지 큰 문제점을 발견했었다.

 

Dagger를 기반으로 만들어졌다는 것을 인증하는 것 같이

Hilt와 Dagger는 동시에 사용할 수가 없다.

 

그래서 이전에 Dagger2, Koin을 적용해 두었던 프로젝트에서 Hilt도 적용하려고 하자 다양한 오류가 발생하였다.

하지만 새로 프로젝트를 생성해서 적용하기에 많은 코드들을 작성하고, 귀찮아서

Dagger2를 사용했던 부분을 모두 주석처리하고 Hilt를 작성하였다.

 

해당 오류를 보고 느낀 것인데,

Dagger2를 적용하고 있던 프로젝트에서 Hilt로 마이그레이션 하는 것은 생각보다 쉽지 않을 것 같다고 생각했다.

Koin처럼 함께 사용이 가능하다면, 일부를 적용해보고 테스트 하며 점진적으로 변경이 가능하면 좋을 것 같지만

함께 사용이 안되기 때문에 한번에 변경을 해야한다는게 단점이 될 수 있다고 생각한다.

 

해당 게시글에 사용한 예제는 다음 GitHub 에 올려두었다.

https://github.com/HeeGyeong/DiSample/tree/Hilt

 

GitHub - HeeGyeong/DiSample: DiSample

DiSample. Contribute to HeeGyeong/DiSample development by creating an account on GitHub.

github.com

 

*

 

Dagger가 주석처리 된 것을 그대로 커밋하게 되면 추후 혼동이 있을 것으로 보여

Hilt를 적용한 부분은 브랜치를 나누어서 커밋을 해두었습니다.

728x90