GQ1. POP가 뭘까?
GQ2. POP가 왜 좋을까?
구분 OOP POP 중심 구조 클래스 + 상속 프로토콜 + 조합 다형성 상속 기반 프로토콜 기반 코드 재사용 부모 클래스 프로토콜 익스텐션 타입 중심 참조 타입(class) 값 타입(struct) 예측 가능성 낮음 (공유) 높음 (복사) Swift와의 궁합 중간 최상
// OOP
class Animal {
func speak() { print("...") }
}
class Dog: Animal {
override func speak() { print("멍멍") }
}
// POP
protocol Animal {
func speak()
}
struct Dog: Animal {
func speak() { print("멍멍") }
}
protocol Drivable {
func drive()
}
extension Drivable {
func drive() {
print("운전 중")
}
}
struct Car: Drivable {}
let myCar = Car()
myCar.drive() // 운전 중
protocol Flyable { func fly() }
extension Flyable {
func fly() { print("날아갑니다") }
}
struct Drone: Drivable, Flyable {}
let drone = Drone()
drone.drive()
drone.fly()
MockAPIClient를 활용해 실제 네트워크 없이 테스트 가능protocol APIClient {
func fetchData() async throws -> [String]
}
struct RealAPIClient: APIClient {
func fetchData() async throws -> [String] {
// 네트워크 코드
return ["Real 데이터"]
}
}
struct MockAPIClient: APIClient {
func fetchData() async throws -> [String] {
return ["Mock 데이터"]
}
}
protocol APIClient {
func request(endpoint: String) async throws -> String
}
class ViewModel {
let api: APIClient // 프로토콜에만 의존
init(api: APIClient) {
self.api = api
}
func fetchData() async {
let data = try? await api.request(endpoint: "/hello")
print(data ?? "오류")
}
}
상속에서도 공통 기능을 재사용할 수 있는데 왜 하필 프로토콜을 써야할까?
class Animal {
func sound() { print("기본 소리") }
}
class Dog: Animal {}
class Cat: Animal {}
class Animal {
func sound() { print("🐾 Animal이 새롭게 소리냄") }
}
➡️ Dog와 Cat이 바뀐 Animal 클래스의 영향을 받는다
class Dog: Animal {
override func sound() { print("멍멍") }
}
➡️ 맞음. 하지만 override를 하지 않은 상태인 Cat, Hamster는..?(🐹: 당황찌)
➡️ 현실적으로 불가능하다. 수십 개의 하위 클래스를 매번 override하고 이 규칙을 직접 우리가 기억해야한다.
class Animal {
func sound() { print("소리") }
}
class Mammal: Animal {}
class Canine: Mammal {}
class Dog: Canine {} // Depth: 3
let dog = Dog()
dog.sound()
➡️ Animal의 sound() 하나 바꾸면 Dog도 바뀜
➡️ Dog()는 Animal에 의존하고 있는지도 몰랐음 = 숨겨진 의존성
➡️ 높은 결합도 + 낮은 유연성
protocol Soundable {
func sound()
}
extension Soundable {
func sound() {
print("소리")
}
}
struct Dog: Soundable {}
let dog = Dog()
dog.sound() // "소리"
➡️ 상속 구조가 아닌 역할 중심으로 분리
➡️ UIKit 사용시에는 꼭 사용을 해야할 때가 있다
대표 예시
UIViewController 커스터마이징class HomeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
}
class MyButton: UIButton {
override func layoutSubviews() {
super.layoutSubviews()
// 버튼 디자인 변경
}
}
class MyCell: UITableViewCell {
override func prepareForReuse() { ... }
}
UIResponder 상속 구조에 연결돼 있음