๊ฐœ๋ฐœ/Android

[Android] Dependency Injection Part 2. ์ง์ ‘ ์˜์กด์„ฑ ์ฃผ์ž…ํ•˜๊ธฐ

๋„๋ฆฌ ๐ŸŸ 2021. 4. 9. 00:17

์ž‘์„ฑ์ผ: 2020.01.16


์ด ๊ธ€์€ Android Developer ๊ฐ€์ด๋“œ ๋‚ด์šฉ์„ ํ† ๋Œ€๋กœ ์ž‘์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ์ œ ์ฝ”๋“œ๋Š” Kotlin๋งŒ ๊ฐ€์ ธ์™”์œผ๋ฉฐ, Java ์ฝ”๋“œ๋Š” ์›๋ฌธ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์ง์ ‘ ์˜์กด์„ฑ ์ฃผ์ž…ํ•˜๊ธฐ

Android์—์„œ ๊ถŒ์žฅํ•˜๋Š” ์•ฑ ์•„ํ‚คํ…์ฒ˜๋Š” ๊ด€์‹ฌ์‚ฌ์˜ ๋ถ„๋ฆฌ๋ฅผ ๊ฐ•์กฐํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด์„œ๋Š” ์ž‘์€ ํด๋ž˜์Šค๋“ค๋กœ ๋”์šฑ ์ชผ๊ฐ  ํ›„, ์‹คํ–‰์„ ์œ„ํ•ด ๊ฐ ๋””ํŽœ๋˜์‹œ๋ฅผ ๋ชจ๋‘ ์—ฐ๊ฒฐํ•ด์ฃผ๋Š” ๊ณผ์ •์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

MVVM ์•„ํ‚คํ…์ฒ˜๋‚˜ Repository ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ, Anemic Repository๋ฅผ ๊ผญ ์ฝ์–ด๋ณด์‹œ๊ธธ ์ถ”์ฒœํ•ฉ๋‹ˆ๋‹ค!

ํด๋ž˜์Šค ๊ฐ„์˜ ์˜์กด ๊ด€๊ณ„๋Š” ๊ทธ๋ž˜ํ”„๋กœ ๋‚˜ํƒ€๋‚ผ ์ˆ˜ ์žˆ๊ณ , ๊ฐ ํด๋ž˜์Šค๋Š” ์ž์‹ ์ด ์˜์กดํ•˜๋Š” ํด๋ž˜์Šค์™€ ์—ฐ๊ฒฐ๋ฉ๋‹ˆ๋‹ค. ๋ชจ๋“  ํด๋ž˜์Šค์™€ ์˜์กด๊ด€๊ณ„๋ฅผ ๊ทธ๋ฆฌ๋ฉด ๊ณง ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ทธ๋ž˜ํ”„๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆผ1์—์„œ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ทธ๋ž˜ํ”„์˜ ํ˜•ํƒœ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํด๋ž˜์Šค A(ViewModel)์ด ํด๋ž˜์Šค B(Repository)์— ์˜์กดํ•  ๋•Œ, A์—์„œ B๋กœ ๊ฐ€๋Š” ํ™”์‚ดํ‘œ๋กœ ์˜์กด๊ด€๊ณ„๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.

 

DI๋Š” ์ด๋Ÿฐ ์—ฐ๊ฒฐ์„ ํŽธ๋ฆฌํ•˜๊ฒŒ ํ•ด์ฃผ๊ณ , ํ…Œ์ŠคํŠธ ์‹œ ๊ตฌํ˜„์ฒด๋ฅผ ์‰ฝ๊ฒŒ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ค๋‹ˆ๋‹ค. ํ•œ Repository์— ์˜์กดํ•˜๋Š” ViewModel์„ ํ…Œ์ŠคํŠธํ•˜๋Š” ๊ฒฝ์šฐ, Repository์˜ ๋‹ค๋ฅธ ๊ตฌํ˜„์ฒด๋ฅผ ์ „๋‹ฌํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

์ง์ ‘ ์˜์กด์„ฑ ์ฃผ์ž…์˜ ๊ธฐ๋ณธ

์ด ํŒŒํŠธ์—์„œ๋Š” ์‹ค์ œ Android ์•ฑ ํ”Œ๋กœ์šฐ์— ์˜์กด์„ฑ ์ฃผ์ž…์„ ์–ด๋–ป๊ฒŒ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š”์ง€์— ๋Œ€ํ•ด ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. ์•ฑ์— ์˜์กด์„ฑ ์ฃผ์ž… ์ ์šฉ์„ ์–ด๋””๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด์•ผ ํ• ์ง€๋ฅผ ๋‚˜ํƒ€๋‚ด๊ณ , ๋‚˜์•„๊ฐ€ Dagger๊ฐ€ ์ž๋™์œผ๋กœ ์–ด๋–ค ๋ถ€๋ถ„๋“ค์„ ์ƒ์„ฑํ•ด์ฃผ๋Š”์ง€๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. Dagger์— ๋Œ€ํ•œ ์ž์„ธํ•œ ์„ค๋ช…์€ Part3. Dagger์—์„œ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. (์ฐธ๊ณ : ๊ณต์‹ ์‚ฌ์ดํŠธ ๊ฐ€์ด๋“œ)

 

ํ•œ ํ”Œ๋กœ์šฐ๋Š” ํ•œ ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ํ™”๋ฉด์˜ ๋ฌถ์Œ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜์„ธ์š”. ๋กœ๊ทธ์ธ, ํšŒ์›๊ฐ€์ž…๊ณผ ๊ฐ™์€ ๊ธฐ๋Šฅ๋“ค ๋ชจ๋‘ ํ”Œ๋กœ์šฐ์˜ ์˜ˆ์‹œ๊ฐ€ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์ผ๋ฐ˜์ ์ธ Android ์•ฑ์˜ ๋กœ๊ทธ์ธ ํ”Œ๋กœ์šฐ๋ฅผ ๊ทธ๋ ค๋ณด๋ฉด, LoginActivity๊ฐ€ LoginViewModel์— ์˜์กดํ•˜๊ณ  LoginViewModel์€ UserRepository์— ์˜์กดํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  UserRepository๋Š” UserLocalDataSource์™€ UserRemoteDataSource์— ์˜์กดํ•˜๋ฉฐ, UserRemoteDataSource๋Š” Retrofit ์„œ๋น„์Šค์— ์˜์กดํ•ฉ๋‹ˆ๋‹ค.

 

LoginActivity๊ฐ€ ํ”Œ๋กœ์šฐ์˜ ์‹œ์ž‘์ ์ด๊ณ , ์‚ฌ์šฉ์ž๋Š” Activity๋ฅผ ํ†ตํ•ด ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, LoginActivity๊ฐ€ ๋ชจ๋“  ์˜์กด๊ด€๊ณ„์™€ LoginViewModel์„ ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

์ด ํ”Œ๋กœ์šฐ์˜ Repository์™€ DataSource๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ๊ตฌํ˜„๋˜์–ด ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค.

class UserRepository(
    private val localDataSource: UserLocalDataSource,
    private val remoteDataSource: UserRemoteDataSource
) { ... }

class UserLocalDataSource { ... }
class UserRemoteDataSource(
    private val loginService: LoginRetrofitService
) { ... }

LoginActivity์˜ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

class LoginActivity: Activity() {

    private lateinit var loginViewModel: LoginViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

	// LoginViewModel์˜ ์˜์กด๊ด€๊ณ„๋ฅผ ๋งŒ์กฑ์‹œํ‚ค๊ธฐ ์œ„ํ•ด์„œ๋Š”
	// ์žฌ๊ท€์ ์œผ๋กœ ๋ชจ๋“  ๋””ํŽœ๋˜์‹œ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
	// ๋จผ์ € UserRemoteDataSource์˜ ๋””ํŽœ๋˜์‹œ์ธ retrofit์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
        val retrofit = Retrofit.Builder()
            .baseUrl("https://example.com")
            .build()
            .create(LoginService::class.java)

        // ๋‹ค์Œ์œผ๋กœ๋Š” UserRepository๋ฅผ ์œ„ํ•œ ๋””ํŽœ๋˜์‹œ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
        val remoteDataSource = UserRemoteDataSource(retrofit)
        val localDataSource = UserLocalDataSource()

        // ์ด์ œ LoginViewModel์—์„œ ํ•„์š”ํ•œ UserRepository ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
        val userRepository = UserRepository(localDataSource, remoteDataSource)

        // ๋“œ๋””์–ด LoginViewModel ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ!
        loginViewModel = LoginViewModel(userRepository)
    }
}

์ด ๋ฐฉ๋ฒ•์œผ๋กœ๋Š” ์•„๋ž˜ ์ด์Šˆ๊ฐ€ ์ƒ๊ธธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  1. ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ ์ฝ”๋“œ๊ฐ€ ๋„ˆ๋ฌด ๋งŽ์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์ฝ”๋“œ์—์„œ LoginViewModel ์ธ์Šคํ„ด์Šค๋ฅผ ๋˜ ์ƒ์„ฑํ•ด์•ผ ํ•œ๋‹ค๋ฉด, ์ฝ”๋“œ ์ค‘๋ณต์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
  2. ์˜์กด ๊ด€๊ณ„๊ฐ€ ๋ฐ˜๋“œ์‹œ ์ˆœ์„œ๋Œ€๋กœ ์ •์˜๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. LoginViewModel ์ƒ์„ฑ ์ „์— LoginRepository๋ฅผ ๋จผ์ € ์ƒ์„ฑํ•ด์•ผ๋งŒ ํ•ฉ๋‹ˆ๋‹ค.
  3. ๊ฐ์ฒด ์žฌ์‚ฌ์šฉ์ด ์–ด๋ ต์Šต๋‹ˆ๋‹ค. UserRepository๋ฅผ ์—ฌ๋Ÿฌ ๊ธฐ๋Šฅ์—์„œ ์žฌ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์„ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์€ ํ…Œ์ŠคํŠธ์‹œ ๋ชจ๋“  ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๊ฐ€ ๋™์ผํ•œ ์‹ฑ๊ธ€ํ†ค ์ธ์Šคํ„ด์Šค๋ฅผ ์ฐธ์กฐํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ…Œ์ŠคํŠธ๊ฐ€ ๋” ์–ด๋ ค์›Œ์ง‘๋‹ˆ๋‹ค.

ํ•˜๋‚˜์˜ ์ปจํ…Œ์ด๋„ˆ๋กœ ์˜์กด ๊ด€๊ณ„ ๊ด€๋ฆฌํ•˜๊ธฐ

๊ฐ์ฒด ์žฌ์‚ฌ์šฉ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด, ๋””ํŽœ๋˜์‹œ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ด ์ปจํ…Œ์ด๋„ˆ๋ฅผ ํ†ตํ•ด ํ•„์š”ํ•œ ์˜์กด ๊ด€๊ณ„๋ฅผ ์–ป์Šต๋‹ˆ๋‹ค. ์ปจํ…Œ์ด๋„ˆ๋ฅผ ํ†ตํ•ด ์–ป๋Š” ์ธ์Šคํ„ด์Šค๋Š” ๋ชจ๋‘ public ์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, UserRepository ์ธ์Šคํ„ด์Šค๋งŒ ํ•„์š”ํ•˜๋‹ค๋ฉด UserRepository ์™ธ์— ๋‚˜๋จธ์ง€ ๋””ํŽœ๋˜์‹œ๋Š” private์œผ๋กœ ํ•ด๋†จ๋‹ค๊ฐ€ ํ•„์š”ํ•  ๋•Œ public์œผ๋กœ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.

// Container of objects shared across the whole app
class AppContainer {

    // ์ปจํ…Œ์ด๋„ˆ ์™ธ๋ถ€์— userRepository๋ฅผ ๊ณต๊ฐœํ•˜๊ธฐ ์œ„ํ•ด ๋จผ์ € ํ•„์š”ํ•œ ๋””ํŽœ๋˜์‹œ๋ฅผ ์ƒ์„ฑ
    private val retrofit = Retrofit.Builder()
                            .baseUrl("https://example.com")
                            .build()
                            .create(LoginService::class.java)

    private val remoteDataSource = UserRemoteDataSource(retrofit)
    private val localDataSource = UserLocalDataSource()

    // userRepository ๋Š” public
    val userRepository = UserRepository(localDataSource, remoteDataSource)
}

์ด๋Ÿฐ ๋””ํŽœ๋˜์‹œ๋“ค์€ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‚ด์—์„œ ์ „์ฒด์ ์œผ๋กœ ์‚ฌ์šฉ๋˜๊ธฐ ๋•Œ๋ฌธ์—, ๋ชจ๋“  Activity๊ฐ€ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๊ณตํ†ต ๊ณต๊ฐ„-Application ํด๋ž˜์Šค-์— ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ AppContainer ์ธ์Šคํ„ด์Šค๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์ปค์Šคํ…€ Application ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

class MyApplication : Application() {
    val appContainer = AppContainer()
}
AppContainer๋Š” ์‹ฑ๊ธ€ํ†ค ํด๋ž˜์Šค๊ฐ€ ์•„๋‹ˆ๊ณ , Application ํด๋ž˜์Šค์— ์ €์žฅ๋œ ์ธ์Šคํ„ด์Šค๋ฅผ ์•ฑ ๋‚ด๋ถ€์—์„œ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค. Kotlin์—์„œ๋Š” AppContainer๊ฐ€ object๊ฐ€ ์•„๋‹ˆ๋ฉฐ, Java์—์„œ๋Š” Singleton.getInstance() ์ฒ˜๋Ÿผ ์ ‘๊ทผํ•˜๋Š” ์‹ฑ๊ธ€ํ†ค ํด๋ž˜์Šค๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค.

์ด์ œ Application์—์„œ AppContainer ์ธ์Šคํ„ด์Šค๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๊ณ , UserRepository ์ธ์Šคํ„ด์Šค๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

class LoginActivity: Activity() {

    private lateinit var loginViewModel: LoginViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Application์— ์žˆ๋Š” AppContainer๋กœ๋ถ€ํ„ฐ UserRepository ์ธ์Šคํ„ด์Šค๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
        val appContainer = (application as MyApplication).appContainer
        loginViewModel = LoginViewModel(appContainer.userRepository)
    }
}

UserRepository๋ฅผ ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ๋งŒ๋“ค์ง€ ์•Š๊ณ , ๋ชจ๋“  Activity์—์„œ AppContainer ์ธ์Šคํ„ด์Šค์— ์ƒ์„ฑ๋œ UserRepository ์ธ์Šคํ„ด์Šค ํ•˜๋‚˜๋ฅผ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค.

 

LoginViewModel์ด ๋” ๋งŽ์€ ๊ณณ์—์„œ ํ•„์š”ํ•˜๋‹ค๋ฉด, LoginViewModel์„ ์ƒ์„ฑํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ํ•œ ๊ณณ์— ๋ชจ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ƒ์„ฑ ์ฝ”๋“œ๋ฅผ ์ปจํ…Œ์ด๋„ˆ์— ์˜ฎ๊ธฐ๋ฉด ๋ฉ๋‹ˆ๋‹ค. LoginViewModelFactory์˜ ์˜ˆ์ œ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค:

// Definition of a Factory interface with a function to create objects of a type
interface Factory {
    fun create(): T
}

// Factory for LoginViewModel.
class LoginViewModelFactory(private val userRepository: UserRepository) : Factory {
    override fun create(): LoginViewModel {
        return LoginViewModel(userRepository)
    }
}

AppContainer์— LoginViewModelFactory๋ฅผ ๋‘๊ณ , LoginActivity์—์„œ LoginViewModelFactory๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค:

// AppContainer can now provide instances of LoginViewModel with LoginViewModelFactory
class AppContainer {
    ...
    val userRepository = UserRepository(localDataSource, remoteDataSource)

    val loginViewModelFactory = LoginViewModelFactory(userRepository)
}

class LoginActivity: Activity() {

    private lateinit var loginViewModel: LoginViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Gets LoginViewModelFactory from the application instance of AppContainer
        // to create a new LoginViewModel instance
        val appContainer = (application as MyApplication).appContainer
        loginViewModel = appContainer.loginViewModelFactory.create()
    }
}

์ด ๋ฐฉ๋ฒ•์ด ์•ž์—์„œ ์„ค๋ช…ํ•œ ๊ฒƒ๋ณด๋‹ค ๋‚ซ์ง€๋งŒ, ์—ฌ์ „ํžˆ ๋ช‡ ๊ฐ€์ง€ ๋ฌธ์ œ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค:

  1. AppContainer๋ฅผ ์ง์ ‘ ๊ด€๋ฆฌํ•ด์•ผ ํ•˜๊ณ , ๋ชจ๋“  ์˜์กด ๊ด€๊ณ„์— ํ•ด๋‹นํ•˜๋Š” ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ ์ฝ”๋“œ๋ฅผ ์ง์ ‘ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  2. ์—ฌ์ „ํžˆ ๋งŽ์€ ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ ์ฝ”๋“œ๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ๊ฐ์ฒด ์žฌ์‚ฌ์šฉ์ด ํ•„์š”ํ•˜๋Š๋ƒ์— ๋”ฐ๋ผ Factory๋‚˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ง์ ‘ ์จ์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ”Œ๋กœ์šฐ ๊ด€์ ์—์„œ ์˜์กด ๊ด€๊ณ„ ๊ด€๋ฆฌํ•˜๊ธฐ

ํ”„๋กœ์ ํŠธ์— ๊ธฐ๋Šฅ์ด ์ถ”๊ฐ€๋ ์ˆ˜๋ก AppContainer๋„ ๋ณต์žกํ•ด์ง‘๋‹ˆ๋‹ค. ์•ฑ์ด ์ปค์ง€๋ฉด ๋‹ค๋ฅธ ๊ธฐ๋Šฅ ํ”Œ๋กœ์šฐ๊ฐ€ ์ƒ๊ธธ ์ˆ˜ ์žˆ๊ณ , ๊ทธ ๊ฒฝ์šฐ ์•„๋ž˜์˜ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  1. ์–ด๋–ค ๊ฐ์ฒด๋Š” ํ”Œ๋กœ์šฐ์˜ scope์—์„œ๋งŒ ์œ ํšจํ•˜๊ฒŒ ๋งŒ๋“ค ํ•„์š”๊ฐ€ ์ƒ๊น๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด (๋กœ๊ทธ์ธ ํ”Œ๋กœ์šฐ์—์„œ๋งŒ ์“ฐ์ด๋Š” username๊ณผ password๋ฅผ ํฌํ•จํ•˜๋Š”) LoginUserData๋ฅผ ์ƒ์„ฑํ–ˆ์„ ๋•Œ, ์ด์ „ ๋กœ๊ทธ์ธ ํ”Œ๋กœ์šฐ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ๊ณ„์† ์œ ์ง€๋˜์ง€ ์•Š์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋งค๋ฒˆ ์ƒˆ๋กœ์šด ํ”Œ๋กœ์šฐ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ์ธ์Šคํ„ด์Šค๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ํ•ด๊ฒฐ์„ ์œ„ํ•ด AppContainer ์•ˆ์— ๋‹ค์Œ ์˜ˆ์ œ ์ฝ”๋“œ์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋Š” FlowContainer๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
  2. ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ”Œ๋กœ์šฐ๋ฅผ ์ตœ์ ํ™”ํ•˜๊ธฐ๋„ ์–ด๋ ต์Šต๋‹ˆ๋‹ค. ํ”Œ๋กœ์šฐ๋ฅผ ๋ณด๊ณ  ํ•„์š”์—†๋Š” ์ธ์Šคํ„ด์Šค๋ฅผ ์ฐพ์•„ ์‚ญ์ œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ํ•œ Activity (LoginActivity)์™€ Fragment๋“ค(LoginUsernameFragment, LoginPasswordFragment)๋กœ ๊ตฌ์„ฑ๋œ ๋กœ๊ทธ์ธ ํ”Œ๋กœ์šฐ๊ฐ€ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ด…์‹œ๋‹ค. ํ™”๋ฉด์˜ ์š”๊ตฌ์‚ฌํ•ญ์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค:

  1. ๋กœ๊ทธ์ธ ํ”Œ๋กœ์šฐ๊ฐ€ ๋๋‚˜๊ธฐ ์ „์—๋Š” ๊ฐ™์€ LoginUserData ์ธ์Šคํ„ด์Šค์— ์ ‘๊ทผ
  2. ํ”Œ๋กœ์šฐ๊ฐ€ ๋‹ค์‹œ ์‹œ์ž‘๋˜๋ฉด LoginUserData ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒˆ๋กœ ์ƒ์„ฑ

๋กœ๊ทธ์ธ ํ”Œ๋กœ์šฐ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋งŒ๋“ค์–ด ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์ปจํ…Œ์ด๋„ˆ๋Š” ๋กœ๊ทธ์ธ ํ”Œ๋กœ์šฐ๊ฐ€ ์‹œ์ž‘๋˜๋ฉด ์ƒ์„ฑ๋˜๊ณ , ํ”Œ๋กœ์šฐ๊ฐ€ ๋๋‚˜๋ฉด ๋ฉ”๋ชจ๋ฆฌ์—์„œ ์ง€์›Œ์ง‘๋‹ˆ๋‹ค.

 

์˜ˆ์ œ ์ฝ”๋“œ์— LoginContainer๋ฅผ ์ถ”๊ฐ€ํ•ด๋ด…์‹œ๋‹ค. ์—ฌ๋Ÿฌ LoginContainer๊ฐ€ ํ•„์š”ํ•  ์ˆ˜ ์žˆ์œผ๋‹ˆ, ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ๋งŒ๋“ค์ง€ ์•Š๊ณ  AppContainer์— ์˜์กด ๊ด€๊ณ„์ธ ํด๋ž˜์Šค๋กœ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

class LoginContainer(val userRepository: UserRepository) {

    val loginData = LoginUserData()

    val loginViewModelFactory = LoginViewModelFactory(userRepository)
}

// AppContainer contains LoginContainer now
class AppContainer {
    ...
    val userRepository = UserRepository(localDataSource, remoteDataSource)

    // LoginContainer will be null when the user is NOT in the login flow
    var loginContainer: LoginContainer? = null
}

ํŠน์ • ํ”Œ๋กœ์šฐ์— ํ•œ์ •๋œ ์ปจํ…Œ์ด๋„ˆ๋Š” ์ธ์Šคํ„ด์Šค๋ฅผ ์–ธ์ œ ์ƒ์„ฑํ•˜๊ณ  ์ œ๊ฑฐํ• ๊ฑด์ง€์— ๋Œ€ํ•œ ์ •์˜๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๋กœ๊ทธ์ธ ํ”Œ๋กœ์šฐ๋Š” Activity (LoginActivity)์— ํฌํ•จ๋˜๊ธฐ ๋•Œ๋ฌธ์—, Activity๊ฐ€ ์ปจํ…Œ์ด๋„ˆ์˜ ๋ผ์ดํ”„์‚ฌ์ดํด์„ ๊ด€๋ฆฌํ•˜๋Š” ์ฃผ์ฒด๊ฐ€ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. LoginActivity onCreate() ์‹œ์ ์— ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๊ณ , onDestroy() ์‹œ์ ์— ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.

class LoginActivity: Activity() {

    private lateinit var loginViewModel: LoginViewModel
    private lateinit var loginData: LoginUserData
    private lateinit var appContainer: AppContainer


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        appContainer = (application as MyApplication).appContainer

        // Login flow has started. Populate loginContainer in AppContainer
        appContainer.loginContainer = LoginContainer(appContainer.userRepository)

        loginViewModel = appContainer.loginContainer.loginViewModelFactory.create()
        loginData = appContainer.loginContainer.loginData
    }

    override fun onDestroy() {
        // Login flow is finishing
        // Removing the instance of loginContainer in the AppContainer
        appContainer.loginContainer = null
        super.onDestroy()
    }
}

๋กœ๊ทธ์ธ ๊ด€๋ จ Fragment๋“ค๋„ AppContainer์˜ LoginContainer๋ฅผ ํ†ตํ•ด ๋™์ผํ•œ LoginUserData๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฒฐ๋ก 

DI๋Š” ํ™•์žฅ ๊ฐ€๋Šฅํ•˜๊ณ  ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅํ•œ Android ์•ฑ์„ ๋งŒ๋“œ๋Š” ๋ฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค. ์ปจํ…Œ์ด๋„ˆ๋ฅผ ํ†ตํ•ด ํด๋ž˜์Šค ์ธ์Šคํ„ด์Šค๋ฅผ ๊ณต์œ ํ•˜๊ณ , ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ ์ฝ”๋“œ๋ฅผ ์ปจํ…Œ์ด๋„ˆ์— ๋ชจ์•„์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์•ฑ์ด ์ ์  ์ปค์งˆ์ˆ˜๋ก ๋งŽ์€ ์–‘์˜ Factory ์ฝ”๋“œ์™€ ๊ฐ™์€ ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ ์ฝ”๋“œ๊ฐ€ ์ƒ๊ธฐ๊ธฐ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ ์ฝ”๋“œ๋Š” ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค. ์ปจํ…Œ์ด๋„ˆ์˜ scope์ด๋‚˜ ๋ผ์ดํ”„์‚ฌ์ดํด๊นŒ์ง€ ์ง์ ‘ ๊ด€๋ฆฌํ•ด์•ผํ•˜๊ณ , ๋ฉ”๋ชจ๋ฆฌ ํ•ด์ œ๋ฅผ ์œ„ํ•ด ๋”์ด์ƒ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์ปจํ…Œ์ด๋„ˆ ์ œ๊ฑฐ๋„ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๊ด€๋ฆฌ๋ฅผ ์ž˜๋ชป ํ•˜๊ฒŒ๋˜๋ฉด ์ž ์žฌ์ ์ธ ๋ฒ„๊ทธ๋‚˜ ๋ฉ”๋ชจ๋ฆฌ๋ฆญ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๋‹ค์Œ ํŒŒํŠธ์—์„œ๋Š” ์ด๋Ÿฐ ๊ณผ์ •๋“ค์„ Dagger๋ฅผ ์ด์šฉํ•ด ์–ด๋–ป๊ฒŒ ์ž๋™ํ™”ํ•˜๊ณ , ์—ฌ๊ธฐ์„œ ์ง์ ‘ ์ผ๋˜ ์ฝ”๋“œ๋“ค์„ ์–ด๋–ป๊ฒŒ ์ƒ์„ฑํ•˜๋Š”์ง€ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค!