본문 바로가기

안드로이드

[Android] 2. 구글 코드랩 Dagger를 이용한 리팩토링 - Registration Flow

개요

구글 코드랩 문서를 참조한 게시글 입니다.

 

@Inject annotaction

Application Graph를 자동으로 그리게 하기 위해 Dagger는 인스턴스를 어떻게 만들 지 알아야 한다. 한 가지 방법은  클래스의 생성자에 @Inject을 작성하는 것이다. 해당 생성자의 매개변수들은 타입에 맞게 종속성이 된다.

 

RegistrationViewModel.kt

class RegistrationViewModel @Inject constructor(val userManager: UserManager) {
    ...
}

위 과정을 통해

  • 어떻게 RegistrationViewModel을 만들어야 할 지 알게 된다. 즉, @Inject는 대거에게 해당 타입의 객체를 어떻게 제공하는지 알려준다. 
  • 또한 Dagger는 RegistrationViewModel이 UserManager에 종속한다는 것을 알게 된다.

(참고로 위에서 ViewModel은 AAC 컴포넌트의 ViewModel이 아닌 그 역할을 수행하는 클래스이다.)

 

위와 더불어서 대거는 UserManager가 어떻게 만들어지는지 모른다.

 

UserManager.kt

class UserManager @Inject constructor(private val storage: Storage) {
    ...
}

앞선 과정과 비슷하게 @Inject로 처리하여 UserManager가 어떻게 생성되는지 알게 하고, Storage에 종속한다는 내용 또한 알게 된다. 이 다음을 역시 Storage 또한 어떻게 생성되는지 알아야 한다.

 

 

View는 그래프에서 객체를 필요로 한다.

액티비티, 프래그먼트와 같은 안드로이드 프레임워크의 특정 클래스들은 시스템 자체적으로 초기화가 된다. 따라서 대거는 이들을 자동으로 만들어주지 않는다. 특히 액티비티의 경우 모든 초기화 코드가 onCreate에 존재해야한다. 따라서, 이전 처럼 클래스의 생성자에 @Inject를 시도하면 안된다. 대신 필드 인젝션을 사용해야한다.

 

해당 앱에서 RegistrationActivity는 RegistrationViewModel에 의존한다. RegistrationViewModel을 만드는 방법을 Dagger에게 알려주었기에 필드 인젝션으로 이를 주입하고, 직접 생성하는 대신 대거가 이를 제공하도록 기존의 초기화 코드는 삭제한다.

class RegistrationActivity : AppCompatActivity() {

    // @Inject로 처리된 변수들은 대거가 주입하게 된다.
    @Inject
    lateinit var registrationViewModel: RegistrationViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        // 아래 초기화 라인을 삭제하시오
        registrationViewModel = RegistrationViewModel((application as MyApplication).userManager)
    }
}

 

@Inject가 생성자에 붙으면 Dagger에게 해당 클래스의 객체를 제공해야 한다는 것을 알려주고, 클래스 필드에 붙으면 대거에게 해당 유형의 필드를 생성해야 한다고 Dagger에게 알리게 된다.

 

그런데, RegistrationActivity에 주입해야 하는 객체를 어떻게 알려야 할까? 그 방법은 Application Graph를 만든 뒤 이를 통해 객체를 액티비티에 주입해야 한다.

 

 

 

@Component annotation

우리는 Dagger가 해당 프로젝트의 Application Graph를 만들고 이를 관리하고 그래프에서 의존성 주입을 할 수 있기를 원한다. 이를 구현하려면 @Component로 만든 인터페이스를 작성해야한다. Dagger는 매뉴얼한 의존성 주입을 수행한 것 처럼 컨테이너를 생성하게 된다. 

 

@Component 처리 된 인터페이스는 Dagger가 인터페이스에 존재하는 메소드의 매개 변수를 만족하는 의존성 관련 코드를 작성한다. 즉, 해당 인터페이스에서 RegistrationActivity가 주입을 요청한다는 것을 Dagger에게 알릴 수 있다.

 

app/src/main/java/com/example/android/dagger/di/AppComponent.kt

// Dagger Component로 정의
@Component
interface AppComponent {
    // 이 컴포넌트에 의해 주입 될 클래스
    fun inject(activity: RegistrationActivity)
}

위 inject 메소드로 대거에게 RegistrationActivity가 의존성 주입을 요청한다는 사실을 알릴 수 있다. 즉, 해당 RegistrationActivity가 필요로 하는 의존 객체들을 제공해야 한다.

 

따라서, 대거는 RegistrationViewModel을 만들고 내부적으로 다시 이를 만족하기 위해  UserManager를 만들게 된다. 이 재귀적인 과정에서 대거가 컴파일 타임에 특정 종속성을 제공하는 방법을 모르면 에러를 알린다.

 

@Component 인터페이스는 대거가 컴파일 타임에 그래프를 생성하는데 필요한 정보를 제공한다. 해당 인터페이스의 메소드의 매개변수는 삽입을 요청하는 클래스를 의미한다.

 

 

빌드 버튼을 클릭하면 대거가 Application Graph를 그리게 된다. 하지만 해당 빌드는 실패하게 된다. 앞서 말한 듯 의존성을 충족하지 않기 때문이다. UserManager의 Storage 주입 과정에서 Dagger는 Storage를 어떻게 생성해야 하는지 모르기 때문이다. 이 에러의 이름을 Dagger/MissingBinding이라고 한다. 에러를 계속 읽으면 @Provides 없이는 Storage를 제공할 수 없다는 메시지를 표시하게 된다.