์์ฑ์ผ: 2020.03.02
์ด ๊ธ์ 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๋ฅผ ์ถ๊ฐํ๋ ๋ฐฉ๋ฒ์ ๋ํด ์์๋ณด๊ฒ ์ต๋๋ค.