DI 와 Dagger2 [1]

kimji1
7 min readMay 8, 2020

--

클래스가 필요한 객체를 얻는 3가지 방법

  1. 클래스가 필요한 의존성을 가짐 (ex) val engine = Engine()
  2. 다른 곳에서 가져옴 (ex) Context — getSystemService()
  3. 파라미터로 전달받음 (ex) Car(engine: Engine) { … }
  • 3번이 Dependency Injection으로, 아래의 예제를 보자.
/* 
아래는 1번의 경우, 직접적인 의존성을 가진다.
*/
class Car {
private val engine = Engine()
fun start() {
engine.start()
}
}
fun main(args: Array) {
val car = Car()
car.start()
}
/*
아래는 3번의 경우, 파라미터를 통해 전달받으며 이렇게 툴을 사용하지 않고
직접 DI를 하는 것을 manual DI라고 한다.
*/
class Car(private val engine:Engine) {
fun start() {
engine.start()
}
}
fun main(args: Array) {
val engine = Engine()
val car = Car(engine)
car.start()
}
/*
Field Injection 또는 Setter Injection이라고 한다. 특정 Android 프레임워크 클래스(Activity 또는 Fragment와 같은)는 시스템에서 초기화되기 때문에 constructor injection이 불가능하다. field injection은 의존성이 class으 생성 이후에
이루어진다.
*/
class Car {
lateinit var engine: Engine

fun start() {
engine.start()
}
}
fun main(args: Array) {
val car = Car()
car.engine = Engine()
car.start()
}

Manual Dependency Injection

Benefits of using Dagger

  • DI를 위해 직접 작성했던 코드를 Dagger에서 생성
  • 내부적으로 Dagger는 클래스의 객체를 제공할 방법을 찾는 객체 그래프를 생성
  • 그래프 안의 모든 클래스를 위해, Dagger 는 내부적으로 객체를 얻기 위한 타입으로 사용되는 factory-type 클래스를 생성
  • 빌드 시간에 Dagger는 모든 객체의 의존성이 안정적이게 하고 때문에 Runtime Exception이 발생하지 않으며, 의존성 사이클이 없도록 해서 무한 루프에 빠지지 않도록 함

@Inject

  • Dagger가 어떻게 이 객체의 인스턴스를 생성할지 알게 함
class UserRepository @Inject constuctor(
private val localDataSource: UserLocalDataSource,
private val remoteDataSource: UserRemoteDataSource) {...}
  • @Inject annotated constructor로 UserRepository 인스턴스를 어떻게 생성할지 알려줌
  • 의존성이 무엇인지 알려줌: UserLocalDataSource, UserRemoteDataSource
  • Dagger는 UserRepository의 인스턴스를 어떻게 생성해야할 지에 대해서 알지만, 이 의존성을 어떻게 생성해야할 지에 대해서는 모름. 만약 다른 클래스에도 annotation을 붙이면 Dagger는 그 클래스들의 생성 방법을 알게 됨
class UserLocalDataSource @Inject constructor() {...}
class UserRemoteDataSource @Inject constructor() {...}

Dagger component

  • Dagger가 언제, 어디서 의존성이 필요한지 알 수 있도록 의존성 그래프를 만드는 것을 돕기 위해 @Component 어노테이션이 붙은 interface를 만들어야 함
  • Dagger는 manual dependency injection 으로 했을 때 처럼 container를 만듦
  • @Component interface는 필요한 클래스의 인스턴스를 반환하는 함수를 정의. @Component 어노텡테이션은 Dagger가 모든 의존성이 안전하게 요구되도록 container를 정의할 수 있도록 함. 이것을 Dagger Container라고 하고, 각자의 의존성과 어떻게 제공할지에 대해 알고있는 객체로 되어있는 그래프를 가짐.
@Component
interface ApplicationGraph {
fun repository(): UserRepository
}

@Component: Dagger가 의존성 그래프를 생성하도록 함

  • Dagger가 적용된 프로젝트를 빌드하면 Dagger는 ApplicationGraph interface의 구현을 생성: DaggerApplicationGraph

Scoping

  • 같은 인스턴스의 의존성이 항상 사용된다는 것을 의미하며 그 타입은 제공되어야 함
  • 유일한 인스턴스를 얻기 위해 @Singletone annotation을 사용할 수 있음
  • @Component interface 위의 @Singletone scope annotation은 Dagger에 그래프 내 생명주기와 항상 같은 인스턴스를 제공해야 한다는 것을 알려줌

@Singletone

Summary

  • @Inject는 constructor injection에서 Dagger graph 에 타입을 추가할 때 언제든 사용할 수 있다.
  • @Inject를 사용할 수 없는 경우는 아래 두 가지 경우다

— @Binds를 사사용하여 Dagger에 interface 구현이 되어야한다고 말할 때

— @Provides를 사용하여 Dagger에 어떻게 프로젝트에 없는 class를 제공할지 표현할 때

  • component에는 module을 한번만 지정할 수 있음
  • scope annotation은 생명주기에 의존하며, 생명주기 종류는 아래와 같음

— @ApplicationScope, @LoggedUserScope, @ActivityScope

--

--

kimji1
kimji1

No responses yet