iOS & Swift

프로토콜 지향 프로그래밍(POP), OOP 한계를 넘는 법

프로토콜 지향 프로그래밍(POP)은 무엇을 상속받았는지가 아니라 무엇을 할 수 있는지로 타입을 설계합니다. 상속이 막히는 지점과 프로토콜 조합으로 푸는 방법, 피해야 할 경우까지 Swift 예제로 정리했습니다.

이석우iOS Developer4분 읽기
프로토콜 지향 프로그래밍(POP), OOP 한계를 넘는 법 대표 이미지

클래스 상속으로 기능을 확장하다 보면 부모 클래스가 점점 뚱뚱해지는 경험, 다들 한 번쯤 해보셨을 거예요. 공통 기능을 넣겠다고 슈퍼클래스에 메서드를 계속 밀어 넣다 보면, 나중에는 손대기 무서운 거대한 부모 클래스를 마주하게 되죠.

프로토콜 지향 프로그래밍(POP)은 바로 이 지점에서 OOP의 한계를 넘습니다. POP는 “무엇을 상속받았는가”가 아니라 “무엇을 할 수 있는가”로 타입을 설계하는 방식이에요. 상속의 수직 구조 대신, 필요한 능력을 프로토콜 단위로 조합해서 붙이는 겁니다.

이 글에서는 OOP의 어떤 한계 때문에 POP가 나왔는지, 실제 Swift 코드로는 뭐가 다른지, 언제 쓰고 언제 피해야 하는지까지 짚어 볼게요.

OOP 상속 트리와 POP 조각 조합을 좌우로 대비한 OOP vs POP 썸네일
OOP 상속과 POP, 첫 화면부터 설계 방향이 이렇게 다릅니다

OOP 상속은 어디서 막힐까?

객체지향의 상속은 분명 강력합니다. 하지만 세 가지 지점에서 자주 벽에 부딪혀요.

첫째, 단일 상속의 한계입니다. Swift의 클래스는 부모를 하나만 가질 수 있어요. “네트워크도 되고 캐싱도 되는” 타입을 만들려면 상속만으로는 답이 안 나옵니다.

둘째, 뚱뚱한 기반 클래스 문제예요. 공통 기능을 부모에 몰아넣다 보면, 자식은 자기가 쓰지도 않는 메서드까지 전부 물려받습니다.

셋째, 값 타입과의 궁합입니다. Swift의 struct와 enum은 상속 자체가 안 됩니다. 그런데 Swift 표준 라이브러리는 대부분 값 타입으로 되어 있어요.

즉, 상속 중심으로만 설계하면 Swift가 밀어주는 값 타입의 장점을 살리기 어렵습니다.


프로토콜 지향 프로그래밍(POP)이란?

POP는 2015년 애플이 WWDC에서 “Swift는 프로토콜 지향 언어다”라고 선언하면서 널리 알려졌어요.

핵심은 프로토콜에 기본 구현을 붙일 수 있다는 점입니다. 프로토콜 익스텐션을 쓰면, 인터페이스만 정의하는 게 아니라 실제 동작까지 나눠 줄 수 있어요.

예를 들어 이런 식이에요.

protocol Greetable {
    var name: String { get }
}

extension Greetable {
    func greet() -> String {
        return "안녕하세요, \(name)입니다"
    }
}

struct Person: Greetable {
    let name: String
}

print(Person(name: "지훈").greet())
// 출력: 안녕하세요, 지훈입니다

Greetable을 채택하기만 하면 greet()가 공짜로 따라옵니다. 상속 없이, struct에도 기능을 나눠 준 거예요.

여러 프로토콜을 동시에 채택할 수도 있습니다. 네트워크 능력과 캐싱 능력을 각각 프로토콜로 만들어 두면, 필요한 타입에 필요한 능력만 골라 붙일 수 있어요.

Person 타입에 Greetable·Cacheable 프로토콜을 채택시킨 클래스 다이어그램
프로토콜 하나에 능력 하나씩, 필요한 것만 골라 붙이는 구조예요

OOP 상속 vs POP, 무엇이 다를까?

둘의 차이를 표로 정리하면 이렇습니다.

항목 OOP 상속 POP
코드 재사용 방식 부모 클래스에서 물려받음 프로토콜 익스텐션으로 조합
타입 관계 수직(is-a) 수평(can-do)
다중 채택 단일 상속만 여러 프로토콜 동시 채택
값 타입 지원 struct/enum 불가 struct/enum 모두 가능
결합도 부모-자식 강하게 묶임 능력 단위로 느슨하게 분리

한마디로 이래요.

상속은 “너는 무엇이다”라고 말하고, 프로토콜은 “너는 무엇을 할 수 있다”라고 말합니다.

OOP와 POP 비교 표가 떠 있는 Xcode 화면과 커피가 놓인 책상
저는 이럴 때 '상속부터'보다 '프로토콜로 나눌 수 있을까'를 먼저 떠올립니다

언제 POP를 쓰고 언제 피해야 할까?

POP가 만능은 아니에요. 상황에 맞게 골라 써야 합니다.

상황 판단
값 타입(struct/enum)에 공통 기능을 나누고 싶다 POP가 정답
서로 다른 능력을 조합해야 한다 POP가 유리
명확한 계층 구조(동물-포유류-개)가 이미 있다 상속도 충분히 좋음
참조 공유가 핵심인 객체(예: 뷰 컨트롤러) 클래스 상속이 자연스러움
프로토콜이 너무 잘게 쪼개져 추적이 어렵다 과한 추상화, 되돌아볼 것

골라 쓰는 기준은 간단해요. 값 타입과 능력 조합이 필요하면 POP, 명확한 계층과 참조 공유가 필요하면 상속입니다. 둘은 대립이 아니라 상황에 따라 함께 쓰는 도구예요.

면접에서는 이렇게 물어봅니다

Q. POP가 OOP의 어떤 한계를 해결하나요?

단일 상속과 뚱뚱한 기반 클래스 문제를 해결합니다. 여러 프로토콜을 조합해 필요한 능력만 붙일 수 있고, 상속이 안 되는 struct/enum 같은 값 타입에도 프로토콜 익스텐션으로 기본 구현을 나눠 줄 수 있기 때문입니다.

Q. 프로토콜 익스텐션과 클래스 상속의 차이는 뭔가요?

상속은 수직적 is-a 관계로 하나의 부모만 가질 수 있지만, 프로토콜은 수평적 can-do 관계로 여러 개를 동시에 채택할 수 있습니다. 또 상속은 참조 타입인 클래스만 가능하지만, 프로토콜은 값 타입에도 적용됩니다.


상속이 나쁘고 프로토콜이 좋다는 이분법은 아니에요. 다만 Swift로 개발한다면, “일단 상속부터”가 아니라 “이건 프로토콜로 나눌 수 있을까?“를 먼저 떠올려 보면 좋겠어요. 그 습관 하나가 한결 유연한 코드로 이어질 거예요.

이어서 읽기