-
Android) DI - Dagger2 시작하기, Kotlin + Dagger2 예제Android 2020. 8. 13. 13:08
의존성 주입의 중요성이 커지고 있습니다.
하지만 Dagger 같은 경우에는 러닝커브가 상당히 높은 편입니다.
그래서 차선책으로 Koin을 사용해서 의존성 주입을 하고 있었는데,
Google에서 Dagger를 권장하면서 밀어주기도 하고, Dagger의 이점이 훨씬 많은 것 같아 예제를 다뤄보려고 합니다.
DI (Dependency Injection) 이란?
- Di(dependency injection = 의존성 주입) framework
- 외부에서 의존 객체를 생성하여 넘겨주는 것을 의미합니다.
- Di는 결국 의존성이 있는 객체의 제어를 외부 framework로 올리면서 Ioc(Inversion of control = 제어의 역전)개념을 구현한 것입니다.
의존성?
비유하자면 우리가 어디 먼 곳을 갈때, 자동차나 대중교통을 타고 간다. 즉 이때 이런 탈 것들을 의존해서 갑니다.
프로그래밍에서는 함수에 필요한 클래스 또는 참조변수나 객체에 의존하는 것 = 의존성 이라고 볼 수 있습니다.
주입?
그럼 의존성은 어떻게 만드는 것일까요?
직접 생성하는 방법이 있지만, 이럴 경우 객체 생성의 번거로움, 많은 테스트 케이스 고려, 많은 boiler plate code등의 문제점이 발생합니다.
그래서 이러한 문제점을 줄여주기 위해 DI (의존성 주입) 을 사용합니다.
의존성 주입
즉 의존성 주입 이란
- 소스코드에서 두 모듈 간의 연결의 의미합니다.
- 일반적으로 둘 중 하나가 다른 하나를 어떤 용도를 위해 사용합니다.
- 객체지향언어에서는 두 클래스 간의 관계라고도 말합니다.
- 클래스간의 의존성이 줄어들면 유지보수시 매우 편합니다.
이렇게 하여 클래스간의 결합도(coupling)를 낮추어 의존성을 줄일 수 있습니다.
의존성을 줄여야 하는 이유는 예를들어 A,B,C .. 클래스 들이 있는데 B나 C 클래스가 내부에서 A 클래스를 참조하는 경우 A 클래스가 변화가 있을시에 A 클래스를 참조하는 B, C 클래스도 변화가 생기게 됩니다. 즉 상위 클래스를 참조하고 있는 하위 클래스들은 의존성을 가지고 있어 상위클래스가 변화가 생길시에 의존성을 갖고 있는 모든 클래스에 변화가 발생하게 됩니다.
ex) A 클래스가 b 클래스를 의존하는 경우 B Object를 A클래스가 직접 생성하지 않고 외부에서 생성하여 넘겨주면 의존성을 주입했다고 볼 수 있습니다.
DI 장/단점
장점
- Unit Test가 용이해진다.
- 코드의 재사용성을 높여준다.
- 리팩토링이 수월하다.
- 객체 간의 의존성(종속성)을 줄이거나 없앨 수 있다.
- 객체 간의 결합도(coupling)를 낮추면서 유연한 코드를 작성할 수 있다.
- 보일러 플레이트 코드 감소.
- 스코프를 이용한 객체 관리.
즉 유지보수를 편하게하고 테스팅을 수월하게 할 수 있습니다.
단점
- 학습곡선이 상당히 높다.
- 어렵다.
즉 어렵다.. 이 한마디로 충분한 것 같습니다.
서론이 길었고 이제 DI Framework 인 Dagger를 알아보겠습니다.
Dagger의 필수 개념
-
Inject
-
Component
-
Subcomponent
-
Module
-
Scope
@Inject
- 의존성 주입을 요청합니다.
- @Inject 로 주입을 요청하면 연결된 Component가 Module로부터 객체를 생성하여 넘겨줍니다.
@Component
- 연결된 Module을 이용하여 의존성 객체를 생성합니다.
- @Inject로 요청받은 인스턴스에 생성한 객체를 주입. 의존성을 요청받고 주입하는 Dagger의 주된 역할을 수행합니다.
@SubComponent
- Component는 계층관계를 만들 수 있습니다. @Subcomponent는 Inner Class 방식의 하위계층 Component입니다.
- Subcomponent는 Dagger의 중요한 컨셉인 그래프를 형성합니다.
- @Inject로 주입을 요청받으면 @Subcomponent에서 먼저 의존성을 검색하고, 없으면 부모로 올라가면서 검색합니다.
@Module
- Component에 연결되어 의존성 객체를 생성. 생성 후 Scope에 따라 관리 합니다.
@Scope
- 생성된 객체의 Lifecycle 범위.
- 안드로이드에서는 주로 PerActivity, PerFragment 등으로 화면의 생명주기와 맞추어 사용. Module에서 Scope을 보고 객체를 관리합니다.
위의 5가지 개념에 따라 Dagger가 의존성을 주입하는 Flow는 아래와 같습니다.
Inejct로 의존성을 요청하면 Subcomponent에서부터 Module을 검색하고, Scope에 따라 객체를 가져와 주입하는 방식입니다.
Dagger의 다른 Annotation
- Singleton
- Reusable
- CanReleaseReferences
- Qualifier
@Singleton
- 항상 동일한 인스턴스를 재사용.
- 해당 컴포넌트의 객체가 살아있으면 계속 같은 객체를 사용한다는 의미입니다.
@Reusable
- 새로운 인스턴스를 생성할 수 있지만, 이미 인스턴스가 존재한다면 재사용.
- 다른 scope와 달리 @Component와 관련 없습니다.
- 항상 동일한 인스턴스를 이용해야 하는 환경이 아니라면 메모리 할당 측면에서 @Singleton보다 유용.
@CanReleaseReferences
- GC(Garbage Collection)이 발생했을 때, 해당 어노테이션을 사용하여 해당 인스턴스를 해제할 수 있습니다.
@Qualifier
- 타입만으로 의존성을 식별하기 어려운 경우, 해당 어노테이션을 사용.
Dagger의 Scope는 해당 클래스의 단일 인스턴스가 존재하는 범위를 말합니다.
Dagger의 컨셉은 그래프 이다. 즉 위의 필수 개념을 통해 어떻게 주입하고 Scope에 따라 module과 component를 짤 것인지를 잘 생각해야 합니다..
아주 어렵습니다.Dagger2 + Kotlin 예제
app 수준의 build.gradle에 설정부터 해주겠습니다.
AppComponent.Kt
AndroidInject를 만들기 위해 인터페이스를 하나 생성해줍니다.
App.Kt
App이란 클래스를 만들어 DaggerApplication()을 상속받습는다.
AndroidManifest.xml
방금만든 App을 manifest에 싱글톤이라 보고 후 외부에서 생성되는 객체를 주입하는 하는 인젝터 생성을 끝내겠습니다.
이제 객체의 생성을 담당할 Module을 알아보겠습니다.
MainActivityModule.Kt
후에 액티비티나 프래그먼트의 객체를 편하게 관리하기 위해 Scope를 만들겠습니다.
ActivityScope.Kt
FragmentScope.Kt
Named.Kt
ActivityModule.Kt
AppModule.Kt
MainFragmentModule.Kt
이제 컴포넌트와 모듈을 생성하였으므로 ui에 적용해보겠습니다.
MainActivity.Kt
MainFragment.Kt
이렇게 되면 실행 시간에 onCreate()에서 객체가 주입되는 것을 확인 할 수 있습니다.
간혹 안드로이드 인젝터를 생성할때 컴포넌트 앞에 Dagger 명이 붙는게 참조가 안될 경우가 발생하는데,
build.gradle(app) 에 아래와 같이 해보는 방법과 build -> clean project -> rebuild project를 해보는 방법이 있습니다.
혹시 위의 예제에 전체 소스코드를 보고 싶으신 분은 아래에서 확인 부탁드립니다.
반응형'Android' 카테고리의 다른 글
Android) Fragment에서 Activity의 finish() 메소드 구현하기 (0) 2020.09.06 Android) android.dataBinding.enabled' is obsolete and has been replaced with 'android.buildFeatures.dataBinding (0) 2020.08.20 Android) RxJava 메모리 누수 방지 - Disposable, CompositeDisposable 비교 (0) 2020.08.04 Android) LiveData, MutableLiveData 특징과 차이점 (0) 2020.08.04 Android) AAC - ViewModel 예제 (0) 2020.08.04