Dependency Injection
의존성 주입(Dependency Injection)은 여러 컴포넌트간의 의존성이 강한 안드로이드에서 클래스 간 의존성을 낮춰준다.
객체 생성 시 클래스 간 의존성이 생기게 되는데, 객체의 생성을 클래스 내부에서 하는게 아니라 클래스 외부에서 객체를 생성하여 주입시켜주는 디자인 패턴을 말한다.
즉, 아래와 같이 정리할 수 있다.
의존성 주입은 클래스 외부에서 객체를 생성하여 주입하는 것!
A가 B를 의존한다는 표현은 어떤 의미일까? 추상적인 표현이지만, 토비의 스프링에서는 다음과 같이 정의한다.
의존대상 B가 변하면, 그것이 A에 영향을 미친다.
즉, B의 기능이 추가 또는 변경되거나 형식이 바뀌면 그 영향이 A에 미친다.
예를 들면, Car라는 클래스는 Engine이라는 클래스를 참조하고 있다. 이렇게 클래스를 필요로하는 것을 의존성(dependency)이라고 한다.
특정 클래스가 자신이 의존하고 있는 객체를 얻는 방법은 3가지가 있다.
- Car 클래스 안에서 Engine 인스턴스를 생성하여 초기화한다.
- 다른 곳에서 객체를 가져온다. Android 로 치면 Context, getSystemService() 등에 해당한다.
- 객체를 파라미터로 제공받는다. Car 의 생성자가 Engine 을 파라미터로 받는다.
세 번째 방법이 바로 Dependency Injection 기법 중 하나이다
DI를 사용하지 않을 때
class Car {
private val engine = Engine()
fun start() {
engine.start()
}
}
fun main(args: Array) {
val car = Car()
car.start()
}
이 코드는 아래의 문제가 있다.
- Car와 Engine은 결합(의존성)이 강하다.
- Car는 Engine의 자식 클래스를 사용하기가 쉽지 않아진다.
- Engine을 직접 생성하면 Car를 재사용할 때, Engine을 상속한 Gas나 Electric이라는 클래스를 재사용하기가 쉽지 않다.
- 테스트를 어렵게 만든다. Engine 의 실제 인스턴스를 사용하기 때문에 다양한 시나리오를 고려하지 못한다. (Unit Test 불리)
- Engine 의 생성자가 변경된 경우 Car 클래스에서도 수정이 이루어져야 한다.
DI를 사용 할 때
class Car(private val engine: Engine) {
fun start() {
engine.start()
}
}
fun main(args: Array) {
val engine = Engine()
val car = Car(engine)
car.start()
}
- Car를 재사용할 수 있다. Car 클래스에게 다른 Engine의 구현들을 전달할 수 있다. Engine의 자식 클래스로 Electric을 Car에게 전달할 수 있다. Car는 별다른 수정 없이 새로운 기능을 동작할 수 있다.
- Engine 의 생성자 등 구현이 변경되어도, Car 클래스를 수정하지 않아도 된다.
- Car를 테스트하기 쉽다.FakeEngine이라는 클래스를 작성하는 등 Engine타입을 여러 방면으로 테스트해 볼 수 있다.
DI의 장점
- 외부에서 객체를 생성해서 주입하기 때문에 코드의 재사용성이 높다.
- 테스트에 용이하다.
- 의존성이 낮아지기 때문에 코드 변경에 유연하고 자유롭다.
- 앱 생명주기에 따라 관리되어 적절한 시점에 필요한 객체들이 자동으로 주입된다.
- 보일러 플레이트가 대폭 줄어든다.
Dagger Hilt
인스턴스를 클래스 외부에서 주입하기 위해서는 인스턴스에 대한 생명주기(생성~소멸되기까지)의 관리가 필요하다.
이를 자동으로 관리해주기 위해 Google에서는 2020년 6월에 안드로이드 전용 DI(Dependency injection) 라이브러리인 "Dagger Hilt"를 발표하였다.
Hilt는 기존의 Dagger를 기반으로 하며 Dagger보다 러닝커브가 훨씬 낮고, 초기 DI 환경 구축 비용을 크게 절감(보일러 플레이트 코드 감소)할 수 있으며, 안드로이드 앱에 최적화 된 DI 라이브러리이다.
또한, 최근 Google에서 적극적으로 지원하고 있는 Jetpack과 AAC 라이브러리를 통해서 Hilt 의존성 주입을 정말 간편하게 구현할 수 있도록 지원해주고 있기 때문에 안드로이드 DI 환경 구축에 굉장히 유용한 라이브러리이다.
Adding Dependencies
Hilt를 프로젝트에 적용하기 위한 gradle 셋업이다. (Android Developers Guides 참고)
먼저 hilt-android-gradle-plugin을 project-level의 build.gradle 파일에 추가한다.
buildscript {
...
ext.hilt_version = '2.33-beta'
dependencies {
...
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
}
}
그런 다음, app-level의 build.gradle 파일에 아래 코드를 추가한다.
...
apply plugin: 'kotlin-kapt'
apply plugin: 'dagger.hilt.android.plugin'
android {
...
}
dependencies {
implementation "com.google.dagger:hilt-android:$hilt_version"
kapt "com.google.dagger:hilt-compiler:$hilt_version"
}
이렇게 기본적인 gradle 셋업을 마쳤고, 본격적으로 Hilt를 안드로이드 프로젝트에서 활용하는 방법에 대해 알아보도록 하겠다!
Hilt Application Class
Hilt를 사용하는 모든 앱은 @HiltAndroidApp 어노테이션을 Application Class에 반드시 추가해야한다.
이 어노테이션으로 의존성 주입의 시작점을 지정한다. (앱이 살아 있는 동안 Dependency 제공)
Hilt는 Application 생명 주기를 따르며 컴파일 단계 시 DI에 필요한 구성요소들을 초기화 하기 위함 입니다.
@HiltAndroidApp
class MainApplication : Application()
AndroidEntryPoint
Hilt에서는 객체를 주입할 Android 클래스에 @AndroidEntryPoint 어노테이션을 추가하는 것만으로도 자동으로 생명주기에 따라 적절한 시점에 Hilt 요소로 인스턴스화 되어 처리한다.
@AndroidEntryPoint 어노테이션을 붙여주면 Hilt가 해당 클래스에 Dependency를 제공해 줄 수 있는 Component를 생성해준다.
Hilt가 지원하는 Android클래스는 다음과 같다.
- Activity
- Fragment
- View
- Service
- BroadcastReceiver
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject lateinit var viewModel: MainViewModel
}
@Inject 어노테이션을 사용하여 의존성을 주입 받으려는 변수에 객체를 주입 할 수 있다.
즉 @Inject 어노테이션이 붙은 변수는 의존성을 주입받는 포인터를 선언한다 는 의미이다.
Injection
Hilt는 Dependency Graph를 만들어서 필요한 곳에 Dependency를 제공해주는 라이브러리이기 때문에, 어떤 곳에서 해당 Dependency가 필요하다고 Annotation이 붙어있다면, 해당하는 객체를 어떻게 생성하는지 Hilt가 알고있어야 한다.
Hilt의 "@Inject" Annotation은 Dependency Graph를 이어주는 역할을 한다.
Hilt가 Dependency를 제공해서 생성할 객체의 클래스에도 붙이고, Dependency를 주입받을 객체에도 붙여준다.
Injection은 Install된 Component로부터 Dependency를 주입하거나 받을 수 있다는 것을 의미하는 것이다.
개인적으로 종이컵 전화기 같은 것이라고 생각이 된다.
의존성 생성 방법
1. @Inject constructor
2. Hilt 모듈 (@Module, @Provides, @Binds)
@Inject
Injection은 크게 Fileld Injection과 Constructor Injection으로 나누어 볼 수 있다.
1) Field Injection
아래 코드는 ItemAdapter가 Hilt에 의해 주입될 수 있는 객체라는걸 알려주기 위해 생성자에 @Inject 어노테이션을 붙였다.
class ItemAdapter @Inject constructor(){
...
}
위 클래스는 Component 들에서 사용이 가능하다. (@AndroidEntryPoint가 붙은 Activity 등)
아래코드와 같이 의존성 주입을 받아서 사용한 변수에 @Inject 어노테이션을 붙여서 사용한다. 그러면 객체화 하지 않아도 Hilt로부터 주입된 객체를 사용할 수 있다. (단 private 변수는 주입 받을 수 없다.)
위에서 선언한 방식이 생성자 injeciton이라면, 이러한 방식은 Field Injectio이다.
@AndroidEntryPoint
class MainActivity: AppCompatActivity(){
@Inject lateinit var adapter: ItemAdapter
...
}
다만, 주의할 점은 Hilt에 의해서 주인받은 변수 객체는 private할 수 없다는 것이다.
만약 private field로 선언하면, compilation error를 보게 된다.
Hilt는 빌드 타임시에 Android 클래스의 Dagger 컴포넌트들을 생성해 준다.
그럼, Dagger는 생성된 Dependency 그래프를 따라가게되고, 클래스들과 그들이 필요로하는 Dependency들을 주입해준다.
2) Constructor Injection
의존성이 있는 두가지 클래스를 보도록 할 것이다.
ATypeClass와 BTypeClass가 있다고 가정해보자.
BTypeClass는 @Inject 어노테이션을 붙여서 주입될 객체라고 표시가 되었고
ATypeClass도 마찬가지지만, 생성자에서 BTypeClass의 객체를 주입받고 있다.
Constructor Injection이 사용되는 것이다.
Constructor Injection을 사용함으로서, 생성시에 어떤 클래스의 객체가 필요한지 Hlit가 알 수 있고, 개발자도 알 수 있게 된다.
class ATypeClass
@Inject
constructor(private val bTypeClass: BTypeClass) {
fun doBtypeTest(): String {
return bTypeClass.test()
}
}
class BTypeClass
@Inject
constructor() {
fun test(): String {
return "test is done"
}
}
위 두 클래스를 이용해서, 아래와 같이 field injection으로 객체를 주입 받아, 사용할 수 있다.
@AndroidEntryPoint
class TestActivity : AppCompatActivity() {
@Inject
lateinit var aType: ATypeClass
overide fun onCreate(savedInstanceState: Bundle?){
aType.doBtypeTest()
}
}
그런데, Construction Injection을 사용할 수 없는 경우들이 존재하는데, 아래에서 보도록 하자.
Constructor Injection 예외
1. Interface를 Constructor Injection에 사용하는 것은 금지되어 있음
아래와 같이 interface가 있다고 가정해보자.
interface AInterface {
fun showString(): String
}
위의 Ainterface를 implement하는 classA와 classB가 존재한다고 가정해보자
이들은 각각 AInterface를 implement할 것이다.
문제는 interface를 inject하는 Constructor Injection이 사용되었기 때문에 정상적으로 inject되지 않고 에러가 나게 되는 것이다.
interface나 interface를 implement하는 객체를 inject할 수 없는 것인데,
그이유는 Hilt가 interface가 implement된 타입의 객체를 어떻게 생성해야 할지 알 수 없기 때문이다.
class ClassA
@Inject
constructor(private val bTypeVal: AInterface){
fun doTestA(): String {
return bTypeVal.doTestB()
}
}
class ClassB
@Inject
constructor(): AInterface {
override fun showString(): String {
return "get Go!"
}
}
interface AInterface {
fun showString(): String
}
2. 외부 라이브러리 클래스의 객체를 inject하는 것은 금지됨
retrofit같은 라이브러리의 객체를 Constructor injection하는 것은 금지되어 있다.
자신이 만든 클래스가 아닌 곳에 @Inject를 annotation에 추가할 수도 없기 때문에, 마음대로 사용하는 것은 불가능한 것이다.
결국에는 Hilt가 이 객체를 어떻게 만들어야 할지 모르기 때문에 어찌보면 당연하다고 할 수 있는 부분이다.
그럼 위의 두가지 케이스를 해결하기 위해서는 어떤 방법을 써야할까?
아래에서 알아보도록 하자.
Hilt Modules
위에서 했던 것과 같이 @Inject Annotation을 붙이는 방법말고,
Module을 이용해서 Hilt에게 원하는 Dependency를 생성하는 방법을 알려줄 수 있다.
특히, Interface나 외부라이브러리의 객체처럼, Hilt가 어떻게 객체를 생성해야할지 모르는 경우에는 꼭 필요한 방법이다.
Module을 사용하는 방법은 두가지가 있는데, Provides Annotation과 Binds Annotation을 이용하는 것이다.
개인적으로 Provides Annotation을 사용하는 것이 사용하기가 편한 것 같다.
보통은 임의의 Module 클래스를 생성한 후, 이곳에 Module클래스들을 생성해서 사용하게 된다.
Provides
@Provides는 Hilt에 의존성을 주입하려는 인스턴스 클래스가 외부 라이브러리(Retrofit, OkHttp, Room, DataStore 등)를 사용하는 경우 혹은 빌더 패턴으로 인스턴스를 생성하는 경우에 사용한다.
먼저, 위에서 보았던 AInterface를 implement한 ClassA와 ClassB가 있다고 하겠다.
ClassB의 생성자에 String타입의 cDependency 인자가 들어간다고 가정해보자.
class ClassA
@Inject
constructor(private val bTypeVal: AInterface){
fun doTestA(): String {
return bTypeVal.doTestB()
}
}
class ClassB
@Inject
constructor(private val cDependency: String): AInterface {
override fun doTestB(): String {
return "get Go ${cDependency}!"
}
}
interface AInterface {
fun showString(): String
}
Module 클래스를 생성할 때 가장 먼저 할 것은 @Module Annotation을 붙여주는 것이다.
그래야 Hilt가 여기가 Module이 있는 곳임을 알 수 있을 것이다.
다음으로는 @Installn Annotation을 붙여준다. 예를들어, @Installn(ActivityComponent::class)는 해당 모듈이 activity에서 사용가능하다고 선언하다는 의미이다. 이는 컴포넌트에 따라 지정할 수 있는데 자세한 내용은 아래 Component hierarchy를 참조하자.
Module Class내부에 Provides 함수들을 넣어주어야 하는데, Annotation으로 커뮤니케이션하는 Hilt에게, 아래와 같이 @Provides Annotation을 붙여준다.
위에서는 두가지 의존성을 제공해야 하는 것으로 보인다.
하나는 cDependency라는 String 객체, 다른 하나는 AInterface 타입의 객체이다.
둘 다 어떻게 만드는지 Hilt는 모르므로, @Provides Annotation을 붙여서 각각 알려줘야한다.
@Module
@InstallIn(ActivityComponent::class)
class AModule {
@Provides
fun provideCString(): String {
return "c String"
}
@Provides
fun testProvides(cString: String): AInterface {
return ClassB(cString)
}
}
Provide 방식을 이용하면 Room DB, Regrofit이나 Gson과 같은 외부라이브러리 객체의 경우도 Dependency를 제공할 수 있다.
아래 코드는 Room DB, Repository 클래스의 객체 생성방법, Retrofit 등의 객체를 생성하는 방법들을 Hilt에게 알려주고 있다. 참고로 Application Context를 제공해주어야 할 때도 있는데, 아래와 같이 인자에 application을 넣어주면 이것마저도 Hilt가 알아서 넣어준다.
@Module
@InstallIn(SingletonComponent::class)
object TestModule {
@Singleton
@Provides
fun provideReceiptDatabase(application: Application): ReceiptDatabase {
return Room.databaseBuilder(
application,
ReceiptDatabase::class.java,
"receipt_db"
)
.fallbackToDestructiveMigration() //should be removed when going out as a product
.build()
}
@Singleton
@Provides
fun provideReceiptRepository(receiptDatabase: ReceiptDatabase): ReceiptRepository {
return ReceiptRepositoryImpl(receiptDatabase.dao)
}
@Singleton
@Provides
fun provideAService(): AService {
return Retrofit.Builder()
.baseUrl("https://a.com")
.build().create(AService::class.java)
}
@Singleton
@Provides
fun provideGson(): Gson {
return Gson()
}
}
같은 타입의 객체에 대한 Dependency
위에서 사용한 String 타입 2, 3개가 사용될 경우 Hilt에게 어떻게 말해주어야 할까?
같은 String 타입의 경우 어떤 Type의 객체를 Inject할지 Hilt가 알 수 없을 것이다.
아래와 같이 "Qualifer"를 통해 구분을 해주면 된다.
@Qualifer @Retention(AnnotationRetention.BINARY) Annotation을 붙여서 구분할 Identifer라고 Hilt에게 알려주고, 키워드로 annotation class를 붙인다음 이름을 정해준다.
아래에서는 TestType1과 TestType2를 사용하였다.
이제 Hilt는 이 Qualifer를 따라서 Dependency를 주입해 줄 것이다.
@Module
@InstallIn(SingletonComponent::class)
object TestModule {
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class TestType1
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class TestType2
@TestType1
@Provides
fun testProvides1(): AInterface {
return ClassA()
}
@TestType2
@Provides
fun testProvides2(): AInterface {
return ClassB()
}
}
이제 위에서 만든 Qualifer를 사용해보자
생성자의 인자들 앞에 보면 @TestType1과 @TestType2를 붙여주고 있는데, 각각 다른 Identifer를 붙여줌으로서, Hilt는 이제 어떤 Dependency를 어디에 연결해서 주입해야 되는지를 알게 된다.
class ClassA
@Inject
constructor(
@TestType1 private val bTypeVal1: AInterface,
@TestType2 private val bTypeVal2: AInterface,
){
fun doTest1(): String {
return bTypeVal.doTestB()
}
fun doTest2(): String {
return bTypeVal2.doTestC()
}
}
class ClassB
@Inject
constructor(): AInterface {
override fun doTestB(): String {
return "First Implementation"
}
}
class ClassC
@Inject
constructor(p): AInterface {
override fun doTestC(): String {
return "Second implementation"
I
}
interface AInterface {
fun showString(): String
}
Binds
이번에는 Binds방식으로로는 어떻게 하는지 보도록 해보자.
참고로 Binds방식은 외부라이브러리에는 사용할 수 없다.
보통 interface 타입의 객체를 어떻게 만드는지 Hilt에게 알려주기 위한 용도로 사용하는 것이다.
Provides와 마찬가지로 @Moudle Annotation을 붙여주고 abstract calss를 만든다음 abstract함수를 정의해주면 된다.
@Provide때와 마찬가지로 @Binds Annotation을 붙여준다.
아래와 같이 해주면, 이제 Hilt가 AInterface타입의 객체는 어떻게 생성하여야 되는지 알게되는 것이다.
AInterface타입이지만, classB에서 객체를 생성해야 한다고 알려주는 것.
@Module
@InstallIn(ActivityComponent::class)
abstract class AModule {
@ActivityScoped
@Binds
abstract fun bindInterfaceDependency(testClassB: ClassB): AInterface
}
Component hierarchy
현재 @Installln(component)에 사용되는 컴포넌트들은 다음과 같이 제공하고 있다.
Hilt 내부적으로 컴포넌트들의 생명주기를 자동으로 관리해주기 때문에 개발자가 DI 환경을 구축하는데 수고를 최소화 해주고 있다!
다음은 Hilt에서 제공하는 Component hierachy
Hilt component | Injector for | Created at | Destroyed at | Scope | |
SingletonComponent | Application | Application#onCreate() | Application#onDestroy() | @Singleton | |
ActivityRetainedComponent | 해당 없음 | Activity#onCreate() | Activity#onDestroy() | @ActivityRetainedScoped | |
ViewModelComponent | ViewModel | ViewModel created | ViewModel destroyed | @ViewModelScoped | |
ActivityComponent | Activity | Activity#onCreate() | Activity#onDestroy() | @ActivityScoped | |
FragmentComponent | Fragment | Fragment#onAttach() | Fragment#onDestroy() | @FragmentScoped | |
ViewComponent | View | View#super() | View destroyed | @ViewScoped | |
ViewWithFragmentComponent | @WithFragmentBindings 가 붙은 View | View#super() | View destroyed | @ViewScoped | |
ServiceComponent | Service | Service#onCreate() | Service#onDestroy() | @ServiceScoped |
각 컴포넌트들은 생성 시점부터 ~ 파괴되기 전까지 injection이 가능하고, 각 컴포넌트마다 자신만의 생명주기를 갖는다.
- SingletonComponent: Application의 생명주기를 갖는다. Application이 생성되는 시점에 함께 생성되고, 파괴되는 시점에 함께 파괴된다.
- ActivityRetainedComponent: Activity의 생명주기를 갖는다. 다만 Activity의 Configuration Change(디바이스 화면전환 등...) 시에는 파괴되지 않고 유지된다.
- ViewModelComponent: Jetpack ViewModel의 생명주기를 갖는다.
- ActivityComponent: Activity의 생명주기를 갖는다. Activity가 생성되는 시점에 함께 생성되고, 파괴되는 시점에 함께 파괴된다.
- FragmentComponent: Fragment의 생명주기를 갖는다. Fragment가 Activity에 붙는 시점에 함께 생성되고, 파괴되는 시점에 함께 파괴된다.
- ViewComponent: View의 생명주기를 갖는다. View가 생성되는 시점에 함께 생성되고, 파괴되는 시점에 함께 파괴된다.
- ViewWithFragmnentComponent: Fragment의 View 생명주기를 갖는다. View가 생성되는 시점에 함께 생성되고, 파괴되는 시점에 함께 파괴된다.
- ServiceComponent: Service의 생명주기를 갖는다. Service가 생성되는 시점에 함께 생성되고, 파괴되는 시점에 함께 파괴된다.
Hilt와 ViewModel
Hilt는 기본적으로 Jetpack ViewModel에 대한 의존성 주입을 제공해주기 때문에, Jetpack의 ViewModel을 사용하는 경우 굉장히 쉽고 유리하게 구현할 수 있다.
ViewModel Injection을 위해 app-level build.gradle 파일에 의존성을 추가해준다.
dependencies {
...
implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha01'
// When using Kotlin.
kapt 'androidx.hilt:hilt-compiler:1.0.0-alpha01'
// When using Java.
annotationProcessor 'androidx.hilt:hilt-compiler:1.0.0-alpha01'
implementation 'androidx.fragment:fragment-ktx:1.3.0'
}
Jetpack ViewModel은 Android SDK 내부적으로 ViewModel에 대한 Lifecycle을 관리하고 있다.
ViewModel에서 @HiltViewModel 어노테이션과 @Inject 어노테이션을 사용하면 간단하게 ViewModel 의존성 주입을 활성화 할 수 있다.
@HiltViewModel
class MainViewModel @Inject constructor(
private val repository: MainRepository,
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
...
}
다음은 위에서 생성된 MainViewModel을 @AndroidEntryPoint가 붙은 Activity에서 사용하는 예시이다.
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private val viewModel: MainViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
}
}
ApplicationContext가 필요할 때
객체 생성시 ApplicationContext가 필요할 때가 있다.
예를 들어서 아래와 같이 @provide를 이용해서, interface를 생성하는 방법을 Hilt에 알려주려고 할 때,
@ApplicationContext를 Annotation으로 사용해주면 Hilt가 알아서 생성해주므로, 바로 사용이 가능하다.
@Module
@InstallIn(SingletonComponent::class)
class TestModule {
@Singleton
@Provides
fun provideTest(@ApplicationContext appContext: Context): Test {
return Test(appContext)
}
}
참고자료
https://0391kjy.tistory.com/55
Dagger Hilt로 안드로이드 의존성 주입하기
Dependency Injection 의존성 주입(Dependency Injection)은 여러 컴포넌트간의 의존성이 강한 안드로이드에서 클래스 간 의존성을 낮춰줍니다. 객체 생성 시 클래스 간 의존성이 생기게 되는데 이때, 객체
0391kjy.tistory.com
https://developer88.tistory.com/349
HILT 에 대해서 정리해 보겠습니다. # DI Dependency Injection
오늘은 Hilt를 이용한 Dependency Injection에 대해서 정리해 보도록 하겠습니다. Dependency Injection에 대해 경험이 없으신 분들이라면, 아래 글을 통해서 기본적인 개념에 대해서 이해해 보시면 도움이
developer88.tistory.com
https://brunch.co.kr/@purpledev/44
안드로이드 Hilt 딥 다이브
DI와 Dagger 그리고 Hilt 에 대해서 | 안녕하세요 저희는 서비스 개발팀에서 안드로이드 개발을 하고있는 두루와 스티븐입니다. 오늘은 Dagger와 Hilt에 대해 깊게 조사해본 것을 소개하겠습니다. Hilt
brunch.co.kr
https://velog.io/@dlwpdlf147/Android-%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%A3%BC%EC%9E%85%EA%B3%BC-Hilt
[Android] 의존성 주입과 Hilt
DI와 이를 구현한 Jetpack Library: Hilt
velog.io
https://develop-oj.tistory.com/69
[안드로이드] Hilt에서 @Binds와 @Provides의 차이
Hilt 라이브러리를 이용해 의존성을 주입하는 경우에 Module에서 abstract class에는 @Binds 어노테이션이, object에는 @Provides 키워드가 붙게 되는데 어떤 이유에서인지 알아보겠습니다. @Binds? @Provides? 결
develop-oj.tistory.com
'Android > Hilt' 카테고리의 다른 글
[Hilt] Hilt vs Koin (0) | 2024.11.11 |
---|---|
[Hilt] Hilt의 Componet와 Scope (2) | 2024.11.11 |
[Hilt] 프로젝트에 의존성 주입(DI) 적용해보기 - Hilt + Retrofit (0) | 2023.06.27 |