본문 바로가기
카테고리 없음

프로토콜 지향 프로그래밍에 관하여

by iOS 개린이 2023. 6. 7.

프로토콜 지향 프로그래밍

-프로토콜 지향 프로그래밍은 Swift에서 가장 핵심 패러다임 중 하나이다.

 

Swift는 객체 지향 프로그래밍 패러다임을 지향하기도 하는데,

보통 객체 지향 프로그래밍은 대부분 클래스의 상속을 이용해서 타입에 공통적인 기능을 구현한다.

하지만 Swift 표준 라이브러리의 코드를 보면, 대부분 구조체로 기본 타입이 구현되어 있다.

 

클래스와 같은 참조타입을 사용하는 것은 참조 카운트, 동적 할당 등으로 인해 리소스가 비싸다.

또한 참조타입의 암시적인 공유로 인한 데이터 불일치 문제를 야기할 수 있다.

클래스의 상속 기능은 슈퍼클래스들에 대한 정보를 알고 있어야 하기 때문에 코드가 복잡해질 수 있다.

 

클래스의 비싼 리소스 한계점은 값 타입을 사용하여 해결할 수 있다.

하지만 값 타입만을 사용하고 싶어도, 상속을 할 수 없어서 기능을 다시 구현해주어야 하고, 복사 매커니즘의 한계가 있다.

그래서 Swift는 값 타입과 프로토콜을 같이 활용하여 해당 문제를 해결한다.

이것이 Swift가 프로토콜 지향 프로그래밍을 강력하게 추천하는 이유다.

 

프로토콜 개념

-Swift의 프로토콜 지향 프로그래밍을 이해하기 위해서 다음과 같은 프로토콜의 기본 개념에 대해서 학습해보았다.

 

https://iosjiho.tistory.com/69 -> 프로토콜과 프로토콜 요구사항 준수하기.

https://iosjiho.tistory.com/126 -> 타입으로서 프로토콜.

https://iosjiho.tistory.com/127 -> 프로토콜의 익스텐션 기능.

https://iosjiho.tistory.com/128 -> 프로토콜의 컴포지션.

https://iosjiho.tistory.com/129 -> 프로토콜의 associatedtype

 

중요한 것은 기본 개념을 학습할 때, 단순히 이론적인 부분을 넘어

실제 Swift 코드에서 어떻게 활용되고 있는지, 그리고 이를 통해 어떤 이점을 얻을 수 있는지를 이해해야 한다.

 

이런 방식으로 학습을 하면, Swift가 왜 프로토콜 지향 프로그래밍을 강조하는지,

우리는 어떤 방식으로 프로토콜 지향 프로그래밍을 할 수 있을지에 대해 이해하는 첫 걸음이 된다.

 

https://iosjiho.tistory.com/131 -> WWDC 2016 - 프로토콜 세션 정리

해당 세션은 프로토콜 지향 프로그래밍에 대해 Swift가 어떤 점을 강조하고 있는지 깊게 이해하기 위해 학습했습니다.

MVC 패턴을 사용하고 있는 앱에서, 각 Layer에 대해 프로토콜을 어떤 상황에서 어떻게 이용하고 있는지, 어떤 점을 강조하고 있는지 학습할 수 있습니다.(거의 View Layer 중점.)

 

프로토콜 지향 프로그래밍의 이점

1. 유연성

프로토콜의 다양한 기능들은 유연성을 확보할 수 있다.

 

클래스, 구조체, 열거형 등 어떠한 타입도 프로토콜을 채택하여 구현이 가능하다

따라서 어떠한 하나의 타입에 종속되지 않고, 해당 프로토콜을 채택한 어떠한 타입이든 사용할 수 있기 때문에 유연성을 확보할 수 있다.

 

프로토콜은 다중 채택이 가능하다

클래스는 다중 상속이 불가능하지만,

프로토콜은 다중 채택이 가능하기 때문에 원하는 프로토콜의 기능을 모두 채택하여 사용하는 유연성을 확보할 수 있다. 

또한 다중 채택은 기능확장성에도 유용하다.

 

프로토콜의 익스텐션을 통해 요구사항을 원하는 타입 별로 제한 할 수 있다.

프로토콜의 요구사항을 채택하는 모든 타입에서 무조건 구현해야 한다면?

해당 요구사항이 불필요한 타입에서는 괜히 코드량만 늘어날 것이다.

하지만 프로토콜의 익스텐션과 'where' 절을 통해 프로퍼티나 메서드의 요구사항을 원하는 타입에 제한하면

각 타입에 대해 대응이 가능하고(유연성), 불필요한 코드를 줄일 수 있다.

 

제네릭과 같은 기능을 수행하는 프로토콜의 'associatedtype' 을 통해 유연성을 확보할 수 있다.

Swift에서 제네릭은 범용 타입을 의미하고, 'associatedtype' 은 제네릭과 같은 기능을 수행한다.

따라서 'associatedtype'을 사용하여 한가지 타입에 종속되지 않고, 범용성 있게 타입을 다룰 수 있다.

 

 

2. 타입 안전성

프로토콜의 유연성을 통해 클래스, 구조체, 열거형 등 각각 다른 타입이 하나의 타입으로 사용될 수 있다.

이는 굉장한 장점이지만, 설계를 잘못하면 원치 않는 타입이 들어와 버그를 발생시킬 수 있다.

 

하지만 프로토콜은 'associatedtype'과 'where' 절을 사용하여 원하는 타입만 들어올 수 있도록 타입을 제한하고,

'Extension'과 'where' 절을 통해서 원치않는 타입이 기능을 수행할 수 없도록 만들 수 있다.

이러한 프로토콜의 타입안전성은 보이지 않는 버그를 방지하고, 코드를 안전하고 예측 가능하게 만들 수 있다.

 

3. 기능의 모듈화

기능의 모듈화는 하나의 기능이 특정 타입에 종속되지 않고, 독립적으로 여러 곳에서 재사용될 수 있게 만드는 것을 말한다.

 

프로토콜은 특정 타입이 갖춰야 할 동작이나 속성을 정의하는 청사진이다.

해당 동작이 구체적으로 어떻게 수행되는지 정의하지 않고, 어떤 동작이 가능해야하는지만 정의한다.

이 특징은 기능과 구현을 분리함으로써 다양한 타입들이 서로 독립적으로 작동하도록 만들어 준다.

 

예를 들어, 'Drivable' 이라는 프로토콜을 정의한다고 할 때, 해당 프로토콜은 'drive' 라는 메서드를 요구한다.

만약 'Car', 'MotorCycle', 'Tank' 등과 같은 다양한 타입이 'Drivable' 프로토콜을 채택하면, 각 타입은 자신의 방식으로 'drive' 메서드를 구현한다.

이렇게 각 타입은 자신만의 'drive' 동작을 가지면서 'Drivable' 이라는 공통 인터페이스를 공유하게 된다.

 

또한 프로토콜의 Composition 기능을 사용하면 여러 프로토콜을 결합하여 하나의 타입이 다양한 기능을 구현하도록 할 수 있다. 각기 다른 기능들을 필요에 따라 조합하고, 다양한 타입에서 구현함으로써 코드의 모듈성을 향상시킬 수 있다.

 

 

프로토콜 지향 프로그래밍의 한계

1. 공유 상태의 부재

프로토콜 자체는 저장 프로퍼티를 가질 수 없다.

이것은 프로토콜을 채택하는 타입이 상태를 공유할 수 있는 공통의 저장 프로퍼티를 가질 수 없음을 의미한다.

예를 들어 사용자의 프로필 정보를 여러 곳에서 동시에 업데이트 해야 하는 경우에는 상태 공유가 가능한 클래스를 사용하는 것이 좋다.

 

2. 프로토콜의 복잡성

과도한 프로토콜의 사용은 복잡한 계층 구도를 만들 수 있다.

여러 프로토콜을 사용하면서 해당 프로토콜에서 요구하는 메소드나 프로퍼티가 많아지고, 

일부 프로토콜은 타입 제약을 가진다면, 해당 프로토콜을 채택하는 타입도 굉장히 복잡해진다.

따라서 프로토콜이 실제로 코드를 명확하고 관리하기 쉽게 만들 수 있는 상황에서 사용하는 것이 좋다.

 

 

 

그럼 우리는 프로토콜 지향 프로그래밍을 어떻게 활용해야 할까?

 

-프로토콜의 기능은 의존성 분리, 모듈화, 유연성 증가 등 많은 장점을 제공한다.

하지만 프로토콜의 이점을 살리기 위해 실제로 어떤 상황에서 적용해야 할까?

Swift에서 프로토콜 지향 프로그래밍의 핵심 목적 중 하나는 클래스 타입의 한계를 값 타입과 프로토콜을 활용하여 극복하는 것이다.

따라서 프로토콜 지향 프로그래밍을 사용할 때, 해당 상황과 문제를 통해 첫 걸음을 시작하면 좋을 것 같다.

그리고 그 과정에서 여러 타입들이 공통적으로 사용하는 기능이나 특성이 무엇인지 파악하고,

이를 어떻게 프로토콜의 요구사항으로 정의할 것인지 그림으로 그려보면서 설계 하는 것이 프로토콜을 효율적으로 활용하는 데 도움이 될 것같다!!