Swift는 Protocol-Oriented Programming(POP)을 기반으로 설계된 언어로, iOS 개발자에게 새로운 사고방식과 설계 전략을 제공합니다. 이 글에서는 Swift의 POP를 활용하여 어떻게 iOS 앱 개발에 접근할지, 그리고 이를 통해 유연하고 유지보수하기 쉬운 코드를 작성하는 방법에 대해 살펴보겠습니다.
1. Protocol을 중심으로 설계하라
클래스나 구조체를 설계하기 전, 객체의 역할과 책임을 정의하세요. 그런 다음, 이를 Protocol로 추상화합니다.
이 접근법은 코드를 더 유연하고 재사용 가능하게 만들어줍니다.
protocol Drivable {
func startEngine()
func drive()
}
struct Car: Drivable {
func startEngine() {
print("Engine started")
}
func drive() {
print("Driving")
}
}
핵심:
• 객체의 구체적인 구현보다는 역할에 초점을 맞추세요.
• 추상화를 통해 코드의 변경과 확장이 쉬워집니다.
2. 상속 대신 다중 프로토콜 채택
Swift는 다중 상속을 지원하지 않지만, 다중 프로토콜 채택으로 이를 대체할 수 있습니다.
필요한 기능을 여러 Protocol로 분리하고 이를 객체에 조합하세요.
protocol Flyable {
func fly()
}
protocol Swimmable {
func swim()
}
struct Duck: Flyable, Swimmable {
func fly() {
print("Duck is flying")
}
func swim() {
print("Duck is swimming")
}
}
핵심:
• 불필요한 상속 계층을 줄이고 필요한 기능만 조합하여 객체를 설계합니다.
• 테스트와 유지보수가 용이합니다.
3. Protocol Extension으로 기본 구현 제공
Protocol Extension은 중복 코드를 줄이고 기본 동작을 정의하는 데 매우 유용합니다.
Protocol에 기본 구현을 추가하여 모든 객체가 이를 사용할 수 있도록 하세요.
protocol Greetable {
func greet()
}
extension Greetable {
func greet() {
print("Hello!")
}
}
struct Person: Greetable {}
struct Robot: Greetable {}
Person().greet() // "Hello!"
Robot().greet() // "Hello!"
핵심:
• 객체별로 추가 구현이 필요 없는 기본 동작을 손쉽게 정의할 수 있습니다.
• 확장성을 유지하면서 공통 로직을 공유합니다.
4. Value Type 중심 설계
Swift는 구조체(Struct)가 기본적으로 Value Type입니다. 데이터 중심 로직은 구조체와 Protocol을 사용하여 설계하는 것이 좋습니다.
protocol Identifiable {
var id: String { get }
}
struct User: Identifiable {
var id: String
var name: String
}
핵심:
• Value Type은 Thread-Safe하며, 메모리 관리가 용이합니다.
• 데이터 무결성을 유지하는 데 유리합니다.
5. 의존성 주입과 Protocol 활용
테스트 가능한 코드를 작성하려면 의존성을 주입하고, 이를 Protocol로 추상화하세요.
이 방법은 Mock 객체를 활용한 테스트를 쉽게 만들어줍니다.
protocol NetworkService {
func fetchData() -> String
}
struct APIService: NetworkService {
func fetchData() -> String {
return "Real Data"
}
}
struct MockService: NetworkService {
func fetchData() -> String {
return "Mock Data"
}
}
struct DataManager {
let service: NetworkService
func getData() -> String {
return service.fetchData()
}
}
핵심:
• 실제 환경과 테스트 환경을 쉽게 분리할 수 있습니다.
• Protocol 덕분에 유연성이 극대화됩니다.
6. 컴포지션을 활용한 코드 재사용
*컴포지션(Composition)*은 Protocol-Oriented Programming의 강력한 장점 중 하나입니다.
Protocol을 조합하여 객체가 필요한 기능만 선택적으로 구현할 수 있습니다.
protocol Runnable {
func run()
}
protocol Jumpable {
func jump()
}
struct Athlete: Runnable, Jumpable {
func run() {
print("Athlete is running")
}
func jump() {
print("Athlete is jumping")
}
}
핵심:
• 객체는 필요한 기능만 채택하여 설계의 유연성을 극대화합니다.
• 여러 Protocol을 조합함으로써 재사용 가능성이 높아집니다.
7. Protocol을 통해 설계 유연성 강화
미래에 변경 가능성이 있는 코드는 Protocol로 추상화하세요.
예를 들어, 데이터 소스, 네트워크 요청, 사용자 인터페이스 등을 추상화하면 유지보수가 쉬워집니다.
8. Protocol과 OOP의 조화로운 사용
Swift는 Protocol-Oriented Programming과 Object-Oriented Programming을 모두 지원합니다.
상속 계층이 필요한 상황에서는 클래스를 사용하고, 재사용성과 유연성이 필요한 부분에서는 Protocol을 사용하세요.