Skip to content

의존성 주입(Dependency Injection, DI)

Youngjun Choi edited this page Feb 1, 2019 · 6 revisions

 

객체 의존성

 

현재 객체가 다른 객체와 상호작용(참조) 하고 있다면 현재 객체는 다른 객체에 의존성을 가진다.

 

의존성이 위험한 이유

 

  • 하나의 객체가 다른 객체의 생성을 제어하기 때문에 두 객체 간에는 긴밀한 결합이 생긴다.
  • 하나의 모듈이 바뀌면 의존한 다른 모듈까지 변경되어야 한다.
  • 두 객체 사이의 의존성이 존재하면 Unit Test 작성이 어려워진다.

 

의존성 주입이란?

 

의존성 주입은 객체에 인스턴스 변수를 제공하는 것을 의미한다. 의존성을 생성할 책임을 가지고 객체를 처리하는 대신 객체에 의존성을 주입하는 것이다.

 

without DI vs with DI

 

class AlgorithmManager {
    var algorithm: AlgorithmType? = Algorithm() 
}

여기서 algorithm 은 인스턴스화를 사용하여 생성되었다. 이외에도 이니셜라이저, lazy 프로퍼티 등으로 생성될 수 있다. 요점은 의존성 주입을 사용하지 않을 경우 AlgorithmManager 클래스가 algorithm의 인스턴스 생성을 담당한다.

그렇기 때문에 AlgorithmManagerAlgorithm 클래스의 동작과 인스턴스화에 대해서도 알고 있다.

let algorithmManager = AlgorithmManager()
algorithmManager.algorithm = Algorithm()

여기서는 Algorithm의 인스턴스를 AlgorithmManager 인스턴스에 주입하였다. 의존성을 주입하지 않을 경우와 결과가 동일해 보일 수 있지만, algorithm을 주입함으로써 AlgorithmManagerAlgorithm의 인스턴스화하는 방법을 알지 못한다.

 

의존성 주입의 이점

 

  • 결합도 감소: 의존 관계 설정이 컴파일시가 아닌 실행시에 이루어져 모듈들간의 결합도를 낮출 수 있다.
  • 코드 재사용성 증가: 코드 재사용을 높여서 작성된 모듈을 여러 곳에서 코드의 수정 없이 사용 가능하다.
  • 투명성 향상: 객체에 의존성을 주입함으로써 클래스 또는 구조체의 책임과 요구사항이 보다 명확하고 투명해진다.
  • 관심의 분리: AlgorithmManager 클래스는 Algorithm을 인스턴스화할 책임이 없다. 그러므로 이 작업을 수행하는 방법을 알 필요가 없다.
  • 테스트 편의성 증가: 의존성 주입을 통해 객체의 의존성을 모의 객체로 대체할 수 있으므로 동작을 분리하고 Unit Test를 보다 쉽고 덜 복잡하게 할 수 있다.
class MockAlgorithm: AlgorithmType { ... }
let algorithmManager = AlgorithmManager()
algorithmManager.algorithm = MockAlgorithm()

 

의존성 주입의 유형

 

  1. 이니셜라이저를 통한 의존성 주입(initializer injection)
  2. 프로퍼티를 이용한 의존성 주입(property injection)
  3. 메서드에서 의존성 주입(method injection)

 

Initializer injection

초기화 중에 전달된 의존성은 변경할 수 없다.

class AlgorithmManager {
    private let algorithm: AlgorithmType
    
    init(algorithm: AlgorithmType) {
        self.algorithm = algorithm
    }
}
let algorithm = Algorithm()
let algorithmManager = AlgorithmManager(algorithm: algorithm)

algorithm 프로퍼티를 설정하는 유일한 방법은 초기화하는 동안 인수로 전달하는 것이다. 이점으론 algorithm 프로퍼티를 변경할 수 없다는 것이다.

 

Property Injection

internal 또는 public 접근 제어자를 갖는 프로퍼티를 선언하여 의존성을 주입할 수도 있다.

class AlgorithmManager {
    var algorithm: AlgorithmType?
}
let algorithmManager = AlgorithmManager()
algorithmManager.algorithm = Algorithm()

이 방법은 편리하지만 의존성을 수정하거나 대체할 수 있다는 점에서 약점이 있다. 즉, 의존성은 불변이 아니다.

 

Method Injection

의존성을 인수로 받아들이는 메서드를 선언함으로써 의존성을 필요할 때마다 주입할 수도 있다.

class AlgorithmManager {
    func recommendProduct(_ algorithm: AlgorithmType) { ... }
}

여기서 algorithmAlgorithmManager의 프로퍼티가 아니다. 대신 recommendProduct(_:) 메서드의 인수로 주입된다. AlgorithmManageralgorithm에 대한 제어권을 잃어버렸지만 대신 메서드 의존성 주입 유형은 유연성을 제공한다. 그리고 algorithm에 대한 참조를 보유하지 않으므로 복잡성을 줄일 수 있다.

 


 

참고

 

Wikipedia

Nuts and Bolts of Dependency Injection in Swift

Dependency Injection in Swift 

Clone this wiki locally