๋ชจ๋ฐ์ผ์์์ Dependency Injection - Part2.
- #kotlin
- #dagger
- #hilt
- #android
- #ios
- #swift
- #swinject
๋ค์ด๊ฐ๋ฉฐ
์ง๋ ํฌ์คํธ์์ DI ๊ฐ ๋ฌด์์ธ์ง์ ๋ํด์ ๊ฐ๋ตํ๊ฒ ์ค๋ช ํ์์ต๋๋ค. ์ด๋ฒ Part2 ํฌ์คํธ์์๋ ๋ชจ๋ฐ์ผํ์์๋ DI ๋ฅผ ์ด๋ป๊ฒ ํ์ฉํ๊ณ ์๋์ง ๊ฐ๋ตํ๊ฒ ์๊ฐ๋๋ฆฌ๋ ค๊ณ ํฉ๋๋ค. ํ์ฉ๋ฐฉ๋ฒ์ ์๊ฐํ๊ธฐ ์์ ๋จผ์ ๊ฐ ํ๋ซํผ ๋ณ๋ก ํ์ฉํ๊ณ ์๋ DI Framework ์ ๊ฐ๋ตํ ์ค๋ช ๋๋ฆฌ๊ณ ์ ํฉ๋๋ค.
๋ชจ๋ฐ์ผํ์์ ํ์ฉํ๋ DI Framework
Hilt in Android
์๋๋ก์ด๋์์๋ Hilt ๋ผ๋ DI framework์ ์ฌ์ฉํ๊ณ ์์ต๋๋ค. Hilt ๋ Android์์ ์ข ์ ํญ๋ชฉ์ ์ฝ์ ํ๊ธฐ ์ํ Jetpack์ ๊ถ์ฅ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค. Dagger ๊ธฐ๋ฐ์ผ๋ก ๋ง๋ค์ด์ง ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ฉฐ Dagger์ ๋ค์ํ๊ณ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ๋ค์ Android ํ๊ฒฝ์ ๋ง์ถฐ ์์ฝ๊ฒ ํ์ฉ ํ ์ ์๋๋ก ๋ง๋ค์ด์ง ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ ๋๋ค.
Hilt ๋ ๋ณต์กํ ์ค์ ์ด๋ ์ฝ๋ ๊ตฌํ ์์ด Annotation๊ธฐ๋ฐ์ผ๋ก Android ์ ๋ผ์ดํ์ฌ์ดํด์ ๋ง๋ ์ข ์์ฑ ์ฃผ์ ์ ์ง์ํฉ๋๋ค.
์๋ฅผ ๋ค์ด
@HiltAndroidApp
class ExampleApplication : Application() {
//...
}
@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {
//...
}
์ ์ฝ๋์ ๊ฐ์ด HiltAndroidApp, AndroidEntryPoint Annotation ์ ์ง์ ํจ์ผ๋ก์ ์ฝ๊ฒ Android ์ปดํฌ๋ํธ์ ๋ผ์ดํ์ฌ์ดํด์ ๋ง์ถฐ ์ข ์์ฑ์ ์ฃผ์ ์ ํ ์ ์๊ฒ ํด์ค๋๋ค.
์ด์ธ์๋ HiltViewModel, ViewModelScoped ๋ฑ Android ์ ๋ง๋ ๋ค์ํ ๊ธฐ๋ฅ๋ค์ ์ ๊ณตํด์ค๋๋ค. ๋ฟ๋ง์๋๋ผ ์ปดํ์ผ ์ Code Generation์ ํตํด DI ๋ฅผ ์ง์ํด์ฃผ๋ ํํ์ด๊ธฐ ๋๋ฌธ์ ๋ฐํ์์ค๋ฅ๋ฅผ ๋ฐฉ์งํ๊ณ ์ฌ์ ์ DI ์ด์๋ฅผ ํด๊ฒฐ ํ ์ ์์ต๋๋ค.
Swinject in iOS
iOS์์๋ Swinject์ด๋ผ๋ DI framework์ ์ฌ์ฉํ๊ณ ์์ต๋๋ค. Swinject์ a lightweight dependency injectionย framework ๋ก์ iOS ๊ฐ๋ฐ์์ ์ฝ๊ฒ DI ๋ฅผ ์ ์ฉํ ์ ์๋๋ก ๋์์ค๋๋ค.
Swinject์ Container ๋ฅผ ํตํด ์ฃผ์ ํ ๊ฐ์ฒด๋ฅผ ๊ด๋ฆฌํฉ๋๋ค.
์๋ฅผ ๋ค์ด
let container = Container()
container.register(Animal.self) { _ in Cat(name: "Mimi") }
container.register(Person.self) { r in
PetOwner(pet: r.resolve(Animal.self)!)
}
์์ ๊ฐ์ด Container ๋ฅผ ์์ฑํ์ฌ ์ฃผ์ ํ ๊ฐ์ฒด๋ฅผ ์ค์ ํ๊ณ
์ฃผ์ ๋์์์๋ ๋ค์๊ณผ ๊ฐ์ด ๊ตฌํ์ฒด๋ฅผ ํธ์ถํ์ฌ ์ฌ์ฉํฉ๋๋ค.
let person = container.resolve(Person.self)!
person.play() // prints "I'm playing with Mimi."
Android์ ๋ฌ๋ฆฌ iOS์๋ ๊ถ์ฅํ๋ DI ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์๊ธฐ ๋๋ฌธ์ ์ ํฌ๋ ๋น๊ต์ ๊ฐ๋ณ๊ณ ์ฌ์ฉ์ด ์ฌ์ด Swinject์ ์ด์ฉํ์ฌ DI๋ฅผ ์ ์ฉํ์์ต๋๋ค.
DI ํ์ฉ
Hilt๋ Swinject์ ์ฌ์ฉ๋ฐฉ๋ฒ์ ๋ฐ๋ก ์ค๋ช ํ์ง ์๊ฒ ์ต๋๋ค. ๊ณต์๋ฌธ์์๋ ์ฌ์ฉ๋ฐฉ๋ฒ์ ์ ๋์ ์์ผ๋ฉฐ ๊ตฌ๊ธ๋ง์ ํตํด์๋ ์ฌ์ฉ๋ฒ์ ์ฝ๊ฒ ์ตํ ์ ์์ต๋๋ค. ์ด๋ฒ ํฌ์คํธ์์๋ ์ ํฌํ์์ ์ด๋ป๊ฒ DI๋ฅผ ํ์ฉํ๋์ง์ ๋ํด์ ์๊ฐ๋๋ฆฌ๊ณ ์ ํฉ๋๋ค.
๋ชจ๋ฐ์ผํ์์๋ Android, iOS ๋ ํ๋ซํผ ๋ชจ๋ UI, Domain, Data ๋ ์ด์ด๋ก ๊ตฌ์ฑ๋ ์ํคํ ์ฒ๋ก ์ค๊ณ ๋์ด ์์ต๋๋ค. Domain Layer์์๋ ํต์ฌ ๋น์ฆ๋์ค ๋ก์ง์ UseCase๋ก ๊ด๋ฆฌํ๋ฉฐ Repository Interface ๋ฅผ ์ ์ํ์ฌ ์ฌ์ฉํฉ๋๋ค. UI ๋ ์ด์ด์์๋ Domain๋ ์ด์ด์์์ UseCase๋ฅผ ํ์ฉํ์ฌ View์ ๊ด๋ จ๋ ๋ก์ง์ ์ฒ๋ฆฌํ๋ฉฐ, Data๋ ์ด์ด์์๋ API I/O, DB I/O, Preference(UserDefault) ๋ฑ ๋ฐ์ดํฐ ์ก์์ ๊ด๋ จ๋ ์ญํ ์ ๋ด๋นํฉ๋๋ค. ๋น์ฆ๋์ค ๋ก์ง์ ๋ด๋น์ Domain๋ ์ด์ด์ ๋ช ํํ๊ฒ ์์ํจ์ผ๋ก์ UI์ Data์ชฝ ๋ก์ง๊ฐ์ coupling์ ์ค์ด๊ณ ์๋ก ์ํฅ์ ์ค์ฌ ์ ์ง๋ณด์๊ฐ ์ฉ์ดํ๊ณ ์ฌ์ฌ์ฉ์ด ๊ฐ๋ฅํ ๊ตฌ์กฐ๋ก ์ค๊ณํ๊ณ ์ ํ์์ต๋๋ค.
Domain๋ ์ด์ด๊ฐ ๋น์ฆ๋์ค ๋ก์ง์ ์ง์คํ ์ ์๋๋ก ๋ค๋ฅธ ๋ ์ด์ด๊ฐ์ Loose Coupling์ ์ํด interface๋ง ์ ๊ณตํ๊ณ ์ค์ ๊ตฌํ์ ๊ฐ ๋ด๋น ๋ ์ด์ด๊ฐ ๋ด๋นํ๊ฒ ๋ฉ๋๋ค. ์ด๋ Domain ๋ ์ด์ด์์๋ IoC๊ฐ ๋ฐ์ํ๊ฒ ๋ฉ๋๋ค.
์๋ฅผ ๋ค์ด๋ณด๊ฒ ์ต๋๋ค. Domain๋ ์ด์ด์์๋ ๋ค์๊ณผ ๊ฐ์ด ๋น์ฆ๋์ค ๋ก์ง์ ๊ตฌํํ๊ณ ์์ต๋๋ค. ์ด๋ UserRepository๋ผ๋ ์ธํฐํ์ด์ค๋ฅผ ํ์ฉํฉ๋๋ค.
class LoginUseCase(private val userRepository) {
fun execute(): User {
userRepository.signIn()
userRepository.getProfile()
}
}
interface UserRepository {
fun signIn()
fun getProfile()
}
Data๋ ์ด์ด์์๋ Domain๋ ์ด์ด์ ์์กด์ฑ์ ๊ฐ๊ณ UserRepository๋ฅผ ๊ตฌํํฉ๋๋ค.
class UserRepositoryImpl: UserRepository {
fun signIn() {
//do something
}
fun getProfile() {
//do something
}
}
์ด๋ Domain๋ ์ด์ด์์ ์์กด์ฑ ์ญ์ ์ด ๋ฐ์ํฉ๋๋ค. Domain๋ ์ด์ด๋ Data๋ ์ด์ด์ ๊ตฌํ์ฒด๋ฅผ ๋ชจ๋ฅธ ์ํ๋ก ๊ฐ์ฒด๋ฅผ ์ฃผ์ ๋ฐ์์ผ Domain๋ ์ด์ด์ decoupling์ ์ ์งํ ์ ์์ต๋๋ค. ์ด๋ DI๊ฐ ํ์ํ๊ฒ ๋ฉ๋๋ค.
์์๋ก ๋ Android ์ฝ๋์์๋ Hilt๋ฅผ ํ์ฉํ์ฌ ์ฝ๊ฒ DI๋ฅผ ์ํํ ์ ์์ต๋๋ค.
class LoginUseCase(@Inject private val userRepository) {
fun execute(): User {
userRepository.getProfile()
}
}
DI Framework๋ฅผ ํ์ฉํ๋ฉด Dependency Tree ๋ฐ Scope์ ์ฝ๊ฒ ๊ด๋ฆฌํ ์ ์์ต๋๋ค. Hilt์ ๊ฒฝ์ฐ ์ด๋ฏธ Android์์ ์์ฃผ ์ฌ์ฉ๋๋ scope๋ค์ ์ ๊ณตํด์ฃผ๊ธฐ ๋๋ฌธ์ ์ฝ๊ฒ Scope๋ฅผ ์ค์ ํ ์ ์์ต๋๋ค. ์ฌ์ค์ ํ์ ์ ์ธ scope๋ค์ธ Activity, Fragment, ViewModel๋ฑ์ ๋ฏธ๋ฆฌ ์ ๊ณตํด์ฃผ๊ธฐ ๋๋ฌธ์ ์ฝ๊ฒ ์์กด์ฑ ์ฃผ์ ์ ์ํํ ์ ์์ต๋๋ค. iOS์ ๊ฒฝ์ฐ Swinject์์ ์ ๊ณตํด์ฃผ๋ ObjectScope ๊ธฐ๋ฅ์ ํ์ฉํ์ฌ ์ฝ๊ฒ ์ํ๋ Scope์ ๊ด๋ฆฌํ ์ ์์ต๋๋ค. ์ ํฌ๋ ViewModel, ViewController ๋ฑ ์ํคํ ์ฒ ๋ฐ ํ๋ซํผ ์ Scope ๊ด๋ฆฌํด์ผํ๋ ๊ฐ์ฒด๋ค์ ObjectScope์ ํ์ฉํ์ฌ Scope์ ์ง์ , ์์กด์ฑ ์ฃผ์ ์ ๊ด๋ฆฌํ๊ณ ์์ต๋๋ค.
๋ง์น๋ฉฐ
์ฌ๊ธฐ๊น์ง ๋ชจ๋ฐ์ผํ์์ DI๋ฅผ ์ด๋ป๊ฒ ํ์ฉํ๊ณ ์๋์ง ๊ธฐ๋ณธ์ ์ธ ๋ถ๋ถ์ ๊ฐ๋จํ๊ฒ ์๊ฐ๋๋ ธ์ต๋๋ค. ๋ค์ part3์์๋ ๊ธฐ๋ณธ์ ์ธ ์์กด์ฑ ์ฃผ์ ์ ๋์ด ๋ณต์กํ ๋น์ฆ๋์ค ๊ตฌํ์ ์ํ ์์กด์ฑ ์ฃผ์ ์ ์ด๋ค ์์ผ๋ก DI Framework์ ํ์ฉํ์ฌ ์ฒ๋ฆฌํ๊ณ ์๋์ง, Custom Scope , Property Wrapper, InjectionMap๋ฑ ๋ค์ํ๊ณ ๊ธฐ๋ฅ๋ค์ ์ด๋ป๊ฒ ํ์ฉํ๊ณ ์๋์ง ์ข ๋ ์์ธํ ์ค๋ช ๋๋ฆฌ๋๋ก ํ๊ฒ ์ต๋๋ค.