의존성 주입(DI)는 객체 간의 의존 관계를 외부에서 주입하는 설계 패턴이다.
객체 간의 결합도를 낮추고 테스트와 유지보수성을 향상시킬 수 있다.
iOS 개발에서 객체지향의 5원칙인 SOLID 원칙(특히 OCP, DIP)을 적용할 때 유용
어떤 객체가 다른 객체를 사용할 때 그 객체에 대한 의존성이 발생
예) AlarmManager가 SoundPlayer를 직접 생성하고 사용한다면 AlarmManager는 SoundPlayer에 강하게 의존
//의존성을 직접 생성하는 방식 (안 좋은 예)
class AlarmManager {
private let soundPlayer = SoundPlayer()
func triggerAlarm() {
soundPlayer.playSound()
}
}
위 방식에서는 AlarmManager가 SoundPlayer를 직접 생성(SoundPlayer() 호출)하고 있기 때문에
//의존성 주입 방식 (좋은 예)
class AlarmManager {
private let soundPlayer: SoundPlayer
init(soundPlayer: SoundPlayer) {
self.soundPlayer = soundPlayer
}
func triggerAlarm() {
soundPlayer.playSound()
}
}
// 외부에서 주입
let player = SoundPlayer()
let alarmManager = AlarmManager(soundPlayer: player)
AlarmManager가 SoundPlayer를 직접 생성하지 않고, 생성자로 주입받음
이를 통해 SoundPlayer를 다른 객체로 교체하기 쉬워지고, 테스트도 용이해짐
위의 방식은 AlarmManager가 SoundPlayer의 구체적인 구현에 의존하고 있습니다.
하지만 의존성을 프로토콜을 통해 추상화하면 더 유연한 구조를 만들 수 있음
protocol SoundPlaying {
func playSound()
}
class SoundPlayer: SoundPlaying {
func playSound() {
print("🔊 알람 소리 재생")
}
}
class AlarmManager {
private let soundPlayer: SoundPlaying
init(soundPlayer: SoundPlaying) {
self.soundPlayer = soundPlayer
}
func triggerAlarm() {
soundPlayer.playSound()
}
}
SoundPlaying 프로토콜을 정의하고, SoundPlayer가 이를 준수하도록 구현
AlarmManager는 SoundPlaying 프로토콜만 알면 되므로, SoundPlayer의 구체적인 구현을 몰라도 됨
SoundPlaying을 준수하는 새로운 클래스를 만들어서 쉽게 교체할 수 있음
class SilentSoundPlayer: SoundPlaying {
func playSound() {
print("🔇 소리를 내지 않음 (무음 모드)")
}
}
// 다른 사운드 플레이어를 사용하여 AlarmManager 생성 가능
let silentPlayer = SilentSoundPlayer()
let alarmManager = AlarmManager(soundPlayer: silentPlayer)
alarmManager.triggerAlarm() // 🔇 소리를 내지 않음 (무음 모드)
이제 SoundPlaying을 따르는 어떤 구현체든 주입할 수 있음
즉, 코드를 수정하지 않고도 다양한 방식의 사운드 플레이어를 사용할 수 있음
유닛 테스트에서는 Mock 객체를 만들어 주입할 수도 있음
class MockSoundPlayer: SoundPlaying {
var isPlayed = false
func playSound() {
isPlayed = true
}
}
// 테스트 코드
let mockPlayer = MockSoundPlayer()
let alarmManager = AlarmManager(soundPlayer: mockPlayer)
alarmManager.triggerAlarm()
assert(mockPlayer.isPlayed == true, "playSound가 호출되지 않음!")
MockSoundPlayer를 이용하면 실제 소리를 재생하지 않고, 호출 여부만 확인 가능
이를 통해 유닛 테스트가 쉽고 빠르게 실행될 수 있음