본문 바로가기

안드로이드

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

개요

해당 게시글은 구글 코드랩을 번역한 게시글입니다.

 

Refactoring the Login Flow

객체를 어떤 수명 주기의 범위로 Scoping하는 것 외에도 subcomponent를 만드는 것은 앱의 다른 부분을 각각 캡슐화하는 좋은 방법이다.

 

앱의 흐름에 따라 Dagger의 하위 그래프(Subcomponent를 생성하는 일)을 통해 앱을 구조화하면 메모리 및 시작 시간 측면에서 더 성능이 뛰어나고 확장성이 좋은 앱을 만드는데 도움이 된다. 읽기 쉽고 모듈화에 좋은 앱을 개발하기 위해서는 앱에 있는 모든 객체를 제공하는 거대한 단일 컴포넌트를 만드는 것을 피해야 한다. 

 

Login flow를 위해 다른 subcomponent를 생성하여 리팩토링해보자.

 

login 패키지에 LoginComponent.kt를 생성하고 RegistrationComponent에서 작업한 것 처럼 @Subcomponent로 지정해준다.

 

login/LoginComponent.kt

// LoginComponent가 사용하는 Scope
// @ActivityScope가 처리된 클래스들은 헤당 Component의 고유 인스턴스를 갖는다
@ActivityScope
// Dagger subcomponent로 정의
@Subcomponent
interface LoginComponent {

    // LoginComponent의 객체를 생성하기 위한 팩토리
    @Subcomponent.Factory
    interface Factory {
        fun create(): LoginComponent
    }

    // LoginComponent에 의해 주입되는 클래스
    fun inject(activity: LoginActivity)
}

LoginComponent를 AcitivityScope로 지정하여 LoginActivity와 같은 생명 주기를 갖는다. (현재 여타 다른 클래스들은 ActivityScope로 지정되지 않아 크게 유의미해 보이지 않음)

 

LoginViewModel의 객체를 생성하는 방법을 알려주기 위해 생성자에 @Inject 처리를 해준다.

 

LoginViewModel.kt

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

이 경우에서 loginViewModel은 다른 클래스들에서 재사용될 필요가 없기 때문에 @ActivityScope로 처리할 필요가 없다.

 

또한 AppSubcomponent 모듈의 AppComponent의 subcomponent로 새로 추가해야 한다.

AppSubcomponents.kt

@Module(subcomponents = [RegistrationComponent::class, LoginComponent::class])
class AppSubcomponents

LoginActivity가 LoginComponent에 접근 가능하게 하기 위해 AppComponent에 LoginComponent 팩토리를 추가해야한다.

AppComponent.kt

@Singleton
@Component(modules = [StorageModule::class, AppSubcomponents::class])
interface AppComponent {
    ...
    // 그래프에서 검색될 수 있는 타입
    fun registrationComponent(): RegistrationComponent.Factory
    fun loginComponent(): LoginComponent.Factory

    // 이 컴포넌트를 통해 의존성 주입을 받을 수 있는 클래스
    fun inject(activity: MainActivity)
}

LoginActivity에 LoginComponent 객체를 생성하기 위한 준비가 모두 끝났다.

  1. LoginViewModel을 @Inject로 처리하여 대거로 부터 주입받고 private 접근자를 제거한다.
  2. LoginComponent 팩토리를 검색하여 LoginComponent 객체를 생성한 뒤 inject 메소드를 호출하여 액티비티를 전달한다.
  3. LoginViewModel의 기존 초기화 라인을 제거한다.
class LoginActivity : AppCompatActivity() {

    // 1) 대거로 부터 LoginViewModel 주입받기
    @Inject
    lateinit var loginViewModel: LoginViewModel

    ...

    override fun onCreate(savedInstanceState: Bundle?) {

        // 2) 앱 그래프에서 LoginComponent 팩토리를 통해 검색하여 생성하고 
        // 이 액티비티를 해당 컴포넌트로 주입
        (application as MyApplication).appComponent.loginComponent().create().inject(this)

        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_login)

        // 3) 아래 기존 초기화 라인 제거
        loginViewModel = LoginViewModel((application as MyApplication).userManager)
        ...
    }
}

 

이제 앱을 실행시키면 Login Flow가 정상적으로 동작할 것이다.

현재 그래프의 상태