티스토리 뷰
곰튀김님의 Inversion 세션 (let us go summer 2020 => 2:18:19 쯤 나와요! 👍) 을 보다가
Dependency Container를 공부해보고자합니다.
Dependency Injection의 개념 & SOLID의 D인 의존관계 역전 원칙(DIP)을 어떻게 따르게 해줄 수 있을지는
위의 세션 또는 이 글 을 참고해주세요
이 포스팅은
Dependency Container 또는
Dependecny Inject Container 또는
IOC Container (Inversion Of Control Container) 에 대해서만
살펴보겠습니다.
[1] Dependency Container란 무엇인가?!
의존성 주입을 해줄때는 밖에서 인스턴스를 만들어서 주입해줍니다. (참고: 의존성 주입(Dependency Injection) 을 해주는 세가지 방법 )
밖에서 인스턴스를 만들어서 주입해주는 곳은 앱에서 여러군데 입니다. 즉 인스턴스를 만드는 위치가 분산되어있습니다.
근데 Container라는 것이 있고 이 친구가 모든 인스턴스를 다 가지고 있고 다 관리를 한다고 생각해봅시다.
Container에 앞으로 내가 사용할 모든 인스턴스를 다 만들어서 등록해두고 => register 라는 용어를 씀
필요한 시점에 Container에게 특정타입의 인스턴스를 달라고 하면 Container가 꺼내주는 => resolve 라는 용어를 씀
것입니다.
예를들어 아래 코드 같은 모양이라고 할 수 있겠습니다.
/// Copyright (c) 2018 Razeware LLC | |
/// 출처: https://www.raywenderlich.com/17-swinject-tutorial-for-ios-getting-started | |
import XCTest | |
import Swinject | |
@testable import Bitcoin_Adventurer | |
class BasicTests: XCTestCase { | |
private let container = Container() | |
// MARK: - Boilerplate methods | |
override func setUp() { | |
super.setUp() | |
// 1 | |
container.register(Currency.self) { _ in .USD } | |
container.register(CryptoCurrency.self) { _ in .BTC } | |
// 2 | |
container.register(Price.self) { resolver in | |
let crypto = resolver.resolve(CryptoCurrency.self)! | |
let currency = resolver.resolve(Currency.self)! | |
return Price(base: crypto, amount: "999456", currency: currency) | |
} | |
// 3 | |
container.register(PriceResponse.self) { resolver in | |
let price = resolver.resolve(Price.self)! | |
return PriceResponse(data: price, warnings: nil) | |
} | |
} | |
override func tearDown() { | |
super.tearDown() | |
container.removeAll() | |
} | |
// MARK: - Tests | |
func testPriceResponseData() { | |
let response = container.resolve(PriceResponse.self)! | |
XCTAssertEqual(response.data.amount, "999456") | |
} | |
} |
이니셜라이저를 통해서 인스턴스를 초기화하는 코드가
Container에 register해줄 때 한번만 쓰이게 되는 것을 볼 수 있습니다.
이제 사용하는 쪽에서는 이니셜라이저가 아니라 resolve()를 통해 인스턴스를 만들게 되는 것이죠

[2] Dependency Container는 왜 필요한가?!
주입해줘야하는 게 많아서 이니셜라이저의 파라미터가 엄청 많은데
아래와 같이 중복코드가 있을때 유용합니다.

저 코드는 Container를 쓰면 이렇게 간단히 변경가능하기 때문입니다.
class ClientA {
func someMethod() {
let dataFetcher = Container.shared.resolve(DataFetcher.self)
}
}
class ClientB {
func someMethod() {
let dataFetcher = Container.shared.resolve(DataFetcher.self)
}
}
위의 예제는 Swinject(Dependency Container 관련 라이브러리)를 소개하는 블로그에서 가져왔습니다.
resolve(DataFetcher.self) 를 할때마다 새로운 DataFetcher 인스턴스를 리턴한다고 합니다. 찍어보면 주소값이 다른 것을 확인할 수 있어요

만약 DataFetcher를 하나의 인스턴스로 쓰고 싶다면 Object Scope로 설정해줄 수있다고 하네요
(다른 라이브러리들은 어떤 지 확인해봐야겠네요-!)
[3] Dependency Container의 내부구현은 대충 어떻게 될까?!
많은 것을 고려안하고 완전완전 슈퍼심플한 Container를 만들어본다면 이렇게 되겠네요
class DIContainer { | |
static let shared = DIContainer() | |
private var dependencies = [String: Any]() | |
private init() {} | |
func register<T>(_ dependency: T) { | |
let key = String(describing: type(of: T.self)) | |
dependencies[key] = dependency | |
} | |
func resolve<T>() -> T { | |
let key = String(describing: type(of: T.self)) | |
let dependency = dependencies[key] | |
precondition(dependency != nil, "\(key)는 register되지 않았어어요. resolve 부르기전에 register 해주세요") | |
return dependency as! T | |
} | |
} |
만약 모델이 이렇게 구성되어있다면
protocol Eatable { | |
var calorie: Int { get } | |
} | |
protocol CityPresentable { | |
var code: String { get } | |
var name: String { get } | |
} | |
struct Pizza: Eatable { | |
var calorie: Int { | |
return 300 | |
} | |
} | |
struct Seoul: CityPresentable { | |
var code: String { | |
return "02" | |
} | |
var name: String { | |
return "서울" | |
} | |
} | |
struct FoodTruck { | |
let food: Eatable | |
let city: CityPresentable | |
init(food: Eatable, city: CityPresentable) { | |
self.food = food | |
self.city = city | |
} | |
} |
아래와 같이 register, resolve 해줄 수 있겠습니다.
class AppDelegate: UIResponder, UIApplicationDelegate { | |
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { | |
registerDependencies() | |
return true | |
} | |
private func registerDependencies() { | |
DIContainer.shared.register(Pizza()) | |
DIContainer.shared.register(Seoul()) | |
let pizza: Pizza = DIContainer.shared.resolve() | |
let seoul: Seoul = DIContainer.shared.resolve() | |
DIContainer.shared.register(FoodTruck(food: pizza, | |
city: seoul)) | |
} | |
} | |
class ViewController: UIViewController { | |
let foodTruck: FoodTruck = DIContainer.shared.resolve() | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
print(foodTruck) | |
} | |
} |
그리고 Swift 5.1에서 추가된 Property Wrapper를 만들어주면
@propertyWrapper | |
class Dependency<T> { | |
let wrappedValue: T | |
init() { | |
self.wrappedValue = DIContainer.shared.resolve() | |
} | |
} |
더 간결해집니다
class ViewController: UIViewController { | |
@Dependency var foodTruck: FoodTruck | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
print(foodTruck) | |
} | |
} |
Reference
www.youtube.com/watch?v=h2FBZcLBeq0
medium.com/flawless-app-stories/ios-dependency-injection-using-swinject-9c4ceff99e41
iOS Dependency Injection Using Swinject
In this article, you’ll find everything you should know about iOS Dependency Injection with Swinject.
medium.com
https://www.raywenderlich.com/17-swinject-tutorial-for-ios-getting-started
Swinject Tutorial for iOS: Getting Started
In this tutorial, you will explore Dependency Injection (DI) through Swinject, a Dependency Injection framework written in Swift. Dependency Injection is an approach to organizing code so that its dependencies are provided by a different object, instead of
www.raywenderlich.com
'🍏 > Architecture, DesignPattern' 카테고리의 다른 글
[iOS] Coordinator 패턴 글 모음 (1) | 2021.11.17 |
---|---|
[CleanSwift] 클린스위프트 템플릿 살펴보기 (0) | 2021.07.02 |
[Clean Architecture] iOS Clean Architecture + MVVM 개념과 예제 (2) | 2020.07.17 |
[Design Pattern] Repository패턴이란 (0) | 2020.07.17 |
[CleanSwift] 클린스위프트(Clean Swift)에 대해 알아보자 (1) | 2020.04.29 |
- Total
- Today
- Yesterday
- cocoapod
- 장고 Custom Management Command
- 장고 URL querystring
- flutter 앱 출시
- PencilKit
- Dart Factory
- 구글 Geocoding API
- 플러터 얼럿
- 플러터 싱글톤
- Python Type Hint
- github actions
- ribs
- flutter deep link
- METAL
- Django Heroku Scheduler
- DRF APIException
- Flutter Text Gradient
- Flutter getter setter
- drf custom error
- Django Firebase Cloud Messaging
- flutter build mode
- Flutter Spacer
- Flutter 로딩
- Sketch 누끼
- Django FCM
- Watch App for iOS App vs Watch App
- flutter dynamic link
- Flutter Clipboard
- ipad multitasking
- SerializerMethodField
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |