"의존성 주입"이란,

객체가 의존관계에 있을 때 "직접 생성"하는 것이 아닌, 

DIP를 적용하여 "외부에서 제공"받는 패턴을 말한다.

 

한 번에 이해하기 어려우니 천천히 알아보자.

 

 

Dependency

대부분의 프로그램에서 여러 객체들은 협력하게 되기 때문에, 객체 사이의 의존성은 존재하게 된다. 

 

 

예를 들어, User의 정보를 Networking을 통해 가져와 이를 화면에 출력시키는 객체가 있다 하자.

class UserRepository {
    func fetchUser() -> User {
        ...
        return User(name: "Jung", id: "111")
    }
}

class UsersPresentation {
    private let usersRepository = UserRepository()
    
    func printUserInfo() {
        print(usersRepository.fetchUser())
    }
}

이러한 상황에서 UsersViewControllerUsersRepository에 의존성이 생기게 된다. 

(UsersViewControllerUsersRepository에 의존한다.)

객체 간의 의존성은 최소화되어야 하는데, 

특정 객체(A)의 변경이 의존 관계의 객체(B)에 전파되며,

B를 의존하는 객체에까지 변경이 전파되는 "의존성 전이"가 일어날 수 있기 때문이다.

 

Compile Time Dependency

위의 코드에서의 의존성은 "Compile Time Dependency"라 부르는데, 

Compile Time에 결정되는 의존성을 의미한다. 

UserPresentation은 Compile될 때 UserRepository를 참조하게 된다.

 

Compile Time 의존성은 강한 의존성이기에 다음과 같은 단점을 갖는다. 

  • 재사용이 어렵다. 
  • 테스트하기 어렵다.

DIP를 통해 의존성을 느슨하게 만들어 주어야 한다. 

 

 

DIP (Dependency Inversion Principle)

SOLID 5원칙 중 "DIP (Dependency Inversion Principle)"은 

의존 관계를 맺을 때는 구체 클래스와 같은 변하기 쉬운 것보단

인터페이스와 같은 변하기 어려운 것에 의존해야 한다는 원칙이다.

 

Swift에선 Protocol을 통해 이를 만족할 수 있다. 

protocol UserFetchAble {
    func fetchUser() -> User
}

class UserRepository: UserFetchAble {
    func fetchUser() -> User {
        return User(name: "Jung", id: "111")
    }
}

class UsersPresentation {
    let usersRepository: UserFetchAble
    
    init(userRepository: UserFetchAble) {
        self.usersRepository = userRepository
    }
    
    func printUserInfo() {
        print(usersRepository.fetchUser())
    }
}

이런 경우 UserPresentation과 UserRepository는 인터페이스인 UserFetchAble에 의존하게 된다.

 

Runtime Dependency

이러한 경우 Runtime에 의존성이 결정되기 때문에, "Runtime Dependency"라 부른다.

 

인터페이스에 의존하기 때문에 Compile시점에는 User를 Fetch한다는 것만 알고 있을 뿐, 

실행 시점에 어떤 객체를 주입받아서 실행할지 결정난다. 

따라서, 느슨한 의존성을 갖게 되기 때문에, 

재사용에 용이하며, 테스트코드 작성이 쉽다.

 

예를 들어, UserFetchable Protocol 타입만 만족하면 어떤 클래스든 주입받을 수 있다. 

 

또한, 실제 UserPresentation을 테스트 할 때, 실제 서버와 통신이 아닌 Mock 서버를 통해 진행해야 한다. 

따라서, UserFetchable Protocol을 채택하는 Mock서버와 통신하는 클래스를 생성하여 주입해 줄 수 있다.

 

이러한 DIP를 적용한 대표적인 예시가 UIKit의 UITableView이다.

UITableView는 구체 클래스가 아닌,

UITableViewDelegate, UITableViewDataSource라는 Protocol을 통해 

데이터의 요청과 행위를 위임한다.

class ViewController: UIViewController, UITableViewDelegate {
	@IBOutlet weak var tableView: UITableView
	
	override func viewDidLoad() {
		super.viewDidLoad()
		tableView.delegate = self
	}
}

UITableView는 해당 Protocol 타입이면 Interaction할 수 있게 되며, 

이는 재사용성이 높다.

 

 

Dependency Injection

앞서 문장을 다시 살펴보자.

"의존성 주입"이란,
객체가 의존관계에 있을 때 "직접 생성"하는 것이 아닌, 
DIP를 적용하여 "외부에서 제공"받는 패턴을 말한다.

DIP를 적용하는 방법까지 알아보았으니, "외부에서 제공"받는 방법에 대해 알아보자. 

Dependency Injection은 크게 3가지가 있다.

  • Initializer Injection
  • Property Injection
  • Method Injection

 

Initializer Injection

Initializer를 통해 의존관계를 주입받는 방법이다.

class UsersPresentation {
    let usersRepository: UserFetchAble
    
    init(userRepository: UserFetchAble) {
        self.usersRepository = userRepository
    }
    ...
}

let userPresentation = UsersPresentation(userRepository: UserRepository())

Initializer는 1회 호출을 보장하기 때문에, 주입받은 객체가 변하기 않는다는 것을 보장할 수 있다. 

 

Property Injection

class UsersPresentation {
    var usersRepository: UserFetchAble?
    
    ...
}

let userPresentation = UsersPresentation()
userPresentation.usersRepository = UserRepository()

 

Method Injection

class UsersPresentation {
    var usersRepository: UserFetchAble?
    ...
    func methodInjection(userRepository: UserFetchAble) {
        self.usersRepository = userRepository
    }
}

let userPresentation = UsersPresentation()
userPresentation.methodInjection(userRepository: UserRepository())

 

DI 패턴은 객체 사이의 의존성을 느슨하게 만들어 

테스트에 용이하고, 변경에 유연하게 만들어 준다. 

 

하지만, 실제 어떤 객체가 주입될 지는 외부에서 결정 나기 때문에, 

코드의 가독성을 떨어트린다. 

'iOS > Pattern' 카테고리의 다른 글

[Design Pattern] Coordinator 패턴  (0) 2024.05.26
[Architecture Pattern] MVVM  (0) 2023.05.27
[Architecture Pattern] SoftWare Architecture  (0) 2023.05.24
[Architecture Pattern] MVP  (0) 2023.03.24
[Architecture Pattern] MVC  (0) 2023.03.22
복사했습니다!