๋ชฉ์ฐจ
๋ค์ด๊ฐ๊ธฐ ์ ์
ํ๋ก์ ํธ์์ Hilt๋ฅผ ์ฌ์ฉํ๊ณ ์๋ค. ๊ธฐ๋ฅ์ ๊ตฌํํ๊ธฐ์ ๋ฐ๋น Hilt๊ฐ ๋์ค๊ธฐ๊น์ง ํ๋ฆ์ ์ ๋๋ก ์๊ฐ ๋ค์ฌ ๊ณต๋ถํด๋ณด์ง ๋ชปํ๋๋ฐ ์ฑ ์ถ์๋ฅผ ์ํด ํ ์คํธ๊น์ง ๊ฑธ๋ฆฌ๋ ๊ธฐ๊ฐ๋์ ๊ทธ๋์ ๋ชปํ๋ ๊ณต๋ถ๋ฅผ ํ๋ฉฐ ๋ถ์กฑํ ๋ด์ฉ์ ์ฑ์๋ณด๋ คํ๋ค~! ์ค๋์ ์์กด์ฑ ์ฃผ์ ํํธ!!
์์กด์ฑ ์ฃผ์ ๊ธฐ๋ณธ
์กํฐ๋นํฐ์์ ๋ทฐ๋ชจ๋ธ ๊ฐ์ฒด๊ฐ ํ์ํ ๊ฒฝ์ฐ
๋ทฐ๋ชจ๋ธ ๊ฐ์ฒด๋ฅผ ๊ตฌ์ฑํ๊ธฐ ์ํด์ ๋ ํฌ์งํ ๋ฆฌ ๊ฐ์ฒด๊ฐ ํ์
๋ ํฌ์งํ ๋ฆฌ ๊ฐ์ฒด๋ฅผ ๊ตฌ์ฑํ๊ธฐ ์ํด ๋ฐ์ดํฐ ์์ค ๊ฐ์ฒด๊ฐ ํ์
๋ฐ์ดํฐ ์์ค ๊ฐ์ฒด๋ฅผ ๊ตฌ์ฑํ๊ธฐ ์ํด์ ์๋น์ค ๊ฐ์ฒด๊ฐ ํ์
์๋น์ค ๊ฐ์ฒด๋ฅผ ๊ตฌ์ฑํ๊ธฐ ์ํด์๋ ๋ ํธ๋กํ ๊ฐ์ฒด๊ฐ ํ์
// ์ฝ๋ ๊ตฌํ ์
0. ๋ ํธ๋กํ ๊ฐ์ฒด ๊ตฌ์ฑ
1. ์๋น์ค ๊ฐ์ฒด ๊ตฌ์ฑ
2. ๋ฐ์ดํฐ ์์ค ๊ฐ์ฒด ๊ตฌ์ฑ
3. ๋ ํฌ์งํ ๋ฆฌ ๊ฐ์ฒด ๊ตฌ์ฑ
4. ๋ทฐ๋ชจ๋ธ ๊ฐ์ฒด ๊ตฌ์ฑ
์์ ๊ฐ์ด ์์๋๋ก ๊ตฌํํด์ค์ผํ๋ค.
๋ค๋ฅธ ๋ถ๋ถ์์ ๋ค๋ฅธ ๋ทฐ๋ชจ๋ธ ๊ฐ์ฒด๋ฅผ ๋ง๋๋ ค๊ณ ํ๋ค๋ฉด ์ ๊ณผ์ ์ ์ค๋ณตํด์ ๊ตฌํํ์
์ข ์ ํญ๋ชฉ์ ์์๋๋ก ์ ์ธํด์ค์ผํ๋ค.
๊ฐ์ฒด๋ฅผ ์ฌ์ฌ์ฉํ๊ธฐ ์ด๋ ต๋ค.
์ปจํ ์ด๋๋ก ์ข ์ ํญ๋ชฉ ๊ด๋ฆฌ
- ๊ฐ์ฒด ์ฌ์ฌ์ฉ ๋ฌธ์ ํด๊ฒฐ์ ์ํด
- ์ข ์ ํญ๋ชฉ์ ๊ฐ์ ธ์ค๋๋ฐ ์ฌ์ฉํ๋ ์์ฒด ์ข ์ ํญ๋ชฉ ์ปจํ ์ด๋ ํด๋์ค ์์ฑ!
- Application์ ๋ฐฐ์นํ๊ธฐ
// Application.kt
class Application : Application() {
// ์ฑ๊ธํค ํจํด์ ๋ฐ๋ฅด์ง ์์
// ์ฑ ์ ์ฒด์์ ๊ณต์ ๋๋ ๊ณ ์ ํ ์ธ์คํด์ค๊ฐ ์๋ ํด๋์ค์ด๋ค.
val appContainer : AppContainer()
}
- AppContainer
class AppContainer {
val userViewModelFactory = UserViewModelFactory(userRepository)
private val retrofit = Retrofit
.Builder()
.baseUrl(movieApiBaseUrl)
.client(OkHttpClient)
.addCallAdapterFactory(ResultCallAdapter.Factory(retryCount))
.addConverterFactory(json.asConverterFactory(contentType.toMediaType()))
.build()
private val userService = retrofit.create(UserService::class.java)
private val userDataSource = UserDataSourceImpl(userService)
val userRepository = UserRepositoryImpl(userDataSource)
- ํ๋๊ทธ๋จผํธ์์ ๋ทฐ ๋ชจ๋ธ ๊ฐ์ฒด ๋ง๋ค๊ธฐ
class UserFragment : Fragment() {
private lateinit var viewModel : UserViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Gets userRepository from the instance of AppContainer in Application
val appContainer = (application as Application).appContainer
// viewModel = UserViewModel(appContainer.userRepository)
viewModel = appContainer.userViewModelFactory.create()
}
}
์ด๋ ๊ฒ ๋ง๋ค์ด์ง UserRepository ๊ฐ์ฒด๋ ์ฑ๊ธํค์ด ์๋๋ค. ๋ค๋ฅธ ํด๋์ค์์๋ ์ฌ์ฉํ ์ ์๋ ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด์ฃผ๋ ๊ฒ์ด๋ค.
์ด๋ฌํ ๋ฐฉ์์ผ๋ก ๋ ํฌ์งํ ๋ฆฌ ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ค๋ฉด ํ๋ก์ ํธ์์ ์ฌ์ฉํ๋ ๋ชจ๋ ๋ ํฌ์งํ ๋ฆฌ ๊ฐ์ฒด ์์ฑ๊ณผ ๊ด๋ จํ์ฌ dataSource, Service, retrofit์ ๋ชจ๋ ๊ตฌ์ฑํด์ฃผ๋ ์ฝ๋๊ฐ ํ์ํ๊ฒ ๋ค....
๋ง์ฝ viewModel์ด ๋ ๋ง์ ๊ณณ์์ ํ์ํ๋ค๋ฉด ํ๊ณณ์์ ๊ฐ์ฒด๋ฅผ ๋ง๋๋ ๊ฒ์ด ์ข๋ค. ๋ง๋ ๋ทฐ๋ชจ๋ธ์ appContainer๋ก ์ด๋ํ๊ณ , ๊ทธ ์ ํ์ ์ ๊ฐ์ฒด์ ํฉํ ๋ฆฌ๋ฅผ ์ ๊ณตํ ์ ์๋ฐ.
// T : ๋ง๋ค๊ณ ์ถ์ ์ด๋ค ํ์
์ด๋ ok
// create() ๋ ๊ทธ ํ์
์ ์๋ก์ด ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด์ฃผ๋ ๋ฉ์๋
interface Factory<T> {
fun create() : T
}
// UserViewModel์ฉ Factory
class UserViewModelFactory(
private val userRepository: UserRepository
) : Factory<UserViewModel> {
// ์ฌ๊ธฐ์ T : UserViewModel
override fun create(): UserViewModel {
return UserViewModel(userRepository)
}
}
๋น๊ต
- ๊ธฐ์กด
- ์กํฐ๋นํฐ๋ ํ๋๊ทธ๋จผํธ์์ ๋ทฐ๋ชจ๋ธ ์์ฑ
userViewModel = UserViewModel(appContainer.userRepository)
- ๋ณ๊ฒฝ : Factory๋ฅผ ํตํด AppContainer๋ก ๋ทฐ๋ชจ๋ธ ์์ฑ ์ฑ ์์ ์ด๋ํ๋ค.
- class AppContainer { val userViewModelFactory = UserViewModelFactory(userRepository)
ํฉํ ๋ฆฌ ํจํด
- ๊ฐ์ฒด ์์ฑ์ ๋ด๋นํ๋ ๋ณ๋์ ํด๋์ค(ํฉํ ๋ฆฌ)๋ฅผ ๋์ด ๊ฐ์ฒด ์์ฑ ๋ก์ง์ ์บก์ํํ๋ ๋์์ธ ํจํด
๋ฒ์
- ๋ค์ํ ๊ธฐ๋ฅ ํ๋ฆ์ ๋์
ํ๋ฉฐ ๊ฐ์ฒด๊ฐ ํด๋น ํ๋ฆ์ ๋ฒ์์๋ง ์๊ธฐ๋ฅผ ์ํ ์ ์๋ค.
- ์๋ฅผ ๋ค์ด ๋ก๊ทธ์ธ ํ๋ฆ์์๋ง ์ฌ์ฉ๋๋ ์ฌ์ฉ์ ์ด๋ฆ๊ณผ ๋น๋ฐ๋ฒํธ๋ก ๊ตฌ์ฑ๋ ์ ์๋ LoginUserData๋ฅผ ๋ง๋ค ๋, ๋ค๋ฅธ ์ฌ์ฉ์์ ์ด์ ๋ก๊ทธ์ธ ํ๋ฆ์ ๋ฐ์ดํฐ๋ฅผ ์ ์งํ์ง ์๊ณ ๋ชจ๋ ์ ํ๋ฆ์ ์ ์ธ์คํด์ค๋ฅผ ๋ง๋ค๊ณ ์ถ์ ์๋ ์๋ค.
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
}
ํ๋ฆ ๊ด๋ จ ์ปจํ ์ด๋๊ฐ ์์ผ๋ฉด ์ปจํ ์ด๋ ๊ฐ์ฒด๋ฅผ ์ธ์ ๋ง๋ค๊ณ ์ญ์ ํ ์ง ํ๋จํด์ผํ๋ค.
๋ก๊ทธ์ธ ํ๋ฆ์ด LoginActivity์ ๋ ๋ฆฝ์ ์ด๋ฏ๋ก ์ด ์ปจํ ์ด๋์ ์๋ช ์ฃผ๊ธฐ๋ฅผ LoginActicity์์ ๊ด๋ฆฌํ๋ฉด ๋๋ค. 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()
}
}
Dagger
์ข ์ํญ๋ชฉ์ ์ ์ธํ๊ณ , ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ์ฌ ์ข ์ํญ๋ชฉ์ ์ด๋ป๊ฒ ์ถฉ์กฑํ๋์ง ์ง์ ํ๊ธฐ๋ง ํ๋ฉด ์๋๋ฅผ ๋น๋ ์๊ฐ์ ์๋์ผ๋ก ๊ตฌํํด์ค๋ค.
์์์ ์๋์ผ๋ก ์ข ์ํญ๋ชฉ์ ๋ํ ์ปจํ ์ด๋๋ฅผ ์์ฑํด๋ณด๋ฉฐ , ์ฌ๋ฌ ๋ ํฌ์งํ ๋ฆฌ ๊ฐ์ฒด๋ฅผ ๊ตฌ์ฑํ ๊ฒฝ์ฐ ์ค๋ณต, ๋ฐ๋ณตํด์ ์์ฑํด์ค์ผํ ์ฝ๋๊ฐ ๋ง๊ฒ ๋ค๊ณ ๋๊ผ๋ค. ์ด๋ฅผ ํด์ํด์ฃผ๊ธฐ ์ํด Dagger์์๋ ์๋์ผ๋ก ๊ตฌํํ๋ ๋ก์ง์ ์๋์ผ๋ก ๊ตฌํํด์ค๋ค.
- ์์์ ์๋์ผ๋ก ๊ตฌํํ๋ AppContainer ์ฝ๋๋ฅผ ์์ฑํด์ค๋ค.
- AppContainer(application grapth)์์ ์ฌ์ฉํ๋ ํด๋์ค์ ํฉํ ๋ฆฌ๋ฅผ ๋ง๋ค์ด์ค๋ค.
- ๋ฒ์๋ฅผ ํตํด ์ข ์ ํญ๋ชฉ์ ์ฌ์ฌ์ฉํ ์ง, ์ ์ธ์คํด์ค๋ฅผ ๋ง๋ค์ง ๊ฒฐ์
- ๋ก๊ทธ์ธ ํ๋ฆ์ ์ฒ๋ฆฌํ๋ ๊ฒ์ฒ๋ผ Dagger subcomponent๋ฅผ ์ฌ์ฉํ์ฌ ํน์ ํ ํ๋ก์ฐ๋ฅผ ์ํ ์ปจํ
์ด๋๋ฅผ ๋ง๋ค์ด์ค๋ค.
- ๋์ด์ ์ฌ์ฉ๋์ง ์์ ๋ ๊ฐ์ฒด๋ฅผ ํด์ ํด์ค์ผ๋ก์จ ์ฑ๋ฅ ํฅ์
- ํด๋์ค์ ์ธ์คํด์ค๋ฅผ ์ ๊ณตํ๋ ๋ฐฉ๋ฒ์ ์ฐพ๊ธฐ ์ํด ์ฐธ์กฐํ ์ ์๋ ๊ฐ์ฒด์ ๊ทธ๋ํ๋ฅผ ๋ง๋ ๋ค.
- ๊ทธ๋ํ์ ๋ชจ๋ ํด๋์ค์ ๊ด๋ จํ์ฌ ํ์ํ ์ ํ์ ์ธ์คํด์ค๋ฅผ ๊ฐ์ ธ์ค๋๋ฐ ๋ด๋ถ์ ์ผ๋ก ํฉํ ๋ฆฌ ์ ํ ํด๋์ค๋ฅผ ์์ฑ
์์
UserRepository ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ธฐ ์ํด UserLocalDataSource, UserRemoteDataSource๊ฐ ํ์ํ๋ค๋ฉด ์๋์ ๊ฐ์ด ์์ฑํ ์ ์๋ค.
class UserRepository (
private val localDataSource : UserLocalDataSource,
private val remoteDataSource: UserRemoteDataSource
) { ... }
Dagger๊ฐ UserRepository ๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ์ ์ ์ ์๋๋ก @Inject ์ฃผ์์ UserRepository ์์ฑ์์ ์ถ๊ฐํ๋ค.
// @Inject
// 1. Dagger๊ฐ UserRepository ๊ฐ์ฒด๋ฅผ ์ด๋ป๊ฒ ์์ฑํ๋์ง ์๊ฒ ํด์ค๋ค.
// 2. ์ข
์ํญ๋ชฉ์ UserLocalDataSource ๋ฐ UserRemoteDataSource์์ ์๋ ค์ค๋ค.
class UserRepository @Inject constructor(
private val localDataSource : UserLocalDataSource,
private val remoteDataSource:
) { ... }
์ด์ ์ข ์ํญ๋ชฉ์ ๋ง๋๋ ๋ฐฉ๋ฒ์ ์๋ ค์ค์ผํ๋ค. UserLocalDataSource์UserRemoteDataSource์๋ @Inject๋ฅผ ๋ถ์ฌ์ ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ์ ์๋ ค์ค๋ค.
class UserLocalDataSource @Inject constructor() { ... }
class UserRemoteDataSource @Inject constructor() { ... }
Dagger ๊ตฌ์ฑ์์
Dagger๋ ์ข ์ ํญ๋ชฉ์ด ํ์ํ ๋ ๊ฐ์ ธ์ฌ ์์น๋ฅผ ์ฐพ๋๋ฐ ์ฌ์ฉํ ์ ์๋ ํ๋ก์ ํธ์ ์ข ์ ํญ๋ชฉ ๊ทธ๋ํ๋ฅผ ๋ง๋ค ์ ์๋ค.
- ์ธํฐํ์ด์ค ์์ฑ
- @Component๋ก ์ฃผ์ ์ง์
→ ์ด๋ฅผ ํตํด ์ปจํ ์ด๋๋ฅผ ๋ง๋ ๋ค.
@Component๋ก ์ฃผ์์ ์ค interface ๋ด์์ ํ์ํ ํด๋์ค์ ์ธ์คํด์ค(UserRepository)๋ฅผ ๋ฐํํ๋ ํจ์๋ฅผ ์ ์ํ ์ ์๋ค.
์ฆ @Component๋ Dagger์๊ฒ ์ด๋ ๊ฒ ๋งํ๋ ๊ฒ๊ณผ ๊ฐ๋ค.
@Component : Dagger์ผ ๋๋ ์ด๋ฐ ๊ฒ๋ค์ด ํ์ํด. ์์์ ๋ง๋ค์ด์ค๋?
//@Component : Dagger๊ฐ ์์กด์ฑ ๊ทธ๋ํ๋ฅผ ์์ฑํ๋๋ก ํ๋ค.
@Component(modules = [NetworkModule::class, DatabaseModule::class])
interface ApplicationGraph {
fun repository(): UserRepository
}
ํ๋ก์ ํธ ๋น๋ ์์ Dagger๋ ์๋์ผ๋ก ApplicationGraph ์ธํฐํ์ด์ค์ ๊ตฌํ์ธ DaggerApplicationGraph๋ฅผ ์์ฑํ๋ค.
์ด๋ฅผ ํตํด ์ด ๊ทธ๋ํ๋ UserRepository, UserLocalDataSource, UserRemoteDataSource ํด๋์ค ๊ฐ์ ๊ด๊ณ๋ก ๊ตฌ์ฑ๋ ์ค์ ํญ๋ชฉ ๊ทธ๋ํ๋ฅผ ๋ง๋ค์ด์ค๋ค.
val applicationGraph : ApplicationGraph = DaggerApplicationGraph.create()
val userRepository : UserRepository = applicationGrapgh.repotiroy()
Dagger๋ applicationGrapgh.repository() ๋ฅผ ํธ์ถํ ๋๋ง๋ค UserRepository์ ์ ์ธ์คํด์ค๋ฅผ ๋ง๋ ๋ค.
val applicationGraph: ApplicationGraph = DaggerApplicationGraph.create()
val userRepository: UserRepository = applicationGraph.repository()
val userRepository2: UserRepository = applicationGraph.repository()
assert(userRepository != userRepository2)
๋ง์ฝ ๊ณ ์ ํ ์ธ์คํด์ค๊ฐ ํ์ํ๋ค๋ฉด??
๋งค๋ฒ UserRepository ๊ฐ์ฒด๋ฅผ ์๋ก ์์ฑํ ํ์๊ฐ ์์๊น??
- ๋ง์ฝ ๋์ผํ ๋ฐ์ดํฐ๋ฅผ ์ฌ๋ฌ ๋ทฐ๋ชจ๋ธ์์ ๊ณต์ ํ๊ณ ์ ํ๋ ๊ฒฝ์ฐ
- ๊ฐ์ฒด๋ฅผ ์์ฑํ๋๋ฐ ๋น์ฉ์ด ๋ง์ด ๋ ๋ค๋ ์ ์ ๊ณ ๋ คํ์ ๋ ๋ก์ง์ ๋ง๊ฒ ์ธ์คํด์ค๋ฅผ ๊ตฌ์ฑํ๋ ๊ฒ์ด ์ข๋ค!
์๋์ผ๋ก ์ข ์ ํญ๋ชฉ์ ๊ตฌํํ ๋๋ ๋์ผํ UserRepository ๊ฐ์ฒด๋ฅผ ๋ทฐ๋ชจ๋ธ ํด๋์ค์ ์์ฑ์์ ์ ๋ฌํ๋ ๊ฒ์ผ๋ก ๊ตฌํํ์ง๋ง Dagger์์๋ ๋ฒ์ ์ฃผ์์ ํตํด์ ๋์ผํ ๊ฐ์ฒด๋ฅผ ์์ฑํ ๊ฑฐ์ผ! ๋ผ๊ณ ์๋ ค์ค ์ ์๋ค.
๋ฒ์ ์ง์
// Dagger์๊ฒ ์ด ๊ทธ๋ํ์์ ๋ง๋๋ @Singleton ๊ฐ์ฒด๋ค์ ์ฑ์ด ์ด์์๋ ๋์
// ๋ฑ! ํ๋๋ง ๋ง๋ค์ด์ ์ฌ์ฌ์ฉํด์ค ๋ผ๊ณ ๋งํ๋ ๊ฒ๊ณผ ๊ฐ๋ค.
@Singleton
@Component
interface ApplicationGraph {
fun repository() : UserRepository
}
UserRepository๋ฅผ ์ฑ ์ ์ฒด์์ ๋ฑ ํ๋์ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๋๋ก ๋ง๋ค์ด์ค๋ค.
์ด๋ฅผ ํตํด ๋ทฐ๋ชจ๋ธ์์ UserRepostory๋ก ์์ ์์ ์ผ๊ด๋ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
@Singleton
class UserRepository @Inject constructor(
private val localDataSource: UserLocalDataSource,
private val remoteDataSource: UserRemoteDataSource
) { ... }
์ด ํํธ์์๋ ์ข ์ ํญ๋ชฉ ๊ทธ๋ํ๋ฅผ ๊ทธ๋ ค๋๊ฐ๋ฉด์, ํ์ํ ๋ฒ์๋ฅผ ์๊ฐํด๋ณด๋ฉด ์ข๊ฒ ๋ค. ๋ฌดํฑ๋๋ก ์ฑ๊ธํค์ผ๋ก ๊ตฌํํ๊ธฐ ๋ณด๋ค๋ ์ ์ ํ ๋ฒ์๋ฅผ ์ง์ ํด๋ณด์!
Android์์ Dagger๋ฅผ ์ฌ์ฉํด๋ณด์!
๊ถ์ฅ ์ฌํญ
- ๊ฐ๋ฅํ๋ฉด ์์ฑ์ ์ฝ์ ์ ํ๊ณ ๊ฐ๋ฅํ์ง ์์ ๊ฒฝ์ฐ๋ ์๋์ ๊ฐ์ด ํ์
- @Binds -> ์ธํฐํ์ด์ค ํํ์ ์ข ์ ํญ๋ชฉ ์ฝ์ ์ผ๋ก ์ธํฐํ์ด์ค์ ์ด๋ค ๊ตฌํ์ด ์์ด์ผํ๋์ง ์๋ ค์ฃผ๊ธฐ
- @Provides -> ํ๋ก์ ํธ๊ฐ ์์ ํ์ง ์์ ํด๋์ค๋ฅผ ์ ๊ณตํ๋ ๋ฐฉ๋ฒ์ ์๋ ค์ค๋ค.
- ์ฌ์ฉ๋๋ ์ ์ฒด ๊ธฐ๊ฐ์ ๋ฐ๋ผ ๋ฒ์๋ฅผ ์ง์ ํด์ค๋ค
// 1. ์ฑ์ ์์กด์ฑ ๊ทธ๋ํ ์ ์
@Component
interface ApplicaitionComponent {...}
// Application์ ์๋ช
์ฃผ๊ธฐ๋ฅผ ๊ณต์ ํ๊ธฐ ์ํด appComponent๋ฅผ Application์ ๋๋ค.
class Application : Application() {
val appComponent = DaggerApplicationComponent.create()
}
์กํฐ๋นํฐ๋ ํ๋๊ทธ๋จผํธ์ ๊ฐ์ ํด๋์ค๋ ์์คํ ์ ์ํด ์ธ์คํด์คํ๋๊ธฐ ๋๋ฌธ์ Dagger๊ฐ ์๋์ผ๋ก ์์ฑํ ์ ์๋ค.
๋ฐ๋ผ์ ์กํฐ๋นํฐ๋ ํ๋๊ทธ๋จผํธ์์๋ ํ๋ ์ฝ์ ์ ํตํด์ ํ์ํ ์ข ์ ํญ๋ชฉ์ ์๋ ค์ค ์ ์๋ค.
Activity, Fragment, Service, BroadcastReceiver, ContentProvider , ์ฆ ์๋๋ก์ด๋ ํ๋ ์์ํฌ ํด๋์ค์์ ์์ฑ์ ์ฝ์ ์ด ๋ถ๊ฐ๋ฅํ๋ค!! ๋ฐ๋ผ์ ํ๋ ์ฝ์ ์ ์ฌ์ฉํด์ผํ๋ค.
class UserFragment : Fragment() {
@Inject lateinit var userViewModel : UserViewModel
}
์กํฐ๋นํฐ์์ ๋ทฐ๋ชจ๋ธ ๊ฐ์ฒด๋ฅผ ์ ๊ณต๋ฐ๊ธฐ ์ํด์๋
@Component
interface ApplicationComponent {
fun inject(activity : LoginActivity)
// Dagger์๊ฒ " LoginActivity๊ฐ ์ฃผ์
์ ์์ฒญํ๊ณ ์์ด.
// ๊ทธ๋ฌ๋๊น ๊ทธ๋ํ๋ ์กํฐ๋นํฐ๊ฐ ์์ฒญํ๋ ๋ชจ๋ ํ๋์ ์์กด์ฑ์ ๋ง์กฑํด์ผํด!"
// ์กํฐ๋นํฐ๊ฐ ๊ทธ๋ํ์ ์ ๊ทผํ๊ธธ ์ํ๋ฉฐ ์ฝ์
์ ์์ฒญํ๋ค๊ณ Dagger์๊ฒ ์๋ ค์ค๋ค.
}
class LoginActivity : Activity() {
// ๋ทฐ๋ชจ๋ธ ๊ฐ์ฒด
// ์ข
์ ํญ๋ชฉ ๊ทธ๋ํ๋ฅผ ํตํดLoginViewModel ๊ฐ์ฒด๋ฅผ ๋ฐ๊ณ ์ถ์ด์! ๋ผ๊ณ Dagger์๊ฒ ์์ฒญํ๋ ๊ฒ
@Inject lateinit var loginViewModel : LoginViewModel
override fun onCreate(savedInstanceState: Bundle?) {
(applicationContext as Application).appComponent.inject(this)
/*
1. Dagger์ผ , LoginActivity์ @Inject ํ๋ ๋ค์ ์ด๊ธฐํํด์ค
2. ์ค์ ๋ก ์ฃผ์
3. ์ด์ loginViewModel ์ฌ์ฉ ๊ฐ๋ฅ
*/
super.onCreate(savedInstanceState)
}
}
@Inject : Dagger์ผ, LoginViewModel์ ์ด๋ ๊ฒ ๋ง๋ค๋ฉด ๋ผ!
class LoginViewModel @Inject constructor(
private val userRepository: UserRepository
)
๊ทธ๋ฌ๋ฉด Dagger๋ ๋ ํ ์งํ ๋ฆฌ ๊ฐ์ฒด๋ฅผ ๊ตฌ์ฑํ๋ ค๊ณ ํ ๊ฒ์ด๊ณ , ์ด์ ๋ฐ๋ผ ํ์ํ UserLocalDataSource, UserRemoteDataSource ๊ฐ์ฒด๋ค์ ๊ตฌ์ฑํ๋ ๋ฐฉ๋ฒ์ Dagger์๊ฒ ์๋ ค์ค์ผํ๋ค. ์ด๋ฅผ ์ํด์๋ @Inject constructor (...)์ ์ฌ์ฉํ๋ฉด ๋๋ค.
๋ชจ๋
UserRemoteDataSource์๋ ๋ ํธ๋กํ์ด ์ฌ์ฉ๋๋ค. ๋ ํธ๋กํ ๊ฐ์ฒด ๊ตฌ์ฑ์ ์ํด์๋ ํด๋์ค ์ธ์คํด์คํ๊ฐ ์๋ Retrofit.Builder()๋ฅผ ํธ์ถํด์ผํ๋ค.
์ด ๋๋ @Inject ๋์ @Module์ ํตํด Dagger์๊ฒ ์ด ํด๋์ค๋ Dagger ๋ชจ๋์ด์ผ ๋ผ๊ณ ์๋ ค์ค ์ ์๋ค.
@Module
class NetworkModule {...}
์ฌ๊ธฐ์ ๋ชจ๋์ ๊ฐ์ฒด๋ฅผ ์ ๊ณตํ๋ ๋ฐฉ๋ฒ์ ๋ํ ์ ๋ณด๋ฅผ ์บก์ํํ ๊ฒ์ด๋ค.
์๋์์๋ ๋คํธ์ํน ๊ด๋ จ ๊ฐ์ฒด๋ฅผ ์ ๊ณตํ๋ ๋ก์ง์ ๊ทธ๋ฃนํํ๊ธฐ ์ํด NetworkModule ํด๋์ค๋ฅผ ํธ์ถํ๋ค.
// 1. Dagger ๋ชจ๋์ด์ผ
@Module
class NetworkModule {
// 2. ์ด ํจ์๊ฐ ๋ฐํํ๋ ํ์
์ ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ฉด ๋ผ
// ํ๋ก์ ํธ๊ฐ ์์ ํ์ง ์์ ํด๋์ค(ex. retrofit, okHttpClient, Gson ๋ฑ) ๊ฐ์ฒด๋ฅผ ์ ๊ณตํ๋ ๋ฐฉ๋ฒ์ ์๋ ค์ค๋ค.
@Provides
fun provideLoginRetrofitService(
okHttpClient : OkHttpClient
) : LoginRetrofitService {
return Retrofit.Builder()
.baseUrl("https://example.com")
.build()
.create(LoginService::class.java)
}
}
์ด๋ ๊ฒ ๋ชจ๋์ ์ ์ํด์ค ํ Dagger ๊ทธ๋ํ๊ฐ ์ด ๋ชจ๋์ ๊ดํด ์ ์ ์๋๋ก ํ๋ ค๋ฉด ๋ค์๊ณผ ๊ฐ์ด @Component ์ธํฐํ์ด์ค์ ๋ชจ๋์ ์ถ๊ฐํด์ค์ผํ๋ค.
// modules: ๊ทธ๋ํ๋ฅผ ๊ตฌ์ฑํ ๋ ํฌํจ๋์ด์ผํ ๋ชจ๋์ ์๋ ค์ฃผ๋ ์ญํ
@Component(modules = [NetworkModule::class])
interface ApplicationComponent {...}
LoginActivity๋ LoginViewModel์ด ํ์ํ๋ค. Dagger๋ LoginViewModel & ์ข ์ ํญ๋ชฉ์ ์ธ์คํด์ค๋ฅผ ์ ๊ณตํ๋ ๋ฐฉ๋ฒ์ ๋ด์ ๊ทธ๋ํ๋ฅผ ๋น๋ํ๋ค.
Dagger๋ ํด๋์ค ์์ฑ์์ @Inject๋ ํตํด ๋ฐฉ๋ฒ์ ์๊ณ ์๋ค. ์์ฑํ ApplicationComponent ๋ด์๋ ์ ๊ณต ๋ฐฉ๋ฒ์ ์ ๊ณ ์๋ ๋ชจ๋ ํด๋์ค์ ์ธ์คํด์ค๋ฅผ ๊ฐ์ ธ์ค๋ ํฉํ ๋ฆฌ ๋ฉ์๋๊ฐ ์๋ค. ๋ํ LoginRetrofitService ๊ฐ์ฒด๋ฅผ ์ป๊ธฐ ์ํด NetworkModule์ ํฌํจํ๊ณ ์๋ค.
๋ฒ์
@Singleton
@Component(modules = [NetworkModule::class])
interface ApplicationComponent {
fun inject(activity : LoginActivity)
}
@Singleton
class UserRepository @Inject constructor(
private val localDataSource: UserLocalDataSource,
private val remoteDataSource: UserRemoteDataSource
) { ... }
@Module
class NetworkModule {
@Singleton
@Provides
fun provideLoginRetrofitService(): LoginRetrofitService { ... }
}
์ํ๋ Scope๋ก ๊ตฌ์ฑํด๋ณด์
๋ก๊ทธ์ธ ํ๋ฆ์ ๊ตฌ์ฑํ๋ ํ๋ฉด์ด ์ฌ๋ฌ ๊ฐ์ ํ๋๊ทธ๋จผํธ๋ก ๋์ด์๋ค๋ฉด ๋ก๊ทธ์ธ ํ๋ฆ์ ํ๋๊ทธ๋จผํธ์์๋ ๋์ผํ LoginViewModel ๊ฐ์ฒด๋ฅผ ์ฌ์ฌ์ฉํด์ผํ๋ค.
๊ทธ๋ ๋ค๋ฉด ๋ก๊ทธ์ธ ํ๋ฆ์ด ์๋ฃ๋ ํ์๋ ์ด๋จ๊น?
๋ก๊ทธ์ธ ํ๋ฆ์ด ๋๋ฌ๋ค๋ฉด ์ด์ ๋์ด์ LoginViewModel์ด ๋จ์์์ง ์์๋ ๋๋ค.
๊ฐ ๋ก๊ทธ์ธ ํ๋ฆ๋ง๋ค , ๋ค์ ๋งํด ๋ก๊ทธ์ธ ํ๋ฆ์ ๋ํด์ ๋ค๋ฅธ LoginViewModel ์ธ์คํด์ค๊ฐ ํ์ํ๋ค. ์ฌ์ฉ์๊ฐ ๋ก๊ทธ์์ํ ๊ฒฝ์ฐ์๋ ์ฒ์ ๋ก๊ทธ์ธํ์ ๋์๋ ๋ค๋ฅธ ๋ทฐ๋ชจ๋ธ ๊ฐ์ฒด๊ฐ ํ์ํ๋ค.
์ฑ๊ธํค์ผ๋ก ๊ตฌํ ์์๋ ์ด์ ๋ก๊ทธ์ธ ์ ๋ณด๊ฐ ๋จ์์๊ฑฐ๋ ์ด์ ๋ฐ์ดํฐ๋ ๋จ์์์ ์ ์๊ธฐ ๋๋ฌธ์ ์ด๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด์ ๋ทฐ๋ชจ๋ธ์ ๋ฒ์๋ฅผ LoginActicity์ ์๋ช ์ฃผ๊ธฐ๋ก ์ง์ ํ ์ ์๋ค.
๋ฐ๋ผ์ ์ด๋ฐ ๊ฒฝ์ฐ๋ผ๋ฉด ๋ก๊ทธ์ธ ์ ์ฉ Scope๋ฅผ ๋ง๋ค์ด์ฃผ๋ฉด ๋๋ค.
@Component
interface LoginComponent {}
LoginActivity๋ ๋ก๊ทธ์ธ ๋ณ ๊ตฌ์ฑ์ด ์์ผ๋ฏ๋ก LoginComponent์์ ์ข ์ ํญ๋ชฉ์ ๋ฐ์์ผํ๋ค.
@Component
interface LoginComponent {
fun inject(activity : LoginActivity)
}
LoginActivity๋ LoginViewModel์ด ํ์ํ๋ค. ์ด ๋ LoginViewModel์ UserRepository์ ์ข ์๋๊ธฐ ๋๋ฌธ์ LoginComponent๋ ApplicationComponent์ ๊ฐ์ฒด์ ์ ๊ทผํ ์ ์์ด์ผํ๋ค. ์ด ๋ Dagger์ SubComponent๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
์ ๊ตฌ์ฑ์์๊ฐ ๋ค๋ฅธ ๊ตฌ์ฑ ์์์ ์ผ๋ถ๋ฅผ ์ฌ์ฉํ๋๋ก ํ๋ ๋ฐฉ๋ฒ์ด @SubComponent์ด๋ค.
์ฌ๊ธฐ์ ํ์ ๊ตฌ์ฑ์์๋ ์์ ๊ตฌ์ฑ ์์์ ๊ฐ์ฒด ๊ทธ๋ํ๋ฅผ ์์ ๋ฐ ํ์ฅํ๋ ๊ตฌ์ฑ ์์์ด๋ค. ๋ฐ๋ผ์ ์์ ๊ตฌ์ฑ์์์ ์ ๊ณต๋ ๋ชจ๋ ๊ฐ์ฒด๋ ํ์ ๊ตฌ์ฑ์์์๋ ์ ๊ณต๋๋ค. ์ฆ ApplicationComponent์ ์ ๊ณต๋ ๋ชจ๋ ๊ฐ์ฒด๋ LoginComponent์๋ ์ ๊ณต๋๋๋ก ํ ์ ์๋ค.
// @SubComponent : Dagger์๊ฒ ์ด ์ธํฐํ์ด์ค๊ฐ ํ์ ๊ตฌ์ฑ ์์์์ ์๋ ค์ค๋ค.
@SubComponent
interface LoginComponent {
fun inject(loginActivity: LoginActivity)
}
๋ํ ApplicationComponent๊ฐ LoginComponent์ ์ธ์คํด์ค๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ์ ์ ์ ์๋๋ก ์๋์ ๊ฐ์ด ํ์ ๊ตฌ์ฑ์์ ํฉํ ๋ฆฌ๋ฅผ ์ ์ํด์ค๋ค.
@Subcomponent
interface LoginComponent {
@Subcomponent.Factory
interface Factory {
fun create() : LoginComponent
}
fun inject(loginActivity: LoginActivity)
}
๊ทธ๋ฆฌ๊ณ LoginComponent๊ฐ ApplicationComponent์ ํ์ ๊ตฌ์ฑ ์์์์ Dagger์๊ฒ ์๋ ค์ค๋ค.
1. ๋จผ์ ํ์ ๊ตฌ์ฑ ์์ ํด๋์ค๋ฅผ subcomponents ์์ฑ์ ์ ๋ฌํ๋ ์๋ก์ด ๋ชจ๋์ ๋ง๋ค์ด์ค๋ค.
@Module(subcomponents = LoginComponent::Class)
class SubComponentModule {}
2. ์ ๋ชจ๋์ ApplicationComponent์ ์ถ๊ฐํด์ค๋ค.
@Singleton
@Component(modules = [NetworkModule::Class, SubComponentsModule::Class])
interface ApplicationComponent { ... }
์ด์ LoginActivity์ ์ข ์ํญ๋ชฉ์ ์ฃผ์ ํ๋ ๊ฒ์ ApplicationComponent๊ฐ ์๋ LoginComponent์ด๊ธฐ ๋๋ฌธ์ ApplicationComponent ๋ด๋ถ inject() ๋ฉ์๋๋ฅผ ์ญ์ ํ ์ ์๋ค.
๋์ LoginComponent ์ธ์คํด์ค๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ์ ์์์ผํ๊ธฐ ๋๋ฌธ์ ํฉํ ๋ฆฌ๋ฅผ ํธ์ถํด์ค๋ค.
@Singleton
@Component(modules = [NetworkModule::class, SubcomponentsModule::Class])
interface ApplicationComponent {
// fun inject(activity : LoginActivity) -> ์ญ์
fun loginComponent() : LoginComponent.Factory
}
LoginComponent์ ๋ฒ์๋?
LoginComponent๋ฅผ ๋ง๋ค์๋ ์ด์ ๋
1. ๋ก๊ทธ์ธ ํ๋ฆ ์ค ๋์ผํ LoginViewModel ์ธ์คํด์ค๋ฅผ ๊ณต์ ํ๊ธฐ ์ํจ
2, ๊ฐ ๋ก๊ทธ์ธ ํ๋ฆ๋ง๋ค๋ ๋ค๋ฅธ ๋ทฐ๋ชจ๋ธ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํด์ผํจ
์ด์๋ค. ๋ฐ๋ผ์ LoginComponent์ ์ ์ ํ ๊ธฐ๊ฐ์ LoginActivity์ ๋์ผํ๋ค. ๋ชจ๋ ์ ํ๋์๋ LoginComponent์ ์๋ก์ด ์ธ์คํด์ค์ ์ด๋ฌํ ์ธ์คํด์ค๋ฅผ ์ฌ์ฉํ ์ ์๋ ํ๋๊ทธ๋จผํธ๊ฐ ํ์ํ๋ค.
์ด๋ ๊ฒํจ์ผ๋ก์จ LoginComponent๋ LoginActivity์ ์๋ช ์ฃผ๊ธฐ์ ์ฐ๊ฒฐ๋๋ค.
class LoginActivity : Activity() {
// ๋ก๊ทธ์ธ ๊ทธ๋ํ์ ๋ํ ์ฐธ์กฐ
lateinit var loginComponent : LoginComponent
}
ApplicationComponent๋ฅผ ์ฌ์ฉํด์ LoginComponent์ ์ฐธ์กฐ๋ฅผ ๊ฐ์ ธ์จ ํ ๋ณ์์ ๋ฃ์ด์ฃผ๋ฉด ๋๋ค.
class LoginActivity: Activity() {
lateinit var loginComponent: LoginComponent
// ๋ก๊ทธ์ธ ๊ทธ๋ํ์ ์ํด์ ์ฃผ์
๋์ด์ผํ๋ ํ๋
@Inject lateinit var loginViewModel : LoginViewModel
override fun onCreate(savedInstanceState: Bundle?) {
// application ๊ทธ๋ํ๋ฅผ ์ด์ฉํด์ ๋ก๊ทธ์ธ ๊ทธ๋ํ๋ฅผ ๋ง๋ ๋ค.
loginComponent = (applicationContext as Application).appComponent.loginComponent().create()
// ๋ก๊ทธ์ธ ์กํฐ๋นํฐ์์ @Inject ํ๋๋ค์ ์ด๊ธฐํํด์ค๋ค. -> ์ฝ์
-> ๋ทฐ๋ชจ๋ธ์ ์ฌ์ฉํ ์๋ค.
loginComponent.inject(this)
super.onCreate(savedInstanceState)
}
}
์ด ์ฏค์์ ๊ตฌ์กฐ๋ฅผ ํ๋ฒ ์ ๋ฆฌํด๋ณด๋ฉด
- Application Scope
- ApplicationComponent : ์ฑ ์ ์ฒด์ ์ฑ๊ธํค ์์กด์ฑ ๊ด๋ฆฌ
- NetworkModule(OkhttpClient, Retrofit)๊ณผ ๊ฐ์ ์ ์ญ์ ์ผ๋ก ํ์ํ ์์กด์ฑ ํฌํจ
- LoginFlow Scope
- LoginComponent๋ ๋ก๊ทธ์ธ ๊ธฐ๋ฅ๋ง ๋ด๋น
- LoginViewModel์ ์ด scope์์์๋ง ์กด์ฌ
- ๋ชจ๋ ๋ก๊ทธ์ธ ํ๋ฆ๊ณผ ๊ด๋ ค๋ ํ๋ฉด๋ค์ ๋ทฐ๋ชจ๋ธ ๊ณต์
LoginViewModel์ด ํ์ํ ๋๊ฐ์ ํ๋๊ทธ๋จผํธ๊ฐ ์๋ค๋ฉด ๋ ํ๋๊ทธ๋จผํธ ๋ชจ๋ ๋์ผํ ์ธ์คํด์ค์ ํจ๊ป ์ ๊ณต๋๋ค.
@ActivityScope
@SubComponent
interface LoginComponent {
@Subcomponent.Factory
interface Factory {
fun create(): LoginComponent
}
// LoginActivity , LoginUsernameFragment , LoginPasswordFragment ๋ฑ
// ๋ก๊ทธ์ธ๊ณผ ๊ด๋ จ๋ ๋ชจ๋ ํ๋ฉด๋ค์ LoginComponent์ ์์กด์ฑ ์ฃผ์
์ ์์ฒญํ๋ค.
fun inject(loginActivity : LoginActivity)
fun inject(usernameFragment : LoginUsernameFragment)
fun inject(passwordFragment : LoginPasswordFragment)
}
์ด์ LoginUsernameFragment์์ ์ฌ์ฉํ๋ ๋ฒ์ ๋ณด์
class LoginUsernameFragment: Fragment() {
// ๋ก๊ทธ์ธ ๊ทธ๋ํ๋ก๋ถํฐ ์ฃผ์
๋์ด์ผํ๋ ํ๋
@Inject lateinit var loginViewModel : LoginViewModel
override fun onAttach(context: Context) {
super.onAttach(context)
// 1. LoginActivity๋ก๋ถํฐ login๊ทธ๋ํ ๊ฐ์ ธ์ค๊ธฐ
// 2. ๊ทธ๋ํ๋ฅผ ์ฌ์ฉํด์ @Inject ํ๋๋ค์ ์ด๊ธฐํํ๊ธฐ
(activity as LoginActivity).loginComponent.inject(this)
// ์ด์ loginViewModel์ ์ ๊ทผ ๊ฐ๋ฅ, onCreateView์์๋ ์ ๊ทผ ๊ฐ๋ฅ,
Hilt
Dagger์ Hilt์ ์ฐจ์ด
- ์ฝ๋๋ก ์ดํด๋ณด๊ธฐ
// Dagger
// Hilt
'๐ค2024 ์๋๋ก์ด๋ > ๐ฟ ์ํ ํ๋ก์ ํธ ๊ฐ๋ฐ ์ผ์ง' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
MVC , MVP, MVVM ๋๋์ด ์ดํดํ๋ค (0) | 2024.12.20 |
---|---|
[์๋ฒ ํธ์ถ ์ฑ๊ณต ์คํจ ์ฒ๋ฆฌ ] ๋ชจ์กฐ๋ฆฌ ์์ ํ๊ธฐ !!!! (0) | 2024.11.29 |
๐ [Android Repository Pattern]์ ์ฌ์ฉํ๋ ์ด์ ์์๋ณด๊ธฐ! (3) | 2024.11.18 |
โบ๏ธAndroid : flow๋ฅผ ์์ฐจ์ ์ผ๋ก ์ฌ์ฉํด๋ณด์~! (2) | 2024.11.04 |
ํ๋ก์ ํธ์ Jetpack Navigation ์ ์ฉํ๊ธฐ (8) | 2024.10.15 |