Swift

Swinject로 DI(Dependency Injection)패턴 적용하기

TaeJoongYoon 2019. 4. 11. 00:03

DI에 대해선 많이 들어봤지만 적용은 안하다가 이번 프로젝트를 진행하면서 적용해야겠다는 생각이 들어서
Swift DI 라이브러리인 Swinject로 DI 패턴을 적용하게 됐다!

Swinject로 DI(Dependency Injection)패턴 적용하기

DI(Dependency Injection, 의존성 주입)이란 뜻 그대로 의존 객체를 외부에서 생성하여 넘겨주는 것을 의미합니다.

아래와 같은 상황이 있다고 생각해봅시다.

 

// Case 1
class A {
  var object = B()

  init() {

  }
}

class B {

}

// Case 2
class A {
  var object: B

  init(object: B) {
    self.object = object
  }
}

class B {

}

 

Case 1 같은 경우, Class A에서 object라는 Class B 객체를 의존할 때 A가 직접 생성.
Case 2 같은 경우, Class A에서 object라는 Class B 객체를 의존할 때 외부에서 생성하여 주입.

이렇게 Case 2같은 경우를 의존성을 주입했다고 말합니다.

 

하지만 뭔가 깔끔하지 않죠. 의존하는 객체가 많아질수록 initialize에는 많은 parameter가 들어가게 되고
이후, 유연한 코드가 되지 않을 가능성이 높아집니다.

 

그래서 DI Framework들은 하나의 Container를 가지고 이 Container에서 의존성 객체를 생성하여 주입해주는 방식을 가집니다.

아래는 Swinject를 활용한 DI패턴입니다.
각 ViewController에 ViewModel을 주입해주는 코드입니다

 

let container = Container()

// register는 등록
container.register(SplashViewModel.self) { r in SplashViewModel() }
container.register(LoginViewModel.self) { r in LoginViewModel() }
container.register(SignUpViewModel.self) { r in SignUpViewModel() }

// resolve는 이용
container.register(SplashViewController.self) { r in
      let controller = SplashViewController()
      controller.viewModel = r.resolve(SplashViewModel.self)
      return controller
    }
container.register(LoginViewController.self) { r in
  let controller = LoginViewController()
  controller.viewModel = r.resolve(LoginViewModel.self)
  return controller
}
container.register(SignUpViewController.self) { r in
  let controller = SignUpViewController()
  controller.viewModel = r.resolve(SignUpViewModel.self)
  return controller
}

// viewController resolve in AppDelegate.swift
self.window?.rootViewController = container.resolve(SplashViewController.self)

 

저 같은 경우는 AppDelegate.swift에서 하나의 Container를 만들어 앱 전체 ViewController들의 의존성을 관리해줬습니다.

 

Swinject는 쉽게 생각하면
register(key) -> 등록
resolve(key) -> 이용
이라고 생각하면 될 것 같습니다 :)

 

이렇게 DI패턴을 적용하니 매 순간순간 객체를 생성 안해도 되고 Container에서 resolve만 해도 되서 간편하고
코드 자체가 깔끔하고 좀 더 유연한 코드가 되는 것 같습니다.
테스트에도 용이한 코드가 된다는데 아직 테스트코드를 작성해보지는 않아서 나중에 작성해보면서 이 장점을 느껴보겠습니다!