일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
- Hilt
- android DI
- 안드로이드 의존성주입
- 안드로이드 Espresso
- Android App Architecture Guideline
- 안드로이드 앱 아키텍처 가이드라인 설명
- RxJava
- 안드로이드 최적화
- Koin
- 안드로이드 클린 아키텍처
- 안드로이드 앱 아키텍처 가이드라인
- 안드로이드 hilt
- 코루틴
- 안드로이드 Mockito
- 안드로이드 앱 아키텍처 가이드라인 사용법
- 스타트업 코딩테스트
- 안드로이드 JUnit
- MVVM
- 안드로이드 아키텍처 컴포넌트
- android memory leak
- android clean architecture
- 리싸이클러뷰 최적화
- Android MVVM
- 안드로이드 테스트코드
- sharedFlow
- coroutine
- 안드로이드 mvvm예제
- 안드로이드 mvvm
- 안드로이드 앱 아키텍처 가이드라인 예시
- 안드로이드 리싸이클러뷰
- Today
- Total
안드로이드 연구소
[여기가 DI 설명 제일 잘함] Dagger1, Dagger2 그리고 Hilt 본문
지난번에 포스트에서 의존중인 코드가 어떤것들인지,
의존중이 코드를 없애기위해 어떻게 3가지 방법으로 의존성을 주입하였는지(Dagger, Hilt, Koin아님),
가장 손쉽게 의존성을 주입한 3번째 방법을 하기위해 어떤 라이브러리들이 있었는지(Dagger, Hilt, Koin맞음)
알아보았습니다.
그렇다면 오늘 이 라이브러리들 사용법에 대해서 알아보겠습니다.
첫번째는 바로 Dagger입니다.
앞전의 의존성에 문제가 있었던 예제를 Dagger를 사용하면 어떻게 해결할 수 있는지 확인해볼까요?
class UserViewModel : ViewModel() {
private val userRepository = UserRepository()
// using userReposiroty instance ...
}
Q1. ChatGPT, 위 예제를 Dagger1를 사용한 예제를 보여줘.
최신버전: https://github.com/google/dagger/releases
dependencies {
implementation 'com.google.dagger:dagger:<version>'
annotationProcessor 'com.google.dagger:dagger-compiler:<version>'
}
첫번째로 @Module과 @Provides를 사용하여 "UserRepository 종속성을 제공하는 모듈"을 정의합니다.
UserRepository클래스가 ViewModel안에 종속성 주입이 될것이기 때문에
UserRepository를 @Provides 종속시켜준다.
@Module
class UserModule {
@Provides
UserRepository provideUserRepository() {
return new UserRepository();
}
}
두번째로 위에서 만든 모듈 클래스를 포함한 컴포넌트를 생성합니다.
@Component(modules = UserModule.class)
interface UserComponent {
void inject(UserViewModel viewModel);
}
세번째로 viewModel에서 Dagger컴포넌트를 생성하여 의존성을 주입하면
이 수정된 코드에서 userRepository 필드에 @Inject 주석을 추가하여 주입해야 함을 나타냅니다.
UserComponent 구성 요소의 인스턴스를 만든 다음 component.inject(this)를 호출하여 주입을 수행합니다.
class UserViewModel : ViewModel() {
@Inject
UserRepository userRepository;
init {
// Create the Dagger component
UserComponent component = DaggerUserComponent.create();
// Perform injection
component.inject(this);
}
}
이제 @Inject한 userRepository를 호출없이 사용가능합니다.
한번 세팅만 해놓는다면 너무나 편하겠죠?
근데 Dagger 뭔가 복잡해보이는감이 없지 않아있네요.
이를 더 사용할 수 있도록 만든 Dagger2를 확인해볼까요?
Q2. 위 예제를 Dagger2 사용하여 해결한 예제를 보여줘.
첫번째로, 아까와 똑같이 UserModule 모듈을 생성합니다.
@Module
class UserModule {
@Provides
fun provideUserRepository(): UserRepository {
return UserRepository()
}
}
두번째로, UserComponent 컴포넌트 인터페이스를 생성합니다.
아까와는 달리 @Component(modules = UserModule.class) 사용해서 모듈을 연결시키지 않고
아까와 같이 상위 종속 클래스인 viewModel를 파라미터로 받게 합니다.
@Component
interface UserComponent {
fun inject(viewModel: UserViewModel)
}
마지막으로 UserViewModel에서
의존성을 주입할 userRepository에 @Inject어노테이션을 추가하고
생성해둔 Dagger컴포넌트를 생성하고 주입을 실행합니다.
class UserViewModel : ViewModel() {
@Inject
lateinit var userRepository: UserRepository
init {
// Create the Dagger component and perform injection
DaggerUserComponent.create().inject(this)
}
// Use the injected userRepository instance...
}
근데 Dagger2도 Module클래스와 component인터페이스를 만들어서 관리를 해줘야하는 귀찮음이 있습니다.
이러한 수고 없이 의존성 주입 가능할까요?
Q3. 위 예제를 Hilt 사용하여 해결한 예제를 보여줘.
최신버전: https://developer.android.com/jetpack/androidx/releases/hilt?hl=ko
build.gradle(app)
plugins {
id("com.google.dagger.hilt.android") version "2.44" apply false
}
build.gradle(android)
plugins {
id("dagger.hilt.android.plugin")
}
dependencies {
implementation 'com.google.dagger:hilt-android:$version'
kapt 'com.google.dagger:hilt-android-compiler:$version'
}
첫번째로 애플리케이션 클래스에서 @HiltAndroidApp으로 주석을 달아 Hilt를 활성화합니다.
애플리케이션 단위에 Hilt파일을 만들어줍니다.
HiltApplication.kt
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
class HiltApplication : Application()
마지막으로 AndroidManifest에서
아래처럼 이름을 변경해주면 기본 세팅 끝!
<application
android:name=".HiltApplication"
두번째로 Hilt를 사용하여 종속성에 주석을 달고 삽입합니다.
class UserViewModel @ViewModelInject constructor(
private val userRepository: UserRepository
) : ViewModel() {
// Use the userRepository instance ...
}
이게 끝입니다!!
Dagger처럼 Module클래스도 Component인터페이스를 만들 필요없이
구성요소의 기본이되는 Application에서 Hilt를 활성화시켜서
의존성을 주입할 클래스에 맞는 @Inject어노테이션을 사용한 constructor안에 종속성 주입된 데이터를 선언합니다.
그리고 이제 종속받은 userRepository 인스턴스를 사용하면 됩니다.
그렇다면 이 예제에서는 @ViewModelInject 어노테이션이 쓰였는데
viewModel에서 사용해서 해당 생성자 어노테이션이 쓰였을까요?
또 어떤 생성자 어노테이션이 있었을까요?
Q4. Hilt에서 constructor annotation(생성자 어노테이션)에 어떤 종류가 있니?
1. @Inject
: Hilt에서 삽입할 생성자, 필드 또는 메서드를 표시하는 데 사용됩니다.
class UserRepository @Inject constructor() { // ... }
class UserViewModel @Inject constructor( private val userRepository: UserRepository ) : ViewModel() { // ... }
UserRepository는 @Inject constructor()에 아무런 인자 없이 생성되었므로 다른 클래스에 삽입될 클래스임을 의미합니다.
그리고 UserViewModel에서 @Inject constructor안에 가져온 userRepository를 의존성 주입받아 인스턴스를 사용합니다.
만약 엑티비티에서 의존성 주입하여 사용한다면 아래와 같이 사용할 수 있다.
@AndroidEntryPoint class MainActivity : AppCompatActivity() { @Inject lateinit var userRepository: UserRepository override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // The dependencies will be injected before onCreate() is called // Use the injected dependencies userRepository.getUserData() } }
@AndroidEntryPoint로 해당 엑티비티에서 의존성 주입을 활성화하여
의존성 주입할 UserRepository를 @Inject 어노테이션하여 의존성 주입한다.
이때는 위와 다르게 UserRepository에 빈 인자를 넣은 생성자를 만들 필요는 없다.
2. @AssistedInject
: 일부 런타임 파라미터(컴파일 타임에 알려지는 것이 아니라 프로그램 실행 중에 결정되고 제공되는 값 또는 인수)가 필요한 클래스에 종속성을 주입하는 방법인 보조 주입에 사용됩니다.
class UserViewModel @AssistedInject constructor( private val userRepository: UserRepository, @Assisted private val userId: String ) : ViewModel() { // ... }
아래처럼 Factory클래스를 만들어서 보조 주입 시킬 수 있다.interface UserViewModelFactory { fun create(userId: String): UserViewModel } // Usage val userViewModel = userViewModelFactory.create(userId)
Square에서 개발한 "Assisted Injection"이라는 별도의 라이브러리의 일부입니다.
보조 주입은 파라미터를 수동으로 주입하여 사용하는데
Dagger 또는 Hilt의 자동 의존성 주입을 함께 사용할 수 있게 하였습니다.
3. @ViewModelInject
: ViewModel 클래스에 종속성을 주입하는 데 사용됩니다.
class UserViewModel @ViewModelInject constructor( private val userRepository: UserRepository ) : ViewModel() { // ... }
이번 예제에서는 "1번의 @Inject어노테이션"처럼
UserRepository클래스에 빈 인자의 @Inject constructor()를 사용하지 않습니다.
그 이유는 "@ViewModelInject 어노테이션"에서는
자동으로 인자안의 의존성 주입될 클래스에 의존성주입 처리를 해놓기 때문입니다.
그렇다면 여기서 고민인 점은 Hilt로 개발한다면 Dagger2는 사용하지 않아도 되는 것일까요?
Q5. Hilt는 Dagger2보다 모든 관점에서 성능이 더 우수하니?
Hilt와 Dagger2는 모두 매우 효율적이고 성능이 뛰어난 종속성 주입 프레임워크로 설계되었습니다.
실제로 Hilt와 Dagger 2의 성능 차이는 대부분의 애플리케이션에서 무시해도 될 정도라는 점은 주목할 가치가 있습니다.
따라서 성능 고려 사항에만 초점을 맞추는 것보다 프로젝트 요구 사항, 팀 전문 지식 및 선호하는 개발 스타일에 더 잘 맞는 프레임워크를 선택하는 것이 좋습니다.
성능의 관점에서는 두 라이브러리에서 차이점은 찾기 힘들것으로 보입니다.
하지만 제 개인적인 견해로는 Dagger는 2012년부터 오랫동안 많은 사람들에게 사용되어서
많은 자료들과 사례들이 있어서 다양하게 최적화를 해야하는 상황에서는 Dagger2를 사용하는게 우수해보입니다.
그리고 이미 Dagger2를 사용하고 있다면 Hilt로 바꿔야할 이유가 없는 것 같습니다.
하지만 새로운 DI라이브러리를 채택해내가야하는 관점에서 보면
러닝커브가 낮아서 사용하기 쉬우면서 간단하면서
Dagger의 기능들을 거의 다 구현 가능한
Hilt를 사용하는것이 더 유리해보입니다.
즉, "기존에 Dagger2쓰시는 분은 Dagger쓰고 새로 의존성 주입을 도입하면 Hilt를 쓰자!" 라는 저의 생각!
하지만 Koin과 비교해보면 어떻게 될까요?
다음시간에
'안드로이드 연구소 > 의존성주입' 카테고리의 다른 글
[Flow vs RxJava] RxJava(ReactiveX에 대하여) (0) | 2023.06.08 |
---|---|
[여기가 DI 설명 제일 잘함] Koin vs Hilt (0) | 2023.05.17 |
[여기가 DI 설명 제일 잘함] 의존성 주입 하는 법 & DI 라이브러리들 (0) | 2023.05.16 |