본문 바로가기
안드로이드 공부 & 앱

[안드로이드/Kotlin] 의존성 주입 및 Hilt 프레임워크 사용 과정

by 문톰 2022. 5. 17.

 

 

 

Dagger Hilt를 알아보기 전에 의존성 주입을 하는이유에 대해서 알아보겠습니다.

1.의존성 주입(dependency injection)

-"의존성 주입"은 하나의 객체가 다른 객체에게 의존성을 제공하는 것입니다.

-그러면 의존성이 필요한 이유를 보면서 의존성을 이해해봅시다.

 

1-1)의존성 주입을 하는이유

의존성 주입이 안된 코드

-위의 코드처럼 객체를 필드안에서 new 연산자를 생성해서 객체를 생성할 때 문제가

발생합니다.

-만약 Pocket 클래스의 코드가 수정되어서 객체 생성시 입력값이 필요하면 Draemon

클래스도 수정을 해줘야 하는 문제가 발생합니다.

-이것을 객체간의 의존성이 발생한다고 얘기합니다.

 

1-2)의존성 주입을 통한 객체 생성 방식

의존성 주입이 된 코드

-위의 그림의 외부에서 Pocket 객체를 가져와서 주입하면 Pocket클래스의 변경이

생겨도 Draemon클래스는 수정을 하지 않아도 되는 장점이 있습니다. 

 

1-3)의존성 주입의 장점

-코드의 가독성을 높여준다.

-Unit Test가 쉬워진다.

-코드의 재활용성을 높인다.

-객체 간의 의존성을 직접 설정하여 줄이거나 없앨 수 있다.

-객체 간의 결합도를 낮추면서 유연하게 만들 수 있다.

 

2.의존성 주입 프레임워크

1)Koin

1-1)장점

-별도의 어노테이션을 활용하지 않으므로 상대적으로 가볍습니다.

-Daager에 비해 상대적으로 학습이 쉽습니다.

-ViewModel 주입을 쉽게할 수 있는 별도의 라이브러리 제공

 

1-2)단점

-Dagger에 비해 런타임시 오버헤드가 있습니다.

-프로젝트의 규모가 커질수록 유지보수하기가 점점 힘들어집니다.

-DSL을 사용해 런타임에서 의존성을 주입함으로 런타임 중에 Eroor가 발생할 수 있다.

 

2)Dagger2

2-1)장점

-Koin에 비해 대규모 프로젝트의 경우 더욱 유연하게 코드 모듈을 관리할 수 있음.

-컴파일 환경에서 의존성을 주입하기 때문에 빌드가 완료된 시점에선 어느정도

 안정성을 보장함.

 

2-2)단점

-어노테이션, Module, Component간의 역할 관계, Scope등 여러가지 알아야 할 개념이

있어서 상대적으로 학습하는데 시간이 더 걸림

-환경 세팅 작업이 필요함으로 간단한 프로젝트에는 오히려 번거로움 작업이 될 수 있음.

 

 

3.Dagger Hilt

-Dagger2를 기반으로 만들어졌으며 기존의 Daagger2의 단점인 환경세팅과 높은

러닝커브를 줄이고 Daager2의 장점들을 모아서 만든 DI 프레임워크

-Hilt를 더 깊게 이해하려면 Dagger2에 대한 공부가 필요합니다.

 

1)Hilt 사용법

1-1)@HiltAndroidApp

-Hilt를 사용하는 모든 앱은 @HiltAndroidApp으로 주석이 지정된 어플리케이션

클래스를 포함해야 합니다.

-Hilt 관련 컴포넌트, 모듈 등 모든 코드 생성을 시작하는 어노테이션 입니다.

-Hilt를 사용하는 앱은 반드시 @HiltAndroidApp을 가진 Application 클래스를

Manifest App에 포함시켜야합니다.

-HiltAndroidApp은 Application 객체의 수명 주기에 연결된 앱의 최상단 부모 컨포넌트입니다.

-어플리케이션 클래스를 생성하고 @HiltAndroidApp 어노테이션을 붙여야합니다.

-@HIltAndroidApp 어노테이션에 대해서 간단하게 설명하겠습니다.

 

 1-1)@AndroidEntryPoint

-의존성 주입을 받을 엑티비티와 프래그먼트에는 반드시 @AndroidEntryPoint

어노테이션을 등록해야합니다.

1-2)@HiltViewModel

-의존성 주입을 받는 ViewModel에 @HiltViewModel 어노테이션을 등록해야 합니다.

-@Inject Constructor를 통해 Module에 정의한 의존성 객체를 받아올 수 있습니다.

 

1-3)사용할 Module 작성

-위의 그림처럼 사용할 모듈을 @Module, @InstallIn을 사용해 작성합니다.

-인터페이스를 전달할 때 @Binds를 사용하고 Class객체는 @Provides를 사용해 주입합니다.

 

2)Module 작성하는 방법

-Module을 작성하는 방법을 이해하려면 Componenthierachy를 알아야 합니다.

2-1)Component hierachy

그림1

-기존의 Daager2와 달리 안드로이드 환경에서 표준적으로 사용하는 Component들을

Hilt에서는 기본적으로 제공합니다.

-Dagger2에서 개발자가 직접적으로 작성해야 하는 초기 Component 세팅 작업을

기본적으로 제공합니다. 

 

각각의 구성요소는 위와 같은 생성위치와 제거 위치를 나타내고 있습니다.

 

 

예시1) SingletonComponent

-위의 그림처럼 Module의 @InstallIn 어노테이션을 사용해  '그림1' Component들을

정의할 수 있습니다.

-InsatllIn안에 SingletonComponent를 정의하면 Activity, Fragment, Service,

ViewModel등 어디서나 위의 그림에 정의한 providesTestClass() 메서드로

TestClass 객체를 생성할 수 있습니다.

-또한 SingletonComponent를 사용했기 때문에 사용하는 클래스에 정의된 메서드에는

모두 @singleton 어노테이션을 붙여주어야 합니다.

-메서드 위에 @Singleton 어노테이션을 붙임으로써 객체를 재생성하지 않고 처음 한번

생성 후에 생성한 객체를 재사용합니다.

 

예시2) ActivityComponent

 -만약 InstallIn의 Component를 ActivityComponent로 정의한다면 클래스 내부에

정의된 메서드에는 @ActivityScoped 어노테이션을 붙여야합니다.

 -정의한 모듈은 Activity내에서만 사용할 수 있습니다.

 

ActiivtyRetainedComponent

-이 컴포넌트의 경우 ViewModel의 생명주기 처럼 화면회전에 의해서 소멸되지 않고

엑티비티가 완전히 소멸되어야만 소멸되는 Component입니다.

 

 

-@Provides 어노테이션의 역할에 대해서 궁금하실텐데요 @Provides 어노테이션을

이해하기 위해서 먼저 의존성 주입방식에 대해서 알아보겠습니다.

 

 

3)의존성 주입 방식

-의존성 주입에는 두 가지 방법이 있습니다. 

1-1)생성자 주입

-위의 코드처럼 객체의 생성자에 주입하는 방식을 생성자 주입방식이라고 얘기합니다.

-@Inject constructor 키워드를 입력해서 정의해놓은 모듈에 정의해놓은 객체를 주입

받을 수 있습니다.

 

1-2)필드 주입

-Activity나 Fragment의 경우 안드로이드 시스템에서 객체를 인스턴스화 하기 때문에

@inject Constructor를 사용해 의존성을 주입받을 수 없습니다.

-이러한 경우 주로 필드 주입을 하게됩니다.

-@Inject 키워드를 사용해서 Filed 주입을 할 수 있습니다.

 

4)@Provides, @Bind를 사용하는 이유

-위의 그림처럼 Module을 통해 의존성 객체를 전달받지 않고 전달하는

클래스에서 @Inject Constructor를 입력하고 전달받는 Fragment나

Activity에 @Inject를 사용해 객체를 전달받을 수 있습니다.

 

4-1)외부라이브러리 및 인터페이스의 경우

-그러나 Room이나 Retrofit처럼 생성하려는 클래스가 외부라이브러리에서

제공받는 경우에는 @Inject Constructor를 붙일 수 없기 때문에 

Module 클래스를 만들어서 @Binds와 @Provides로

전달할 객체를 작성합니다.

 

 

4-1)@Provides

-Room이나 Retrofit 같은 외부라이브러리에서 제공되는 클래스 객체를 전달받을 때 사용합니다.

-위의 코드는 build()(builder 패턴)을 사용해서 외부에서 객체를 생성해서 가져오기

때문에 @Inject Constructor를 통한 의존성 주입이 불가능해서

Module을 생성해서 @Provides를 사용한 것입니다.

 

 

Object 클래스로 정의하면 좋은 점

-@Provides만 포함되는 모듈인 경우에 object로 생성했을 시 Provider는 최적화된

코드를 제공하며 inline으로 된 코드를 제공합니다.

 

 

 

4-2)@Binds

-@Inject Constructor를 사용할 수 없는 Interface 객체에 대한 모듈을 작성할 때 사용합니다.

-모듈에 정의된 메서드의 입력값에는 repository의 구현체가 들어가고 반환값으론

Repository객체가 반환됩니다.

 

 

 

 

참고

https://hungseong.tistory.com/29

 

[Android, Kotlin] Hilt에서 @Binds와 @Provides의 차이점

Android에서 * DI (의존성 주입)를 도와주기 위한 라이브러리인 Hilt를 학습하여 사용 중이다. * DI : https://hungseong.tistory.com/10 DI(Dependency Injection), 의존성 주입 Dependency 의존성 함수에 필요한..

hungseong.tistory.com

https://hanyeop.tistory.com/220

 

[Android] Dagger Hilt 사용하여 의존성 주입(DI) 하기

0️⃣ 의존성 주입(DI) 이란? 먼저, 의존성이란 A 클래스가 자체적인 B 클래스를 구성하는 것을 말한다. 구글의 예시를 통해 알아보자. 그림과 같이 Car라는 클래스가 Engine 라는 클래스를 가져다 쓰

hanyeop.tistory.com

https://wonsohana.wordpress.com/2021/05/15/di-dependency-injection-%EB%8A%94-%EC%99%9C-%EC%8D%A8%EC%95%BC%ED%95%A0%EA%B9%8C-2-android-%EC%97%90%EC%84%9C%EC%9D%98-di/

 

DI (Dependency Injection) 는 왜 써야할까? (2) – Android 에서의 DI

1편에서는 DI 의 개념에 대해서 배워봤다면 2편에서는 Android 에서 DI 를 사용하는 방법에 대해서 알아보고자 한다. 늘 그렇듯 문서는 에버노트 링크가 훨씬 잘 되어 있다… DI 는 왜 써야할까?(1) 

wonsohana.wordpress.com

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

 

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

blog.banksalad.com

https://velog.io/@sysout-achieve/Android-DI-Framework-%EC%84%A0%ED%83%9D%EC%A7%80Dagger2-Koin-Hilt

 

[Android] DI Framework 선택지(Dagger2, Koin, Hilt)

대세 DI Framework에 대해 고민한 내용을 공유합니다.

velog.io

https://congruous-caboc-e66.notion.site/2-Hilt-Dependency-Injection-220411-450bd447e13b470cbca1eca14a81f61f

 

2.Hilt Dependency Injection (220411 복습)

-Hilt는 프로젝트의 모든 Android 클래스에 컨테이너를 제공

congruous-caboc-e66.notion.site

 

댓글