의존성 주입, Hilt 기초부터 해보실래요?

10712 단어 androidandroid

이번 글은 Android Jetpack 의존성 주입 라이브러리인 Hilt 에 대한 기초를 정리한 글입니다.

의존성 주입의 기초적인 부분을 알고있다는 가정 하에 작성하였습니다. 만약 의존성 주입의 기초에 대해 제가 정리한 글을 읽어보고 싶으시다면 아래 링크를 통해 읽으실 수 있습니다.

Dependency Injection(DI), 의존성 주입에 대해 조금 쉽게? 접근해보기


🌱 의존성 주입 방식

의존성 주입에는 다음과 같은 세가지 방법이 있습니다.
1. 생성자 주입
2. 메소드 (Setter) 주입
3. 필드 주입

Android MVVM 구조와 Repository Pattern 을 사용하여 개발을 하고 있는 저는 첫번째 방식인 생성자 주입을 Repository구현체와 ViewModel에서 다음과 같이 주로 사용합니다.

class InjectTargetRepository(
	private val injectedAPI: InjectedAPI
)

class InjectTargetViewModel(
    private val injectedRepository: InjectedRepository
)

하지만 위에서 생성한 ViewModel 을 View 에서 사용하기 위해서는 View 에서 Repository 객체를 생성하여 ViewModel 생성 시 함께 주입해주어야 합니다. 이렇게 된다면 View 와 Repository 또한 뗄래야 뗄 수 없는 사이가 되어버립니다.

이러한 문제점을 해결하기 위해서 Koin, Dagger, Hilt 와 같은 의존성 주입을 도와주는 라이브러리를 사용합니다. 이 중 우리는 Hilt 에 대해서 알아보겠습니다.

🗡 Hilt

Hilt 라이브러리는 Dagger 기반의 컴파일 타임에 코드를 생성하는 의존성 주입 라이브러리 입니다. 이전의 Dagger 라이브러리는 많은 Annotation 과 보일러 플레이트로 인해서 러닝커브가 높다는 평을 받아 사용하기 쉽지 않았습니다.

하지만 이러한 단점들을 보완하고 Android 프레임워크에 알맞게 수정된 Hilt 라이브러리가 Android 공식블로그에 따르면 5월 4일부로 Stable Release 를 사용할 수 있는 것을 알 수 있습니다.

자세한 사항을 알아보기 전에, 먼저 어떻게 사용하는지 부터 간단하고 빠르게 알아보도록 하겠습니다.

@HiltAndroidApp
class HiltExampleApplication: Application() {...}

@AndroidEntryPoint
class HiltActivity: Activity() {...}

@HiltViewModel
class HiltViewModel : ViewModel() {...}

위 코드에서 볼 수 있듯이, 기본적인 Annotation 세가지가 존재합니다.

  • Android의 Application을 지정해주는 @HiltAndroidApp
  • EntryPoint 를 지정해주는 @AndroidEntryPoint
  • ViewModel 을 지정해주는 @HiltViewModel

그 후에는 아래와 같이 Android class 에 생성자 주입을 진행해주어야 해당 모듈이 정상적으로 주입이 됩니다. 주입 시에는 @Inject Annotation 을 사용합니다.

@HiltViewModel
class SimpleViewModel
@Inject
constructor(
  	private val simpleRepository: SimpleRepository
) : ViewModel() { ... }

또한 @HiltViewModel 은 Android AAC ViewModel 주입도 지원합니다. 그리고, Navigation Graph 에 따른 SharedViewModel 또한 지원합니다.

@AndroidEntryPoint
class SimpleFragment: Fragment() {
  	private val simpleViewModel: SimpleViewModel by viewModels()
  	private val simpleSharedViewModel: SimpleSharedViewModel by hiltNavGraphViewModels(R.id.simple_graph)
  	...
}

그렇다면 위 코드에서 생성자 주입을 했는데, 이 주입하려는 모듈들은 어떻게 생성될까요?

@Module
@InstallIn(SingletonComponent::class)
object SimpleModuleObject {
  	@Provides
  	fun provideSimpleModule(): SimpleInterface = SimpleInterfaceImpl
}
@Module
@InstallIn(SingletonComponent::class)
abstract class SimpleModuleAbstract {
  	@Binds
  	abstract fun bindSimpleInterface(simpleInterfaceImpl: SimpleInterfaceImpl): SimpleInterface
}

위의 코드에서 볼 수 있듯이, 모듈을 생성할 때는 기본적으로 네 가지 Annotation이 필요합니다.

  • @module
  • @InstallIn
  • @Provides
  • @Binds

@module Annotation

@HiltAndroidApp@AndroidEntryPoint@HiltViewModel 등, 해당 Object 나 Class 가 Hilt 에서 의존성 주입에 사용하게 될 모듈임을 알려주는 역할을 합니다.

@InstallIn Annotation

해당 모듈을 어떤 곳에 설치할 지를 알려주는 역할을 합니다. 설치할 수 있는 컴포넌트는 다음과 같습니다.

위 컴포넌트들의 lifetime 에 대해서는 다음 링크를 참조하시면 되겠습니다.

@Binds Annotation

만약 주입이 필요한 모듈 중, Interface가 있다면 생성자 주입을 할 수 없기에, 해당 Interface의 구현체를 Binding 하는 역할을 합니다. 주로 Android 에서 Repository Pattern 을 사용하는 프로젝트에서, Repository layer 를 예시로 들 수 있겠습니다.

@Provides Annotation

외부 라이브러리에서 생성되는 인스턴스이거나 Builder Pattern 으로 생성되는 인스턴스에 대한 주입 객체를 생성할 때 필요합니다. 주로 Android 에서는 Retrofit 객체나, OkHttp Client 객체를 Singleton 방식으로 생성하는 데에 사용합니다.

✅ Test

이제는 Hilt 를 사용하여 Test code 에 어떻게 적용하는 지 알아보겠습니다.

사실 Hilt 는 Unit Test 에서는 효용가치가 별로 없다고 볼 수 있습니다. 생성자 주입을 진행할 때, 실제 모듈을 Inject 하는 방식보다는 Mocking 된 의존성을 주입하면 되기 때문입니다.

그렇다면 UI Test 에서는 어떻게 사용되는지 알아보도록 하겠습니다.

Android UI Test 를 진행할 때, @HiltAndroidTest Annotation 을 사용합니다.

@HiltAndroidTest
class SimpleFragmentTest { ... }

물론, 기본적인 모듈을 생성하는 것처럼 Test 에서 사용할 모듈을 테스트 클래스 내에서 선언하여 사용할 수도 있습니다.

지금까지 Hilt 의 기본 Annotation 과 사용법, 그리고 어디에 사용하는 지에 대해서 알아보았습니다.

Hilt 는 컴파일 타임에 주입할 코드를 생성하기 때문에 컴파일 타임에 오류를 잡아낼 수 있는 장점이 있습니다.

또한 다른 DI 라이브러리도 있지만, 구글 공식 문서에서 DI 의 Best Practice 로 발표한 Hilt 가 다른 라이브러리 보다는 좀 더 안드로이드에서 Stable 할 것으로 생각됩니다. 한 번 써보시는 것도 좋을 것 같습니다.


References

Hilt is stable! Easier dependency injection on Android

Android DI with Hilt — Hilt를 이용한 의존성 주입

안드로이드 Hilt 알아보기

Dagger Hilt로 안드로이드 의존성 주입 시작하기


좋은 웹페이지 즐겨찾기