[Android] Dependency Injection Part 3. Dagger basics
μ΄ κΈμ Android Developer κ°μ΄λ λ΄μ©μ ν λλ‘ μμ±λμμ΅λλ€. μμ μ½λλ Kotlinλ§ κ°μ ΈμμΌλ©°, Java μ½λλ μλ¬Έμμ νμΈν μ μμ΅λλ€.
μ§μ μμ‘΄μ± μ£Όμ μ΄λ Service locator μ¬μ©μ νλ‘μ νΈ ν¬κΈ°μ λ°λΌ λ¬Έμ κ° μκΈΈ μλ μμ΅λλ€. Daggerλ₯Ό μ¬μ©ν΄ λνλμλ₯Ό κ΄λ¦¬νλ©΄ νλ‘μ νΈμ κ·λͺ¨κ° 컀μ§λλΌλ 볡μ‘μ±μ μ μ§ν μ μμ΅λλ€.
Daggerμ μ₯μ
Daggerλ λ²κ±°λ‘κ³ μλ¬κ° λ°μνκΈ° μ¬μ΄ 보μΌλ¬νλ μ΄νΈ μ½λλ₯Ό μμ±νμ§ μλλ‘ μλ κΈ°λ₯μ μ 곡ν©λλ€.
- Part 2μμ μ§μ ꡬννλ
AppContainer
μ½λ(application graph) μμ± - Application graphμμ μ¬μ© κ°λ₯ν ν΄λμ€λ€μ λν ν©ν 리 μ½λ μμ± β λ΄λΆμ μΌλ‘ κ° μμ‘΄μ±μ λͺ¨λ λ§μ‘±μν΄
- Scopeμ μ¬μ©νμ¬ νμ μ μ΄λ»κ² μ νλλμ λ°λΌ λνλμλ₯Ό μ¬μ¬μ©νκ±°λ μ μΈμ€ν΄μ€ μμ±
- μ΄μ ννΈμ λ‘κ·ΈμΈ νλ‘μ° μμ μμμ²λΌ Dagger subcomponentλ₯Ό μ΄μ©νμ¬ νΉμ νλ‘μ°μ λν 컨ν μ΄λ μμ± β λ μ΄μ νμμλ μμμ λ©λͺ¨λ¦¬μμ ν΄μ νμ¬ μ± μ±λ₯ ν₯μμ λμ
Daggerλ μ΄ λͺ¨λ μμ μ λΉλ νμμ μλμΌλ‘ μννλ©°, μ΄μ ννΈμμ μλμΌλ‘ μμ±νλ κ²κ³Ό λΉμ·ν μ½λλ€μ μμ±ν©λλ€. λ΄λΆμ μΌλ‘, Daggerλ ν΄λμ€ μΈμ€ν΄μ€λ₯Ό μ 곡 μ μ°Έμ‘°ν μ μλ κ·Έλνλ₯Ό μμ±ν©λλ€. κ·Έλνμ λͺ¨λ ν΄λμ€λ€μ λν΄ ν©ν 리 νμ ν΄λμ€λ₯Ό μμ±νμ¬ ν΄λΉ νμ μ μΈμ€ν΄μ€λ₯Ό κ°μ Έμ¬ λ λ΄λΆμ μΌλ‘ μ¬μ©ν©λλ€.
λΉλ νμμ Daggerλ μ½λλ₯Ό νμνκ³ ,
- λνλμ κ·Έλν μμ± λ° μλ νλͺ©μ λν΄ κ²μ¦ν©λλ€:
- λͺ¨λ κ°μ²΄μ λνλμκ° Runtime Exception μμ΄ λ§μ‘±νλμ§
- λνλμ μ¬μ΄ν΄μ΄ μλμ§ (무ν 루ν μ°Έμ‘°κ° μλλ‘)
- λ°νμμ μ¬μ©λ ν΄λμ€μ κ·Έλ€μ λνλμλ₯Ό μμ±ν©λλ€.
κ°λ¨ν Dagger use case: ν©ν 리 μμ±
Daggerλ₯Ό μ΄λ»κ² μ¬μ©νλμ§ μκΈ° μν΄, μλ λ€μ΄μ΄κ·Έλ¨μ UserRepository
μ²λΌ κ°λ¨ν ν©ν 리λ₯Ό μμ±ν΄λ΄
μλ€.
UserRepository
λ μ΄λ κ² μ μν©λλ€.
class UserRepository(
private val localDataSource: UserLocalDataSource,
private val remoteDataSource: UserRemoteDataSource
) { ... }
UserRepository
μ μμ±μμ @Inject
μ΄λ
Έν
μ΄μ
μ μΆκ°νλ©΄ UserRepository
μΈμ€ν΄μ€κ° μ΄λ»κ² μμ±λλμ§ Dagger κ° μ μ μμ΅λλ€.
// @Inject lets Dagger know how to create instances of this object
class UserRepository @Inject constructor(
private val localDataSource: UserLocalDataSource,
private val remoteDataSource: UserRemoteDataSource
) { ... }
μ μ½λ μ€λν«μμ Daggerμκ² μλ €μ£Όλ κ²μ λ κ°μ§μ λλ€.
@Inject
μ΄λ Έν μ΄μ μ΄ μΆκ°λ μμ±μλ₯Ό ν΅ν΄UserRepository
κ° μ΄λ»κ² μμ±λλμ§UserRepository
μ λνλμλ€:UserLocalDataSource
,UserRemoteDataSource
μ΄μ Daggerλ UserRepository
μΈμ€ν΄μ€λ₯Ό μ΄λ»κ² μμ±ν΄μΌ νλμ§ μμ§λ§ κ·Έμ λνλμλ€μ μ΄λ»κ² μμ±ν΄μΌ νλμ§λ μ μ μμ΅λλ€. λ€λ₯Έ ν΄λμ€λ€μλ λκ°μ΄ μ΄λ
Έν
μ΄μ
μ μΆκ°νλ€λ©΄, κ·Έλ€μ λν΄μλ μ μ μμ΅λλ€.
// @Inject lets Dagger know how to create instances of these objects
class UserLocalDataSource @Inject constructor() { ... }
class UserRemoteDataSource @Inject constructor() { ... }
Dagger components
Daggerλ νλ‘μ νΈμ λνλμ κ·Έλνλ₯Ό μμ±νμ¬, κ° λνλμκ° νμν λ μ΄λμμ κ°μ ΈμμΌ νλμ§ μ μ μμ΅λλ€. Daggerκ° μ΄ κ·Έλνλ₯Ό μμ±νκΈ° μν΄μλ μΈν°νμ΄μ€λ₯Ό μμ±νκ³ , κ·Έ μΈν°νμ΄μ€μ @Component
μ΄λ
Έν
μ΄μ
μ λΆμ¬μ€μΌ ν©λλ€. κ·ΈλΌ Daggerλ μλ λνλμ μ£Όμ
μμ μ§μ νλ κ²κ³Ό κ°μ 컨ν
μ΄λλ₯Ό μμ±ν©λλ€.
@Component
μΈν°νμ΄μ€ μμ νμν ν΄λμ€(μ, UserRepository
)μ μΈμ€ν΄μ€λ₯Ό λ°ννλ ν¨μλ₯Ό μ μν μ μμ΅λλ€. @Component
μ΄λ
Έν
μ΄μ
μ Daggerκ° μμ±ν΄μΌνλ 컨ν
μ΄λλ₯Ό λνλ
λλ€. μ΄κ²μ Dagger componentλΌκ³ νλ©°, μ΄λ»κ² μμ±ν μ§μ κ° λνλμμ λν΄ Daggerκ° μκ³ μλ κ°μ²΄λ€λ‘ ꡬμ±λ κ·Έλνλ₯Ό ν¬ν¨ν©λλ€.
// @Component makes Dagger create a graph of dependencies
@Component
interface ApplicationGraph {
// The return type of functions inside the component interface is
// what can be provided from the container
fun repository(): UserRepository
}
νλ‘μ νΈλ₯Ό λΉλν λ Daggerλ ApplicationGraph
μΈν°νμ΄μ€μ ꡬν체λ₯Ό μμ±ν©λλ€: DaggerApplicationGraph
. μμ μμ Daggerλ 3κ°μ§ ν΄λμ€(UserRepository
, UserLocalDataSource
, UserRemoteDataSource
)κ°μ κ΄κ³λ‘ ꡬμ±λ κ·Έλνλ₯Ό μμ±ν©λλ€. ApplicationGraph
λ μλμ κ°μ΄ μ¬μ©ν μ μμ΅λλ€.
// Create an instance of the application graph
val applicationGraph: ApplicationGraph = DaggerApplicationGraph.create()
// Grab an instance of UserRepository from the application graph
val userRepository: UserRepository = applicationGraph.repository()
Daggerλ applicationGraph.repository()
κ° νΈμΆλ λλ§λ€ μΈμ€ν΄μ€λ₯Ό μλ‘ μμ±ν©λλ€.
val applicationGraph: ApplicationGraph = DaggerApplicationGraph.create()
val userRepository: UserRepository = applicationGraph.repository()
val userRepository2: UserRepository = applicationGraph.repository()
assert(userRepository != userRepository2)
κ²½μ°μ λ°λΌ, 컨ν μ΄λμ κ°μ μΈμ€ν΄μ€λ§ νμν κ²½μ°λ μμ΅λλ€. μλμ κ°μ μ΄μ λ‘μ.
- ν΄λΉ νμ
μ λνλμλ‘ κ°λ λ€λ₯Έ νμ
μ κ°μ μΈμ€ν΄μ€λ₯Ό 곡μ νκ³ μΆμ λ (μ, μ¬λ¬
ViewModel
μ λμΌνUserData
μΈμ€ν΄μ€λ₯Ό 곡μ ) - κ°μ²΄λ₯Ό μμ±νλ λΉμ©μ΄ 컀μ λ§€λ² μλ‘μ΄ μΈμ€ν΄μ€λ₯Ό μμ±νμ§ μκ³ , ν λ² μ μλ μΈμ€ν΄μ€λ₯Ό μ¬μ©νκ³ μΆμ λ (μ, JSON parser)
λνλμλ₯Ό μ§μ μ£Όμ
ν λλ ViewModelμ μμ±μμ λμΌν UserRepository
μΈμ€ν΄μ€λ₯Ό μ λ¬νλ κ²μΌλ‘ μ΄λ₯Ό ꡬννμ΅λλ€. νμ§λ§ Daggerλ₯Ό μ¬μ©ν λ μ½λλ₯Ό μ§μ μμ±νμ§ μκΈ° λλ¬Έμ Daggerμ κ°μ μΈμ€ν΄μ€λ₯Ό μνλ€λ κ²λ§ μλ €μ£Όλ©΄ λ©λλ€. μ΄κ²μ scope μ΄λ
Έν
μ΄μ
μ μ΄μ©νμ¬ νμν μ μμ΅λλ€.
Scoping with Dagger
Scope μ΄λ Έν μ΄μ μ μ¬μ©νμ¬ ν κ°μ²΄μ λΌμ΄ννμλΆν° κ·Έ μ»΄ν¬λνΈμ λΌμ΄ννμκΉμ§ μ νν μ μμ΅λλ€. μ¦ λνλμκ° νμν λλ§λ€ κ°μ μΈμ€ν΄μ€λ₯Ό μ¬μ©ν μ μμ΅λλ€.
UserRepository
μ μ μΌν μΈμ€ν΄μ€λ₯Ό κ°μ§κΈ° μν΄, javax.inject
μ μ΄λ―Έ μ μλ @Singletone
μ΄λ
Έν
μ΄μ
μ μ¬μ©ν μ μμ΅λλ€.
// Scope annotations on a @Component interface informs Dagger that classes annotated
// with this annotation (i.e. @Singleton) are bound to the life of the graph and so
// the same instance of that type is provided every time the type is requested.
@Singleton
@Component
interface ApplicationGraph {
fun repository(): UserRepository
}
// Scope this class to a component using @Singleton scope (i.e. ApplicationGraph)
@Singleton
class UserRepository @Inject constructor(
private val localDataSource: UserLocalDataSource,
private val remoteDataSource: UserRemoteDataSource
) { ... }
λλ 컀μ€ν scope μ΄λ Έν μ΄μ μ μ§μ μμ±νμ¬ μ¬μ©ν μ μμ΅λλ€. μλμ²λΌ scope μ΄λ Έν μ΄μ μ μμ±ν©λλ€.
// Creates MyCustomScope
@Scope
@MustBeDocumented
@Retention(value = AnnotationRetention.RUNTIME)
annotation class MyCustomScope
κ·Έλ¦¬κ³ μ΄λ κ² μ¬μ©ν©λλ€.
@MyCustomScope
@Component
interface ApplicationGraph {
fun repository(): UserRepository
}
@MyCustomScope
class UserRepository @Inject constructor(
private val localDataSource: UserLocalDataSource,
private val service: UserService
) { ... }
λ κ²½μ° λͺ¨λ @Component
μΈν°νμ΄μ€μ λμΌν scope μ΄λ
Έν
μ΄μ
μ μ¬μ©νμ΅λλ€. μ¦ applicationGraph.repository()
κ° νΈμΆλ λλ§λ€ κ°μ UserRepository
μΈμ€ν΄μ€λ₯Ό μ»κ² λ©λλ€.
val applicationGraph: ApplicationGraph = DaggerApplicationGraph.create()
val userRepository: UserRepository = applicationGraph.repository()
val userRepository2: UserRepository = applicationGraph.repository()
assert(userRepository == userRepository2)
κ²°λ‘
λ 볡μ‘ν μν©μ μ μ©νκΈ° μ μ, Daggerμ μ₯μ κ³Ό κΈ°λ³Έ λμ λ°©μμ μ΄ν΄νλ κ²μ΄ μ€μν©λλ€.
λ€μ ννΈμμλ Android μ±μ Daggerλ₯Ό μΆκ°νλ λ°©λ²μ λν΄ μμλ³΄κ² μ΅λλ€.
Comments