DI (Dependency Injection)
DI(dependency injection)는 프로그래밍에 널리 사용되는 기술이며 Android 개발에 매우 적합하다.
DI 원칙을 따르면 좋은 앱 아키텍처의 토대를 마련할 수 있다.
종속성 주입을 구현하면 다음과 같은 이점이 있다.
- 코드 재사용성
- 리팩토링이 용이
- 테스트 용이성
먼저, 앱 수준 gradle에 플러그인을 추가
plugins {
id 'com.android.application'
id 'kotlin-android'
// dagger를 위한 plugin
id 'kotlin-android-extensions'
// 어노테이션을 통해 컴파일 타임에 코드 생성을 도와줌
id 'kotlin-kapt'
}
그 후, dagger 라이브러리를 추가
dependencies{
...
// dagger dependency
def dagger_version = "2.40"
implementation "com.google.dagger:dagger:$dagger_version"
kapt "com.google.dagger:dagger-compiler:$dagger_version"
...
}
Inject annotation
애플리케이션의 그래프(구조도)를 생성하기 위해, dagger는 그래프의 클래스에 대한 인스턴스를 생성하는 방법을 알아야 한다. 그 중 한 방법이 class 단에 @Inject를 사용하는 것이다. @Inject는 생성자에 붙는데, 생성자 매개변수가 해당 유형의 종속성이 된다.
Componet annotation
dagger 프로젝트에서 프로젝트의 종속 그래프를 생성하고, 관리하며 종석성을 얻을 수 있기 원한다.
이를 위해서 인터페이스를 생성하고 @Component 어노테이션을 붙일수 있다.
@Component는 Dagger가 갖고있는 매개변수를 충족하는데 필요한 모든 종속성을 포함시킨 코드를 생성한다.
해당 인터페이스에서 dagger에 주입을 요청할 수 있다.
Module & Binds & BindsInstance
@Module
dagger는 인터페이스를 직접 인스턴스화 할 수 없기 때문에 제공 방법을 달리 해야한다.
Component와 비슷하게 Module도 dagger에게 특정 타입의 인스턴스를 알려준다.
단, 모듈 내에서는 @Provides와 @Binds 어노테이션에 의해 종속성이 결정된다.
@Binds
interface를 dagger에 알리기 위해 @Binds 메소드 사용
@Binds를 붙인 메소드는 추상 함수로 만들어 줘야함
리턴타입은 반환하고 싶은 인터페이스 타입을 지정
매개변수로는 그 인터페이스를 구체화하는 클래스 등을 주입
이렇게 하면 dagger는 interface를 알게됨
@BindsInstance
@BindsInstance는 dagger 종속성 그래프 외부의 객체를 가져와 사용할 수 있음 (ex. android context)
// Factory 패턴
@Component.Factory
interface Factory {
// @BindsInstance는 그래프 외부의 객체를 가져와 사용할 수 있음
// @BindsInstance에 의해 context를 가진 Appcomponent가 반환됨
fun create(@BindsInstance context: Context): AppComponent
}
@BindsInstance에 의해 context를 가진 Appcomponent가 반환됨
Singleton annotation
다양한 이유로 같은 인스턴스를 제공하는 것을 원할 때도 있다. (같은 인스턴스를 공유하는 다른 타입의 객체를 만들고 싶을떄.. 매번 인스턴스를 만들때마다 비용이 클 때 등..)
이러한 특별한 인스턴스를 같는 것이 있다. (Scopes)
이건 Component 생명주기의 스코프라 불리곤 한다.
이는 구성 요소의 수명 주기에 따라 유형 범위를 지정 "이라고도 한다.
타입을 구성 요소로 범위 지정한다는 것은 타입을 제공해야 할 때마다 해당 타입의 동일한 인스턴스가 사용된다는 의미이다.
이것은 한 번 만들어지면 생명주기 동안 계속 유지되는 특성이 있다. (새로 만들어지지 않음)
dagger에서는 @Singleton 어노테이션을 통해 위와 같은 기능을 수행할 수 있다.
SubComponent annotation
dagger 사용을 위해 fragment에도 inject를 적용하였다.
여기서 같은 ViewModel이 여러 군데에서 사용될 수 있는데, 각 액티비티(프래그먼트) 마다 뷰모델 인스턴스가 공유되지 않는 문제가 있다.
여기서 RegistrationViewModel을 @Singleton으로 만드는 방법을 생각할 수 있다.
하지만, 이 방법은 잠정적인 문제들을 지니고있다.
1. flow가 끝나고 메모리에서 해제되길 바라는데 Singleton으로 계속 메모리에 남아있게 된다.
2. 다른 register flow 마다 같은 뷰모델이 아닌 다른 뷰모델 인스턴스를 이용하고 싶다. (회원 등록, 삭제 등에서 필요한 데이터가 다르기 때문)
결국 ViewModel을 재사용하길 원하지만 @Singleton을 사용하지 않으면서, 다른 인스턴스를 사용하길 원한다.
이를 위해, dagger에서는 flow에 따른 새로운 component를 만들고 ViewModel의 범위를 해당 component로 새로 지정할 수 있다.
SubComponent를 이용하면 된다!
@SubComponent
SubComponent는 상위 Component 요소의 그래프를 상속하고 확장하는 component이다.
따라서, 상위 요소에 있는 모든 인스턴스들은 하위 요소에도 제공된다.
이런 방식으로 하위 요소의 인스턴스는 상위 요소에서 제공하는 인스턴스에 종속성을 갖게된다.
'Android' 카테고리의 다른 글
[Android] Clean Architecture (0) | 2024.03.11 |
---|---|
[Android] Compose (RE) (0) | 2024.03.04 |
[Android] Compose (basic) (1) | 2024.01.15 |
[Android] 인앱 업데이트 (0) | 2023.11.24 |
fragment & lifecycle (0) | 2023.09.15 |