Bibi's DevLog ๐Ÿค“๐ŸŽ

[iOS] DIContainer : ์˜์กด์„ฑ ์ฃผ์ž…์„ ์œ„ํ•œ ๊ฐ์ฒด ๊ด€๋ฆฌํ•˜๊ธฐ ๋ณธ๋ฌธ

๐Ÿ“ฑ๐ŸŽ iOS

[iOS] DIContainer : ์˜์กด์„ฑ ์ฃผ์ž…์„ ์œ„ํ•œ ๊ฐ์ฒด ๊ด€๋ฆฌํ•˜๊ธฐ

๋น„๋น„ bibi 2022. 12. 5. 15:28

์ฐธ๊ณ  ๋ฌธ์„œ :

[DI] DI Container, IOC Container ๊ฐœ๋…๊ณผ ์˜ˆ์ œ

DIContainer๋ž€?

  • DI = Dependency Injection ์˜์กด์„ฑ ์ฃผ์ž…
    • ๋ฐ–์—์„œ ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค์–ด ์ฃผ์ž…ํ•ด์ฃผ๋Š” ๊ฒƒ
  • Container : ๋ชจ๋“  ์ธ์Šคํ„ด์Šค๋ฅผ ๊ฐ€์ง€๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ๊ฐ์ฒด.
    • register : ๋‚ด๊ฐ€ ์‚ฌ์šฉํ•  ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค์–ด ๋“ฑ๋ก
    • resolve : ํ•„์š”ํ•œ ์ธ์Šคํ„ด์Šค๋ฅผ ๋‹ฌ๋ผ๊ณ  ์š”์ฒญ

์ฆ‰ DIContainer๋Š” ์˜์กด์„ฑ ์ฃผ์ž…์— ํ•„์š”ํ•œ ๊ฐ์ฒด๋“ค์„ ์ €์žฅํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.

  • ์™œ ํ•„์š”ํ•œ๊ฐ€?
    • ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์ค‘๋ณต์ฝ”๋“œ๊ฐ€ ๋งŽ์•„์งˆ ๋•Œ
    • ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ํ•„์š”ํ•œ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ๋งŽ์„ ๋•Œ

์‚ฌ์šฉ๋ฐฐ๊ฒฝ

ํ”„๋กœ์ ํŠธ๋ฅผ ํ•˜๋ฉฐ ์–ด๋–ค ๊ฐ์ฒด๊ฐ€ ํ•„์š”ํ•  ๋•Œ, ์ƒ์„ฑ์ž๋กœ ์•ฑ์˜ ์—ฌ๊ธฐ์ €๊ธฐ์—์„œ ๋ฌด๋ถ„๋ณ„ํ•˜๊ฒŒ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์žˆ์—ˆ๋‹ค.

๋˜, ์ด๋ฏธ ์ƒ์„ฑํ–ˆ๋˜ ์‹œ์ ๊ณผ ์œ„์น˜๋ฅผ ๋ชจ๋ฅด๊ณ  ๊ฐ™์€ ๊ฐ์ฒด๋ฅผ ์—ฌ๋Ÿฌ ๋ฒˆ ์ƒ์„ฑํ–ˆ๋Š”๋ฐ ์ด๋Š” ์ž์›์„ ๋‚ญ๋น„ํ•˜๋Š” ํ–‰์œ„๋ผ๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค.

๊ทธ๋ž˜์„œ ๋‚˜๋Š” ๊ฐ์ฒด๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐฉ์‹์„ ํ†ต์ผํ•˜๊ณ , ํ•œ ๋ฒˆ ๋งŒ๋“  ๊ฐ์ฒด๋ฅผ ์žฌ์‚ฌ์šฉํ•˜๊ธฐ ์‰ฝ๊ฒŒ ๋งŒ๋“ค๊ณ  ์‹ถ์–ด์„œ DIContainer๋ฅผ ์ ์šฉํ•ด ๋ณด์•˜๋‹ค.

์ ์šฉ๋ฐฉ๋ฒ•

DIContainer์˜ ๊ธฐ๋ณธ ๊ฐœ๋…์€ ๊ฐ„๋‹จํ•˜๋‹ค.

์ƒ์„ฑ๋œ ๊ฐ์ฒด๋ฅผ ์ €์žฅํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.

import UIKit

class Container {

    let environment: ContainerEnvironment
    private var registeredObjects: [String: Any] = [:]

    init(environment: ContainerEnvironment) {
        self.environment = environment
    }

    // model, viewcontroller, coordinator๋ฅผ ์ƒ์„ฑ ์‹œ์ ์— ๋“ฑ๋กํ•จ
    func register<T>(_ object: T) {
        let key = String(describing: T.self)
        registeredObjects[key] = object
    }

    // let value: Type = container.resolve() ๋กœ ์‚ฌ์šฉ
    func resolve<T>() -> T? {
        let key = String(describing: T.self)
        guard let object = registeredObjects[key],
              let object = object as? T else {
            print("โš ๏ธ\(key)๋Š” register๋˜์ง€ ์•Š์Œ")
            return nil
        }
        return object
    }

}
  • private var registeredObjects: [String: Any]
    • ์ปจํ…Œ์ด๋„ˆ์— ๋“ฑ๋ก๋œ ๊ฐ์ฒด๋“ค์„ ์ €์žฅํ•˜๋Š” ๋”•์…”๋„ˆ๋ฆฌ.
    • ํ‚ค : ๊ฐ์ฒด ์ด๋ฆ„, ๊ฐ’ : ๊ฐ์ฒด
  • func register<T>(object:)
    • ์ปจํ…Œ์ด๋„ˆ์— ๊ฐ์ฒด๋ฅผ ๋“ฑ๋กํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๋ฉ”์„œ๋“œ
  • func resolve<T>() -> T?
    • ์ปจํ…Œ์ด๋„ˆ์— ๋“ฑ๋ก๋œ ๊ฐ์ฒด๋ฅผ ๊บผ๋‚ด ์“ฐ๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๋ฉ”์„œ๋“œ
    • let value: Type = container.resolve() ์™€ ๊ฐ™์ด ํƒ€์ž…์„ ๋ช…์‹œํ•˜๊ณ  ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.
    • ์š”์ฒญํ•œ ๊ฐ์ฒด๊ฐ€ ๋“ฑ๋ก๋œ ๊ฐ์ฒด๋ผ๋ฉด ๊บผ๋‚ด์ฃผ๊ณ , ๋“ฑ๋ก๋˜์ง€ ์•Š์€ ๊ฐ์ฒด๋ผ๋ฉด nil์„ ๋ฐ˜ํ™˜

์‚ฌ์šฉ๋ฐฉ๋ฒ•

  • ์•ฑ ๋‚ด์—์„œ ์–ด๋–ค ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ๋•Œ, ์ƒ์„ฑํ•œ ๊ฐ์ฒด๋ฅผ Container์— ๋“ฑ๋กํ•œ๋‹ค.
  • let environment = LoginModelEnvironment { [weak self] completion in self?.container.environment.oAuthService.requestCode(completion: { result in completion(result) }) } let model = LoginModel(environment: environment) // ์ƒ์„ฑ let viewController = LoginViewController(model: model) container.register(model) // ๋“ฑ๋ก container.register(viewController)
  • ์ดํ›„ ๊ทธ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•  ๋•Œ ์ƒˆ๋กœ ์ƒ์„ฑํ•˜์ง€ ์•Š๊ณ , ์ปจํ…Œ์ด๋„ˆ์— ๋“ฑ๋กํ–ˆ๋˜ ๊ฐ์ฒด๋ฅผ ๊บผ๋‚ด์“ฐ๋ฉด ๋œ๋‹ค.
  • guard let issueCoordinator: IssueCoordinator = container.resolve() else { return } issueCoordinator.fetchIssues()

์‘์šฉ - DIContainer๋กœ ์ฝ”๋””๋„ค์ดํ„ฐ&๋ทฐ์ปจํŠธ๋กค๋Ÿฌ ์Œ ๊ด€๋ฆฌํ•˜๊ธฐ

์ž์„ธํ•œ ๋‚ด์šฉ์€ ์—ฌ๊ธฐ ๋ฅผ ์ฐธ์กฐํ•ด ์ฃผ์„ธ์š”.

์ปจํ…Œ์ด๋„ˆ๋Š” ์•ฑ ๋‚ด์˜ ๊ฐ์ฒด๋“ค์„ ๊ด€๋ฆฌํ•ด ์ฃผ๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.

๊ทธ๋ž˜์„œ ์ดํ›„ ์ฝ”๋””๋„ค์ดํ„ฐ ํŒจํ„ด์„ ์ ์šฉํ•˜๋‹ค๊ฐ€, ์ฝ”๋””๋„ค์ดํ„ฐ์™€ ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ ์Œ์„ ํ•จ๊ป˜ ๊ด€๋ฆฌํ•ด์•ผ ํ•  ํ•„์š”๊ฐ€ ์ƒ๊ฒผ์„ ๋•Œ ์ด ์ž‘์—…์„ ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ํ•˜๋„๋ก ํ–ˆ๋‹ค.

  • ํ‚ค๊ฐ€ ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ์ด๊ณ , ๊ฐ’์ด ์ฝ”๋””๋„ค์ดํ„ฐ์ธ registeredViewControllerCoordinator ๋ฅผ ๋งŒ๋“ ๋‹ค.
  • ์ปจํ…Œ์ด๋„ˆ์˜ register(object:)์‹œ์ ์—์„œ, ๋“ฑ๋กํ•˜๋Š” ๊ฐ์ฒด๊ฐ€ UIViewController์ผ ๋•Œ ์Œ์ด ๋˜๋Š” ์ฝ”๋””๋„ค์ดํ„ฐ๋ฅผ ์ฐพ์•„ registeredViewControllerCoordinator ์— ๋“ฑ๋กํ•œ๋‹ค.
  • func resolvePair(of viewController: UIViewController) -> Coordinator? : ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ์ฃผ์–ด์กŒ์„ ๋•Œ, ๊ทธ ์ง์ด ๋˜๋Š” ์ฝ”๋””๋„ค์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•ด ์ค€๋‹ค.
class Container {

    let environment: ContainerEnvironment
    private var registeredObjects: [String: Any] = [:]
    private var registeredViewControllerCoordinator: [UIViewController : Coordinator] = [:]

    init(environment: ContainerEnvironment) {
        self.environment = environment
    }

    // model, viewcontroller, coordinator๋ฅผ ์ƒ์„ฑ ์‹œ์ ์— ๋“ฑ๋กํ•จ
    func register<T>(_ object: T) {
        let key = String(describing: T.self)
        registeredObjects[key] = object
        if let viewControllerObject = object as? UIViewController {
            registerPairCoordinator(with: viewControllerObject)
        }
    }

    // let value: Type = container.resolve() ๋กœ ์‚ฌ์šฉ
    func resolve<T>() -> T? {
        let key = String(describing: T.self)
        guard let object = registeredObjects[key],
              let object = object as? T else {
            print("โš ๏ธ\(key)๋Š” register๋˜์ง€ ์•Š์Œ")
            return nil
        }
        return object
    }

    private func registerPairCoordinator(with viewController: UIViewController) {
        let viewControllerName = String(describing: type(of: viewController))

        let allViewController = ViewControllerCoordinator.allCases
        for oneCase in allViewController {
            let oneCaseName = String(describing: oneCase) // Enum case์˜ ์ด๋ฆ„์„ String์œผ๋กœ ๋ณ€ํ™˜
            if oneCaseName == viewControllerName {
                let coordinatorName = oneCase.rawValue
                guard let coordinator = registeredObjects[coordinatorName],
                      let castedCoordinator = coordinator as? Coordinator else {
                    return
                }
                registeredViewControllerCoordinator[viewController] = castedCoordinator
            }
        }
    }

    func resolvePair(of viewController: UIViewController) -> Coordinator? {
        return registeredViewControllerCoordinator[viewController]
    }
}

๊ฐœ์„ ์ 

DIContainer๋Š” ์›๋ฆฌ๊ฐ€ ๋ณต์žกํ•˜์ง€ ์•Š์•„์„œ ์ ์šฉ์ด ์‰ฌ์šฐ๋ฉด์„œ๋„, ๊ทธ ํšจ๊ณผ๋ฅผ ๋ฐ”๋กœ ์ฒด๊ฐํ•  ์ˆ˜ ์žˆ์–ด์„œ ์ข‹์•˜๋‹ค!

์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋งŒ๋“ค๊ณ  ๋ฆฌํŒฉํ† ๋ง์„ ํ•˜๋‹ˆ ๊ฐ์ฒด ์ƒ์„ฑ ์ฝ”๋“œ๊ฐ€ ์—ฌ๊ธฐ์ €๊ธฐ์„œ ๋‚จ๋ฐœ๋˜์ง€ ์•Š๊ณ ,

์‚ฌ์šฉํ•  ๋•Œ๋„ ํ•œ ๋ฒˆ ๋งŒ๋“  ๊ฐ์ฒด๋ฅผ ์—ฌ๋Ÿฌ ๋ฒˆ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์–ด์„œ ์ข‹์•˜๋‹ค.