Swift에서 Protocol을 활용하여 의존성 주입(Dependency Injection, DI)을 구현하는 방법을 정리해보았다.
의존성 주입이란 객체 간의 강한 결합을 줄이고 유연성을 높이기 위해 필요한 객체를 외부에서 주입하는 기법이다. 이를 통해 테스트가 용이해지고 유지보수가 쉬워진다.
Protocol을 사용하면 구체적인 구현체가 아닌 추상적인 인터페이스에 의존하게 만들어 결합도를 낮출 수 있다.
protocol PNetworkService {
func fetchData(completion: @escaping (String) -> Void)
}
class NetworkService: PNetworkService {
func fetchData(completion: @escaping (String) -> Void) {
// 네트워크 요청을 가정한 더미 데이터 반환
completion("Fetched Data from API")
}
}
class DataManager {
private let networkService: PNetworkService
init(networkService: PNetworkService) {
self.networkService = networkService
}
func loadData() {
networkService.fetchData { data in
print("Received Data: \(data)")
}
}
}
let networkService = NetworkService()
let dataManager = DataManager(networkService: networkService)
dataManager.loadData()
Mock 객체를 활용하여 테스트할 수 있음class MockNetworkService: PNetworkService {
func fetchData(completion: @escaping (String) -> Void) {
completion("Mock Data for Testing")
}
}
let mockService = MockNetworkService()
let testDataManager = DataManager(networkService: mockService)
testDataManager.loadData() // "Received Data: Mock Data for Testing" 출력
프로토콜 확장을 사용하면 기본 구현을 제공하여 코드 중복을 줄일 수 있다.
extension PNetworkService {
func fetchData(completion: @escaping (String) -> Void) {
completion("Default Fetched Data")
}
}
class DefaultNetworkService: PNetworkService {}
let defaultService = DefaultNetworkService()
defaultService.fetchData { data in
print("Default Implementation: \(data)")
} // "Default Implementation: Default Fetched Data" 출력