์๋ฌด ์๊ฐ์์ด ์ฝ๋ฉํ๋ค๊ฐ(โฆ) ์์กด์ฑ ์ฃผ์
์ ๊ทธ๋ฅ property ์ฃผ์
์ผ๋ก ํ๊ณ ์์๋ค. ๋น์ฐํ Storyboard๋ฉด ์๋์ง ์์์๋? ํ๋ ๋ฌด์ง์ฑ ์ฝ๋์ง์ ํ๋ค ์ง์ ๋ฐ๊ณ ๋ ์ฌ๋๋ค.. ๊ณผ๊ฑฐ์ ๋ด๊ฐ ์ด๋ฏธ ๊ทธ์ง๊ฑฐ๋ฆฌ๋ฅผ ๊ณต๋ถํ๋ค๋ ๊ฒ์!
๋ธ๋ก๊ทธ ์ด์ ํ๋ฉด์ ๋ค์ ์ ๋ฆฌํด๋ฌ์ผ ๊ฒ ๋ค๋ ์๊ฐ์ ํ๋ค. ์ญ์ ์ฐธ ์ ์์ด๋ฒ๋ฆฐ๋ค..
Trial & Error in Dependency Injection
์ผ๋จ ์์กด์ฑ ์ฃผ์ ์ ์ด๋ค ๋ฐฉ์์ด ์๋์ง ๋ถํฐ ์์๋ณด์. ๋จ๊ณ๋ณ๋ก ์ํ์ฐฉ์ค๋ฅผ ๊ฒช์๋ ๊ฒ์ ๋์ดํด๋ณด๊ฒ ๋ค. ๊ทธ ๊ณผ์ ์์ ์ด๋ค ์ ์ด ์ข๊ณ ๋์์ง์ ๋ํด ์ดํดํ๋ ๊ฒ์ด ์ค์ํ๋ค. ์ฒ์ ๋ฐฐ์ธ ๋ ํ๋ ์ถ์ต๊น์ง ๊ฐ์ด ์ ๋ฆฌํด๋ณด๊ฒ ๋ค.
Bad Way
์ฒ์์ ํ ๋ฐฉ๋ฒ์ด๋ค. ๊ทธ ๋น์์ ๋๋ ์์กด์ฑ ์ฃผ์ ์ด ๋ญ์ง๋ ๋ชฐ๋๋ ๊ฒ ๊ฐ๋ค. ๊ทธ๋ฅ ๋ก์ง์ ๋ถ๋ฆฌํ๊ธฐ๋ง ํ๋ฉด ๋ค๋ผ ์๊ฐํ๋ค. ํ์ง๋ง ์ด๋ฐ์์ ์ ๊ทผ์ ์ ๋ง ๋จ์ํ โ๋ถ๋ฆฌโ์๋ง ์๋ฏธ๋ฅผ ๋์์ ๋ฟ, ๊ธฐ์กด ์ฝ๋์ ๋ฌ๋ผ์ง๋ ๊ฒ์ด ์๋ค. ์ด๋ ๊ฒ ํ๋ฉด ๋นต์ ์ด๋ค.
Property & Method Injection
์ด์ ๋ถํฐ ๊ทธ๋๋ง injection์ด๋ผ ๋ถ๋ฅผ ์ ์๋ ์์ค์ ๋ฐฉ๋ฒ์ด๋ค. ์ธ๋ถ์์ ์ฌ์ฉํ๊ณ ์ ํ๋ ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ ์ด๋ฅผ ๋ฐ์ด๋ฃ์ด ์ต์ข ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด ๋ธ๋ค. ์ด ๋, property injection์ ๋ง๊ทธ๋๋ก instance์ property์ ์ ๊ทผํด์ ๋ฃ์ด๋ฒ๋ฆฌ๋ ๊ฒ์ ๋งํ๋ค. method๋ method๋ก ์ด๋ฅผ ๊ฐ์ธ๋ ๊ฒ์ ๋งํ๋ค.
์ฐจ์ด๊ฐ ์๋ค๋ฉด, property injection์ ๊ฒฝ์ฐ property์ access control์ internal
๋ก ์ด์ด๋์ด์ผ ํ๋ค๋ ์ ์ด๋ค. ๋ฐ๋๋ก method injection์ property์ access๋ private
ํ๊ฒ ๋ ์ ์๋ค. ํ์ง๋ง ํจ์ ๊ธธ์ด๊ฐ ๊ธธ์ด์ง๋ค. ๊ทธ๋์ ์ด ๋ถ๋ถ์ ๊ฐ์ธ์ ํ๋จ์ ๋งก๊ฒจ์ผ ํ๋ค.
๊ทธ๋ผ ์ด ๋ฐฉ์์ ๋จ์ ์ ๋ํด ์๊ฐํด๋ณด์. ์ผ๋จ ์ปดํ์ผ ํ์์ ์ฃผ์ ์ฌ๋ถ ํ์ธ์ด ๋ถ๊ฐํ๋ค. ์ด๊ฒ ๋งน์ ์ธ๋ฐ, ์ฌ์ค ๊ฐ๋ฐ์๋ ์ปดํ์ผ ํ์์ ๋๋ถ๋ถ ์กํ์ฃผ๋ฉด ๋กํ๋ค. ๋ฐํ์์ ์๋ฌ๋๋ฉด ์์ฐ ๋จธ๋ฆฌ์ํ๋ค.. ๊ทธ๋์ ์ด ๋ฐฉ์์ด ๋ง์์ ์๋ค์์๋ค.
Service Locator Pattern
๊ทธ ๋ค์์๋ ์ด๋ ์์ด ์๋ค. ๊ฒฐ๋ก ๋ถํฐ ๋งํ๋ฉด ์ด๋ ์์ Anti Pattern์ด๋ค. ์์ง ์ ๋๋ก ๊ณต๋ถํด๋ณด์ง ๋ชปํด ๋ด ์ธ์ด๋ก ์ดํด๋์ง๋ ์์๋ค. ์ถ๊ตฌ Tech Talk ๊ฒ์๊ธ์์ ๋ง๋๋ณด๊ฒ ๋ ๊ฑฐ๋ค.
๊ฐ๋จํ๊ฒ ๋งํด๋ณด์๋ฉด, ๋ชจ๋ ์์กด์ฑ์ ์๊ณ ์๋ Locator ๊ฐ์ฒด์ ์์กด์ฑ ์ฃผ์ ์ ์์กดํ๋ (?) ๋ฐฉ๋ฒ์ด๋ค. ๋ง์ด ์ฐธ.. ์ฝ๊ฒ ๋งํ๋ฉด ์ด ๋ ์ํํ โ์ผ์ผ ๋ ์์กด์ฑ์ข ํด๊ฒฐํด์ผ ํ๋๋ฐ, ๋ญ๋ก ๋ฃ์ด์ผ ํ๋?โ ๋ผ๊ณ ๋ฌผ์ด๋ณด๋ฉด ์ด๋ ์์ด ๋ต์ ์๋ ค์ฃผ๋ ๊ฑฐ๋ค. ๊ทธ๋ ๋ค ์ฝ๊ฐ์ ์ฑ๊ธํค๊ณผ ๊ฐ์ ๋์.. ์ ์ญ ๊ฐ์ฒด๋ค.
์ผ๋จ ํด๋น ๋ฐฉ๋ฒ์ ์ฌ์ฉํ์ ๋, ์ง๊ด์ ์ผ๋ก ๋ฐ์ํ๋ ๋ฌธ์ ๋ค์ ๋ค์๊ณผ ๊ฐ์ ๊ฒ๋ค์ด ์๋ค.
- ์์ฑ์๋ง ๋ณด๊ณ ์์กด์ฑ์ ํ์ ํ ์ ์๋ค.
- Locator์ ์์กดํ๊ฒ ๋๋ค.
- Class์์ ์์ฒญ์ ํ๋ ํํ์ด๊ธฐ ๋๋ฌธ์, ์ธ๋ถ์์ mock ๊ฐ์ฒด๋ฅผ ๊ฐ์ ๋ผ์์ ํ ์คํธ ํ๋ ๊ฒ์ด ๋ถ๊ฐํ๋ค.
Initialization Injection
๊ฐ์ฅ ๋ง์์ ๋๋ ๋ฐฉ์์ด๋ค. ์์ฑ์๋ง ๋ณด๊ณ ์์กด์ฑ์ ํ์ ํ ์ ์๋ค. ๋ค๋ง, A, B ๋๊ฐ ์ด์์ ๊ฐ์ฒด๊ฐ ์๋ก๋ฅผ ์์์ผ ํ๋ ์ํฉ์ด๋ผ๋ฉด ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค. ์ด๋ฐ ๊ฒฝ์ฐ๋ Property & Method injection์ ์ฌ์ฉํ๋ ๊ฒ์ด ๊น๋ํ ์ ์๋ค.
let a = A(b: )
let b = B(a: )
// ..? ์งํํ ์๊ฐ ์๋ค. ์ํ์ด๋ค..
let a = A()
let b = B()
a.b = b
b.a = a
// Property & Method injection์ด ๊น๋ํ๋ค.
์๊ธธ๋ก ๋น ์ก๋ค. ์ผ๋จ ์์ ์ฌ์ง์ ๊ฒฝ์ฐ๋ Code๋ก๋ง injection์ ํ๋ค๋ ๊ฐ์ ์์ ์งํํ ๊ฒ์ด๋ค. ํ์ง๋ง ์ด๋ ๊ฒ ํ๋ฉด Compile Time์ injection์ ํ์ธํ ์ ์๋ค.
๊ทธ๋์ ์ด๋ ๊ฒ ์ ๊ทผ ๋ชปํ๋๋ก ๋ง์ผ๋ฉด, ๋น๋ก์ ์ด๊ธฐํ ์๊ธฐ์ ์์กด์ฑ ์ฃผ์ ์ ๊ฐ์ ํ๋ฉด์ ํ๋ณตํ ์ํฉ์ ๋ง๋ค ์ ์๋ค.
๋ ๋์๊ฐ๋ฉด, ์ฝ๋๋ก๋ง ํ๋ฉด ์ด๋ ๊ฒ ์ถ์ํ ํด์ ์ฌ์ฉํ๋ฉด ๋ ํธํ๋ค.
ํ์ง๋ง ์ฐ๋ฆฌ๊ฐ ํด์ผ ํ๋ ๊ฒ์ Storyboard๋ฅผ ์ฌ์ฉํ๋ฉด์ initializer๋ก injection์ ํ๊ณ ์ถ์ ๊ฒ์ด๋ค! ์ด๋ป๊ฒ ํ์ง!
Storyboard?!
๊ธฐ์กด์ ์ฌ์ฉํ๋ ์น๊ตฌ๋ ์ด๋ฐ ๋ ์์ด์๋ค.
func instantiateInitialViewController() -> UIViewController?
๊ทธ๋์ ์ค์ ์ฝ๋๋ฅผ ๋ณด๋ฉด,
guard let controller = UIStoryboard(name: "Folder").instantiateInitialViewController() as? FolderViewController else {
return
}
controller.viewModel = FolderViewModel()
์ด๋ฐ์์ผ๋ก ์ฒ๋ฆฌํ๋ค. Bundle์์ ๋ก๋ํ ํ์ property & Method ์ฃผ์ ์ด ์ต์ ์ด์๋ค! ๊ทธ๋ฐ๋ฐ iOS 13๋ถํฐ ์๋ก์ด API๊ฐ ์ ๊ณต๋์๋ค.
New APIs!
์ฌ๊ธฐ์ ํ์ 3์ธ๋ฐฉ์ด iOS 13๋ถํฐ ์๋ก์๊ธด API์ด๋ค! ์ฌ๊ธฐ์ ๊ฐ์ฅ ๊ด์ฌ์ด ๊ฐ๋ ๋ ์์ ์ด๋ ์์ด๋ค.
@MainActor func instantiateViewController<ViewController>(identifier: String, creator: ((NSCoder) -> ViewController?)? = nil) -> ViewController where ViewController : UIViewController
์ @MainActor
๋ ๋ญ์ง..? ์ถํ ์์๋ณด์. ์ผ๋จ์ identifier์ coder๋ฅผ ๋ฐ์ ViewController๋ฅผ ๋ฆฌํดํ๋ ํด๋ก์ ๋ฅผ ์ ๊ณตํ๋ฉด ๋ด๊ฐ ์ํ๋ ViewController๋ฅผ ์ฃผ๋ ๋
์์ด๋ค. identifier๋ storyboard์์ ํน์ VC๋ฅผ ํด๋ฆญํ์ ๋ ์ ํ ์ ์๋ ๋ฌธ์์ด์ ๋งํ๋ค.
๋ฐ๋ก ์ด๋ ์์ด๋ค. ์ ์ด๋ ์์ด ์์ผ๋ฉด Storyboard๋ฅผ ์ฌ์ฉํจ์๋ ์์กด์ฑ ์ฃผ์ ์ด ๊ฐ๋ฅํ ๊น?
Dependency Injection with Storyboard
import UIKit
class FolderViewController {
var viewModel: T
init(viewModel: T) {
self.viewModel = viewModel
super.init(nibName: nil, bundle: nil) // code๋ก VC๋ฅผ ์์ฑํ๋ ๊ฒฝ์ฐ nib, bundle ๋ชจ๋ ๋ถํ์
}
init?(coder: NSCoder, viewModel: T) {
self.viewModel = viewModel
super.init(coder: coder)
}
@available(*, unavailable, renamed: "init(coder:viewModel:)")
required init?(coder: NSCoder) {
fatalError("Invalid way of decoding this ViewController")
}
}
์ผ๋จ ์ด๋ ๊ฒ coder์ viewModel ๋๋ค ๋ฐ์ ์ ์๊ฒ ๋ง๋ค์ด ๋์. ๊ทธ๋ฆฌ๊ณ ์ค์ ์ด๋ ์์ instanceํ ํ๋ ๊ณณ์์ ๋ค์๊ณผ ๊ฐ์ด ์ฌ์ฉํ์.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewModel = storyboard.instantiateInitialViewController { coder -> FolderViewController in
let viewModel = DefaultRoomListViewModel()
return .init(coder: coder, viewModel: viewModel) ?? FolderViewController(viewModel: viewModel)
}
์์ ๋๋์ด ์ฐ๋ฆฌ๊ฐ ์ํ๋ ๋ชฉํ๋ฅผ ๋ฌ์ฑํ๋ค! ์ฌ๊ธฐ์ generic๊น์ง ์ถ๊ฐํ์ฌ ์ฌ์ฉํ๋ค๋ฉด, ๋ค์๊ณผ ๊ฐ์ด ๋๊ฒ ๋ค.
import UIKit
class DefaultDIViewController<T>: DefaultViewController {
var viewModel: T
init(viewModel: T) {
self.viewModel = viewModel
super.init(nibName: nil, bundle: nil) // code๋ก VC๋ฅผ ์์ฑํ๋ ๊ฒฝ์ฐ nib, bundle ๋ชจ๋ ๋ถํ์
}
init?(coder: NSCoder, viewModel: T) {
self.viewModel = viewModel
super.init(coder: coder)
}
@available(*, unavailable, renamed: "init(coder:viewModel:)")
required init?(coder: NSCoder) {
fatalError("Invalid way of decoding this ViewController")
}
}