프로그래밍iOS 개발자

스위프트에서 프로토콜 추상화가 작동하는 방식과 클래스 상속과의 차이점에 대해 설명하십시오. 클래스를 계층 구조 대신 프로토콜을 사용해야 하는 경우는 언제입니까?

Hintsage AI 어시스턴트로 면접 통과

답변.

문제의 역사:

프로토콜 추상화는 OOP의 전통적인 객체 상속에 반하여 스위프트에서 등장했습니다. Objective-C 및 기타 OOP 언어에서는 "일반에서 특별로" 향하는 상속 접근 방식이 지배적이었지만, 스위프트는 상속보다 합성을 강조하며 프로토콜을 추상화의 주요 방법으로 발전시켰습니다.

문제:

전통적인 상속은 엄격한 계층 구조를 가정합니다: 재정의를 통해 의무적으로 확장해야 하는 서브클래스의 트리. 이는 유연성을 제한하고, "부서지기 쉬운" 코드, 복잡한 리팩토링 및 기본 조상 클래스의 과도한 팽창을 초래합니다. 또한, 스위프트는 클래스의 다중 상속을 지원하지 않으므로, 기능성을 재사용하기 위해서는 다른 메커니즘을 통해 가능합니다.

해결책:

프로토콜 추상화는 타입이 구현해야 하는 "요구 사항 집합"을 선언할 수 있게 해줍니다. 프로토콜은 공통 논리를 주입하기 위해 확장(extension)할 수 있으며, 이는 "믹스인" 개념에 더 가깝습니다:

코드 예:

protocol Drawable { func draw() } extension Drawable { func draw() { print("Default drawing") } } struct Circle: Drawable {} let c = Circle() c.draw() // "Default drawing" 출력

주요 특징:

  • 프로토콜은 다중 합성을 지원합니다 (다중 요구 사항).
  • 엄격한 계층 구조를 생성하지 않습니다 — 확장이 쉬우며 아키텍처를 깨뜨리지 않습니다.
  • 값 타입 (struct/enum) 및 클래스와 모두 작동할 수 있습니다, 이는 상속에서는 불가능합니다.

함정이 있는 질문.

프로토콜의 확장과 클래스의 일반 구현 간 차이는 무엇입니까?

프로토콜을 확장합니다. extension은 사용자가 자신의 타입에서 이 메소드를 구현하지 않은 경우에만 default 구현을 추가합니다. 타입에서 명시적으로 구현된 메소드가 있다면, 그 메소드가 호출됩니다.

예:

protocol Demo { func foo() } extension Demo { func foo() { print("default") } } struct X: Demo { func foo() { print("custom") } } X().foo() // "custom"

프로토콜과 해당 extension을 구현하는 타입이 프로토콜 데이터로 참조되면 어떻게 됩니까?

프로토콜이 메소드를 필수(요구 사항)으로 선언하면, 프로토콜 타입으로 변환하더라도 특정 타입의 구현이 사용됩니다. 그러나, extension에 새로운 (프로토콜에서 이전에 선언되지 않은) 속성이 추가되면, 이는 extension을 통해서만 접근할 수 있고 프로토콜 타입을 통해서는 접근할 수 없습니다.

하나의 프로토콜을 구현하는 서로 다른 struct 인스턴스를 배열에 저장할 수 있습니까?

네 — "존재론적" 타입 덕분에 (예: [Drawable]) 이형 컬렉션을 저장할 수 있습니다:

struct Tri: Drawable { func draw() { print("Triangle") } } let arr: [Drawable] = [Circle(), Tri()] arr.forEach { $0.draw() }

일반적인 실수 및 안티 패턴

  • 기본 인터페이스를 위한 두꺼운 슈퍼클래스 상속 대신 프로토콜로 나누기
  • 프로토콜에 명시적 요구 사항 없이 extension을 과도하게 사용
  • associatedtype이 있는 프로토콜을 컬렉션에 저장하려는 시도 ([SomeProtocol]) — 이는 지원되지 않습니다.

실생활 사례

부정적 사례

회사에는 모든 도형(Circle, Square, Polygon)이 상속받는 기본 슈퍼클래스 Shape가 있었습니다. 새로운 도형을 추가할 때마다 기본 클래스가 성장했습니다. 시스템 확장이 점점 더 어려워졌고, 새로운 타입이 기존 코드의 ABI를 깨뜨리곤 했습니다.

장점:

  • 기본 클래스에서 새로운 공통 메소드를 빠르게 도입

단점:

  • 과도한 의존성을 가진 단일 체계 계층
  • 계층 밖에서 재사용성이 떨어짐
  • override 메소드 충돌

긍정적 사례

여러 프로토콜 Drawable, Colorable, Animatable을 사용하기 시작했습니다. 이제 각 도형을 다른 구조를 변경하지 않고도 "애니메이션 효과와 색상"이 동시에 적용 가능하게 되었습니다. 새로운 기능은 extension을 통해 추가됩니다.

장점:

  • 유연성, 쉬운 유지 관리 및 확장성
  • 다양한 컨텍스트에서 개선된 재사용성

단점:

  • API 설계에 대한 주의 깊은 접근 및 associatedtype에 대한 지식 필요