ํฐ์คํ ๋ฆฌ ๋ทฐ
[iOS] ์์กด์ฑ ์ฃผ์ (Dependency Injection) ๊ฐ๋ ๊ณผ ์์ ( feat. Clean Architecture + MVVM)
ir.__.si 2023. 3. 31. 00:30Clean Architecture๋ฅผ ๊ณต๋ถํ๋ฉด์ ํท๊ฐ๋ฆฌ๋ ๊ฐ๋ ์ด์๋ '์์กด์ฑ ์ฃผ์ (Dependency Injection)'์ ๋ํ ์ ๋ฆฌ๋ฅผ ํ๋ ค๊ณ ํฉ๋๋ค.
๋ค๋ง, Clean Architecture์ ๋ํ ์ ๊ณผ์ ์ ๋ค๋ฃจ๋ ๊ธ์ด ์๋๋ ์ด ์ ์ฐธ๊ณ ๋ถํ๋๋ฆฝ๋๋ค.
๋ํ ์๋ฒฝํ ๊ธ์ด ์๋๋ค๋ณด๋, ์๋ชป๋ ๋ด์ฉ์ด ์์ ์ ์์ผ๋ ๋ง์ ์ง์ ๋ถํ๋๋ฆฌ๊ฒ ์ต๋๋ค! :)
์์กด์ฑ(Dependency)์ด๋?
ํํ, Clean Architecture์ ๊ฐ์ฅ ์ค์ํ ์์น์ ์ค๋ช ํ ๋
"๊ฐ ๊ณ์ธต(Layer)์ ์๋ก ๋ ๋ฆฝ์ ์ด์ด์ผ ํ๋ฉฐ, ํ ๊ณ์ธต์ ๋ณํ๊ฐ ๋ค๋ฅธ ๊ณ์ธต์ ์ํฅ์ ์ฃผ์ด์๋ ์๋๋ค!"
๋ผ๊ณ ์ค๋ช ํฉ๋๋ค.
์ฌ๊ธฐ์ ๋งํ๋ '๊ณ์ธต์ด ์๋ก ๋ ๋ฆฝ์ ' ์ด๋ผ๋ ๋ง์, ๊ฐ ๊ณ์ธต์ ๊ฒฐํฉ๋๋ฅผ ์ต์ํ ํ๋๊ฒ์ ์งํฅํด์ผํ๋ค๋ ๋ง ์ ๋๋ค.
ํ์ง๋ง, iOS์ฑ์ ๋์์ํค๊ธฐ ์ํด์๋ ๊ฐ ๊ณ์ธต๊ฐ์ ์ผ์ ์์ค์ ์ํธ ์์กด์ฑ(Dependency)์ด ํ์์ ์ ๋๋ค.
Clean Architecture๋ ์ด๋ฐ '์์กด์ฑ(Dependency)'์ ๋จ๋ฐฉํฅ์ผ๋ก ๊ด๋ฆฌํฉ๋๋ค.
๊ทธ๋ ๊ธฐ ๋๋ฌธ์, ์์ ์ฌ์ง๊ณผ ๊ฐ์ ์์กด์ฑ(Dependency)๋ฐฉํฅ์ ๊ฐ๊ฒ ๋๋๋ฐ,
์ฌ๊ธฐ์ ๋งํ๋ '์์กด์ฑ(Dependency)' ์ ์๋ฏธ๋ "A๊ฐ B๋ฅผ ์์กดํ ๋, B์ ๋ณ๊ฒฝ์ฌํญ์ด A์๋ ์ํฅ์ ๋ฏธ์น๊ฒ ๋๋ค" ๋ผ๋ ์๋ฏธ์ ๋๋ค.
์์กด์ฑ์ ๋ํด ๊ฐ๋จํ ์ฝ๋๋ฅผ ์์๋ก ๋ค์ด๋ณด๊ฒ ์ต๋๋ค.
class Calculator {
func add(_ num1: Int, _ num2: Int) -> Int {
return num1 + num2
}
}
์์ ๊ฐ์ด ์ํ์ ๊ณ์ฐ์ ์ํํ๋ Calculator๋ผ๋ ํด๋์ค๋ฅผ ๊ฐ๋ ์ฑ์ด ์๋ค๊ณ ๊ฐ์ ํด ๋ณด๊ฒ ์ต๋๋ค. Calculator ํด๋์ค์๋ ๋ ๊ฐ์ ์ซ์๋ฅผ ๋ํ๋ add๋ผ๋ ๋ฉ์๋๊ฐ ์์ต๋๋ค.
class MyViewController: UIViewController {
let calculator = Calculator()
func calculate() {
let result = calculator.add(2, 3)
print(result)
}
}
์ด์ ์ฑ์ MyViewController์์ ์ด Calculator ํด๋์ค๋ฅผ ์ฌ์ฉํ๊ณ ์ถ๋ค๊ณ ๊ฐ์ ํด ๋ณด๊ฒ ์ต๋๋ค.
Calculator ํด๋์ค๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด ์ธ์คํดํธ๋ฅผ ์์ฑ ํ, add ๋ฉ์๋๋ฅผ ํธ์ถํด์ผ ํฉ๋๋ค.
๊ทธ๋ฐ๋ฐ, Calculator์ ์ฝ๋๊ฐ ์๋์ ๊ฐ์ด ๋ณ๊ฒฝ๋๋ค๋ฉด ์ด๋ป๊ฒ ๋ ๊น์??
class Calculator {
func sum(_ num1: Int, _ num2: Int) -> Int {
return num1 + num2
}
}
์ด๋ ๊ฒ ๋๋ค๋ฉด, MyViewController์์๋ ๊ธฐ์กด์ add ๋ฉ์๋๋ฅผ ํธ์ถํด์ ์ํํ๋ ๋์์,
sum ๋ฉ์๋๋ก ์์ ํด์ผ ์ด์ ์ ๋์์ด ๊ฐ๋ฅํ๊ฒ ๋ฉ๋๋ค.
์ด์ฒ๋ผ, A๊ฐ B๋ฅผ ์์กดํ๊ฒ ๋๋ค๋ฉด, B์ ์ ๋ณด๊ฐ ๋ณ๊ฒฝ๋์์ ๋ A๋ '์ ์ ํ ํ๋์ ์ทจํด์ผ' ํฉ๋๋ค.
'์์กด์ฑ ์ฃผ์ (Dependency Injection)' ์ด๋??
์์์ ์ค๋ช ํ ๋จ๋ฐฉํฅ ์์กด์ฑ์ Clean Architecture์ ์ ์ฉํ๊ฒ ๋๋ค๋ฉด ์๋์ ์ฌ์ง๊ณผ ๊ฐ์ ๊ด๊ณ๊ฐ ๋ฉ๋๋ค.
๊ฐ์ Level์ ์๋ Persentation, Data๋ Domain์ ์์กดํ๊ฒ ๋ฉ๋๋ค.
์ฆ, Domain์ Presentation๊ณผ Data๋ฅผ ์์กดํ๋ฉด ์๋ฉ๋๋ค.
์, ๊ทธ๋ฐ๋ฐ ์ฐ๋ฆฌ๋ Clean Architecture์์ ๊ณ์ธต๊ฐ์ ๊ฒฐํฉ๋๋ ์ต๋ํ ์ค์ด๋๊ฒ ์ข๋ค๊ณ ํ์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ ์ด๊ฒ์ ์ํด ์ฌ์ฉํ๋ ๊ฒ์ด '์์กด์ฑ ์ฃผ์ (Dependency Injection)' ์ ๋๋ค!
'์์กด์ฑ์ ์ฃผ์ ํ๋ค'์ ๋ป์, '์ธ๋ถ'์์ ์์กด์ฑ์ ๋ถ์ฌํ๋ค๋ ์๋ฏธ ์ ๋๋ค!
๊ณ์ธต๊ฐ์ ๊ฒฐํฉ๋๋ฅผ ์ค์ด๊ธฐ ์ํด์๋, ์์กด์ฑ์ ๋ฎ์ถฐ์ผ ํ๋๋ฐ ์คํ๋ ค ์์กด์ฑ์ ๋ํ๋ค,,,,?
๋ผ๊ณ ์๊ฐ ํ ์ ์๋๋ฐ, ์ฌ๊ธฐ์ ์ค์ํ๊ฑด '์ธ๋ถ'์์ ์์กด์ฑ์ ๋ถ์ฌํ๋ค๋ ๊ฒ์ ๋๋ค.
์ฝ๋๋ฅผ ์๋ก๋ค์ด ์ค๋ช ํด๋ณด๊ฒ ์ต๋๋ค.
์ฐ๋ฆฌ๊ฐ ํํ๊ฒ ์๊ฐํ๋ '์์กด์ฑ'์ ๊ฐ๋ ๊ด๊ณ๋ ์๋์ ์ฝ๋์ ๊ฐ์ด,
Class ๋ด๋ถ์์ ๋ค๋ฅธ ํด๋์ค์ ์ธ์คํดํธ๋ฅผ ์์ฑํ๋ ๊ฒ ์ ๋๋ค.
class Calculator {
func add(_ num1: Int, _ num2: Int) -> Int {
return num1 + num2
}
}
class MyViewController: UIViewController {
let calculator = Calculator()
func calculate() {
let result = calculator.add(2, 3)
print(result)
}
}
// ์ด ์ฝ๋๋, MyViewController ๋ด๋ถ์์ Calculator์ ์ธ์คํดํธ๋ฅผ ์์ฑํ๊ณ ์์ต๋๋ค.
// ๊ทธ๋ ๊ธฐ ๋๋ฌธ์, MyViewController -> Calculator์ ์์กด์ฑ ๊ด๊ณ๋ฅผ ๊ฐ์ต๋๋ค.
๊ทธ๋ ๋ค๋ฉด, ์ด๋ฒ์๋ ์์กด์ฑ ์ฃผ์ (Dependency Injection)์ ์ฌ์ฉํด๋ณด๊ฒ ์ต๋๋ค.
์ด๋ฌํ ์์กด์ฑ ์ฃผ์ ์๋
1. ์์ฑ์ ์ฃผ์
2. setter ์ฃผ์
3. ์ธํฐํ์ด์ค ์ฃผ์ (์์กด์ฑ ๋ถ๋ฆฌ)
์ด๋ ๊ฒ 3๊ฐ์ง์ ๋ฐฉ์์ด ์๋๋ฐ, Clean Architecture์์๋ ์์กด์ฑ ๋ถ๋ฆฌ๋ฅผ ์ฑํํ๊ณ ์์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ ์ด๋ฅผ ์ํด์ '์์กด์ฑ ์ญ์ ์์น (Dependency Inversion Principle)'์ ๋ํด ๋จผ์ ์์์ผ ํฉ๋๋ค.
์ด ์์น์๋ 2๊ฐ์ง์ ๊ท์น์ด ์๋๋ฐ,
- ์์ ๊ณ์ธต์ ํ์ ๊ณ์ธต์ ์์กดํ๋ฉด ์๋๋ค. ๋ ๊ณ์ธต ๋ชจ๋ ์ถ์ํ์ ์์กดํด์ผํ๋ค
- [ ์ถ์ํ -> ์ธ๋ถ์ฌํญ ]์ ์์กด๊ด๊ณ๊ฐ ์๋ [ ์ธ๋ถ์ฌํญ -> ์ถ์ํ ]์ ์์กด๊ด๊ณ๋ฅผ ๊ฐ์ ธ์ผํ๋ค.
๊ทธ๋ฆฌ๊ณ ์ด๋ฅผ ์ํด์ Swift ์์๋ protocol์ ์ฌ์ฉํฉ๋๋ค.
protocol Calculatable {
func add(_ num1: Int, _ num2: Int) -> Int
}
class Calculator: Calculatable {
func add(_ num1: Int, _ num2: Int) -> Int {
return num1 + num2
}
}
class MyViewController: UIViewController {
let calculator: Calculatable = Calculator()
func calculate() {
let result = calculator.add(2, 3)
print(result)
}
}
// ์ด ์ฝ๋๋ Calculatable์ด๋ผ๋ ์ธ๋ถprotocol์ ํตํด์ ์์กด์ฑ์ด ๋ง๋ค์ด์ง๊ณ ์์ต๋๋ค.
// ์ฆ, ์ค์ ์์กด์ฑ์ ๋ฐฉํฅ์ MyViewController -> Calculatable <- Calculator
์ฐจ์ด์ ์ด ๋ณด์ด์๋์??
๊ธฐ์กด์ ์ฝ๋๋ MyViewController -> Caculator ์ ๋ฐฉํฅ์ผ๋ก ์์กด์ฑ์ ๊ฐ์ง๊ณ ์์ง๋ง,
์์ ์ฝ๋๋ ์ธ๋ถ protocol์ธ Calculatable์ ์ฌ์ฉํ์ฌ ์์กด์ฑ์ ์ฃผ์ ํ์์ต๋๋ค.
๊ทธ ๊ฒฐ๊ณผ MyViewController -> Calculatable <- Calculator ์ด๋ผ๋ ์์กด์ฑ ๊ด๊ณ๊ฐ ์์ฑ๋ฉ๋๋ค.
์ด๋ฌํ ์์กด์ฑ ์ฃผ์ (Dependency Injection)์ ์ค์ ๊ณ์ธต๊ฐ์ ๊ฒฐํฉ๋๋ฅผ ๋ฎ์ถ๋ ํจ๊ณผ๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
์๋ํ๋ฉด, protocol ์ด๋ผ๋ ์ธ๋ถ์ ๊ป๋ฐ๊ธฐ๋ฅผ ํตํด์ ์๋ก ๋์จํ๊ฒ ์ฐ๊ฒฐ๋์ด ์๊ธฐ ๋๋ฌธ์ด์ฃ !!
๋ํ, ๊ณ์ธต๊ฐ์ ๊ฒฐํฉ๋๊ฐ ๋ฎ๊ธฐ ๋๋ฌธ์ Unit๋จ์์ Test๊ฐ ์ฉ์ดํ๋ค๋ ์ฅ์ ๋ ์์ต๋๋ค!
๊ทธ๋ ๋ค๋ฉด, ์ฐ๋ฆฌ๊ฐ ๊ณต๋ถํ๊ณ ์๋ Clean Architecture์ ์ ์ฉํด์ ์๊ฐ ํด ๋ณด๊ฒ ์ต๋๋ค
Clean Architecture + MVVM ์์์ '์์กด์ฑ ์ฃผ์ (Dependency Injection)'
์ฐ๋ฆฌ๊ฐ ์๊ณ ์๋ Clean Architecture์์๋
Domain์๋ ์ฑ์ ํต์ฌ ๊ธฐ๋ฅ๊ณผ ๊ฐ์ ๋น์ง๋์ค๋ก์ง์ ๋ด๋นํ๋ UseCase๊ฐ ๋ค์ด์๊ณ ,
Data์๋ RestAPI ์ฒ๋ฆฌ๋, ๋ฐ์ดํฐ ์บ์ฑ ๊ฐ์ ๋คํธ์ํน ๋ก์ง๋ค์ ๊ฐ๋ Repository๊ฐ ๋ค์ด๊ฐ๊ฒ ๋ฉ๋๋ค.
๊ทธ๋ฆฌ๊ณ UseCase์์ ๋น์ง๋์ค๋ก์ง์ ์ฒ๋ฆฌํ๊ธฐ ์ํด์๋ ์ด๋ฌํ ๋ก์ง ์ฒ๋ฆฌ๋ฅผ ์ํ ๋ฐ์ดํฐ๋ฅผ ์๋ฒ์์ ๊ฐ์ ธ ์ฌ
Repository์ Networking ์์ ์ด ์ ํ๋์ด์ผ ํฉ๋๋ค.
์ด๊ฒ์ด ์๋ฏธํ๋ ๋ฐ๋, UseCase๋ Repository๋ฅผ ์์กดํด์ผ ํ๋ค๋ ๋ป ์ ๋๋ค.
ํ์ง๋ง, UseCase๋ Repository๋ณด๋ค ๋ ํ์ ๊ณ์ธต์ด๊ธฐ ๋๋ฌธ์ ์ง์ ์์ ํ ์ ์์ต๋๋ค.
๊ทธ๋ ๊ธฐ ๋๋ฌธ์, Domain ๊ณ์ธต์ protocol์ ๋์ด์ '์์กด์ฑ ์ญ์ ์์น'์ ์งํค๋ '์์กด์ฑ ์ฃผ์ '์ ์งํํ๊ฒ๋ฉ๋๋ค.
protocol์ Domain Layer์ ์ ์ธํ๊ณ , protocol์ ๋ด๋ถ ๊ตฌํ์ Data Layer์์ ์งํ์ ํ๊ฒ ๋๋ค๋ฉด
Domain [ UseCase → Protocol ] ← Data[ Repository ] ์ด์ ๊ฐ์ ํํ๋ฅผ ๋๊ฒ ๋ฉ๋๋ค.
์ด๋ฅผ ํตํด์,
1) Domain ๊ณ์ธต๊ณผ Data๊ณ์ธต๊ฐ์ ๊ฒฐํฉ๋๋ ๋ฎ์์ง๊ณ
2) Repository์ ๋ณํ๊ฐ UseCase์ ์ง์ ์ ์ธ ์ํฅ์ ์ฃผ์ง ์๊ฒ ๋ฉ๋๋ค!
์ด์ ๋น์ทํ๊ฒ, Presenter -> Domain์ ๊ด๊ณ์์๋ protocol์ ์ฌ์ฉํ '์์กด์ฑ ์ฃผ์ '์ด ์ด๋ค์ง๊ฒ ๋ฉ๋๋ค.
ํ์ง๋ง, Domain๊ณผ Data ์ฌ์ด์ '์์กด์ฑ ์ฃผ์ '๊ณผ๋ ๋ญ๊ฐ ๋ชฉ์ ์ฑ์ด ๋ค๋ฅด๋ค๊ณ ๋๊ปด์ง์์ฃ ??
์ด์ฒ๋ผ, Clean Architecture์์๋
Domain ๊ณ์ธต๊ณผ Data ๊ณ์ธต์ ๊ด๊ณ์์ ์ฒ๋ผ
1) ์์กด์ฑ์ ๋ฐฉํฅ์ ์ ์ดํ๊ธฐ ์ํด์
Domain ๊ณ์ธต๊ณผ Presenter ๊ณ์ธต์ ๊ด๊ณ์์ ์ฒ๋ผ
2) ๊ฐ ๊ณ์ธต์ ๊ฒฐํฉ๋๋ฅผ ๋์จํ๊ฒ ํ๊ธฐ ์ํด์
์ฆ, ๋ค์ํ ๋ชฉ์ ์ฑ์ ์ํด ๋์ผํ๊ฒ '์์กด์ฑ ์ญ์ ์์น'์ ๋ฐ๋ฅด๋ '์์กด์ฑ ์ฃผ์ '๊ธฐ์ ์ ์ฌ์ฉํฉ๋๋ค!