-
Notifications
You must be signed in to change notification settings - Fork 3
의존성 주입(Dependency Injection, DI)
현재 객체가 다른 객체와 상호작용(참조) 하고 있다면 현재 객체는 다른 객체에 의존성을 가진다.
- 하나의 객체가 다른 객체의 생성을 제어하기 때문에 두 객체 간에는 긴밀한 결합이 생긴다.
- 하나의 모듈이 바뀌면 의존한 다른 모듈까지 변경되어야 한다.
- 두 객체 사이의 의존성이 존재하면 Unit Test 작성이 어려워진다.
의존성 주입은 객체에 인스턴스 변수를 제공하는 것을 의미한다. 의존성을 생성할 책임을 가지고 객체를 처리하는 대신 객체에 의존성을 주입하는 것이다.
class AlgorithmManager {
var algorithm: AlgorithmType? = Algorithm()
}
여기서 algorithm
은 인스턴스화를 사용하여 생성되었다. 이외에도 이니셜라이저, lazy 프로퍼티 등으로 생성될 수 있다. 요점은 의존성 주입을 사용하지 않을 경우 AlgorithmManager
클래스가 algorithm
의 인스턴스 생성을 담당한다.
그렇기 때문에 AlgorithmManager
는 Algorithm
클래스의 동작과 인스턴스화에 대해서도 알고 있다.
let algorithmManager = AlgorithmManager()
algorithmManager.algorithm = Algorithm()
여기서는 Algorithm
의 인스턴스를 AlgorithmManager
인스턴스에 주입하였다. 의존성을 주입하지 않을 경우와 결과가 동일해 보일 수 있지만, algorithm
을 주입함으로써 AlgorithmManager
는 Algorithm
의 인스턴스화하는 방법을 알지 못한다.
- 결합도 감소: 의존 관계 설정이 컴파일시가 아닌 실행시에 이루어져 모듈들간의 결합도를 낮출 수 있다.
- 코드 재사용성 증가: 코드 재사용을 높여서 작성된 모듈을 여러 곳에서 코드의 수정 없이 사용 가능하다.
- 투명성 향상: 객체에 의존성을 주입함으로써 클래스 또는 구조체의 책임과 요구사항이 보다 명확하고 투명해진다.
- 관심의 분리:
AlgorithmManager
클래스는Algorithm
을 인스턴스화할 책임이 없다. 그러므로 이 작업을 수행하는 방법을 알 필요가 없다. - 테스트 편의성 증가: 의존성 주입을 통해 객체의 의존성을 모의 객체로 대체할 수 있으므로 동작을 분리하고 Unit Test를 보다 쉽고 덜 복잡하게 할 수 있다.
class MockAlgorithm: AlgorithmType { ... }
let algorithmManager = AlgorithmManager()
algorithmManager.algorithm = MockAlgorithm()
- 이니셜라이저를 통한 의존성 주입(initializer injection)
- 프로퍼티를 이용한 의존성 주입(property injection)
- 메서드에서 의존성 주입(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) { ... }
}
여기서 algorithm
은 AlgorithmManager
의 프로퍼티가 아니다. 대신 recommendProduct(_:)
메서드의 인수로 주입된다. AlgorithmManager
는 algorithm
에 대한 제어권을 잃어버렸지만 대신 메서드 의존성 주입 유형은 유연성을 제공한다. 그리고 algorithm
에 대한 참조를 보유하지 않으므로 복잡성을 줄일 수 있다.