스위프트의 프로그래밍 패러다임
스위프트의 프로그래밍 패러다임과 WWDC 2015 세션 408에 대해 정리했습니다.
- 📙 0. 스위프트의 프로그래밍 패러다임
- 📙 1. WWDC 2015 - ‘Protocol-Oriented Programming in Swift’ 세션
- 📙 2. 스위프트의 프로그래밍 패러다임에 대한 정확한 이해
- 📙 3. 스위프트에서의 명령형 프로그래밍과 객체 지향 프로그래밍
📙 0. 스위프트의 프로그래밍 패러다임
“스위프트는 여러 가지 프로그래밍 패러다임을 차용한 다중 패러다임 프로그래밍 언어입니다. 크게 보면 명령형 프로그래밍 패러다임, 객체지향 프로그래밍 패러다임, 함수형 프로그래밍 패러다임, 프로토콜 지향 프로그래밍 패러다임을 차용했습니다. 정확하게는 명령형과 객체지향 프로그래밍 패러다임을 기반으로 한 함수형 프로그래밍 패러다임과 프로토콜 지향 프로그래밍 패러다임을 지향합니다.”
스위프트 프로그래밍 3판, 야곰, p.42
스위프트의 프로그래밍 패러다임에 대해 더 많은 정보를 얻기 위해 구글링을 하면서 느낀 점은, 대부분의 사람들이 위의 내용과 거의 동일하게 설명하고 있다는 것이었다.
위의 내용은 분명 스위프트의 프로그래밍 패러다임에 대해 올바르게 소개하고 있지만, 누군가에게는 오해를 불러 일으킬 수 있기 때문에 주의해야 한다.
📙 1. WWDC 2015 - ‘Protocol-Oriented Programming in Swift’ 세션
WWDC 2015의 ‘Protocol-Oriented Programming in Swift’ 세션을 살펴보면, 그 이유에 대해 알 수 있다.
“Swift is great for object-oriented programming, but from the way for loops and string literals work to the emphasis on generics in the standard library, at its heart, Swift is protocol-oriented. Hopefully, by the time you leave here, you’ll be a little more protocol-oriented yourself. So, to get you started off on the right foot, we have a saying in Swift: ‘Don’t start with a class, start with a protocol.”
스위프트는 객체 지향 프로그래밍에 좋은 언어이지만, for 루프와 문자열 리터럴의 작동 방식, 그리고 제네릭에 대한 표준 라이브러리의 강조로 보아, 스위프트는 본질적으로 프로토콜 지향적입니다. 따라서 여러분이 이곳을 떠날 때, 조금 더 프로토콜 지향적인 사고를 할 수 있기를 바랍니다. 그리고 올바른 시작을 위해 스위프트에서는 “클래스로 시작하지 말고 프로토콜로 시작하라”는 말이 있습니다.
위 세션에서 Dave Abrahams는 자신의 동료 Crusty와의 일화를 인용하며, Swift에서 Class
가 가지는 문제점과 이를 해결하기 위해 고안한 Protocol
에 대해 소개하고 있다.
크러스티가 클래스에 대해 제기한 세 가지 큰 불만에 대해 간단하게 살펴보자.
1. Implicit Sharing
클래스는 참조 타입이므로 서로 다른 인스턴스가 동일한 데이터를 참조할 수 있는데, 이를 ‘암시적 공유’라고 한다.
(암시적 공유는 컬렉션 타입에 적용되는 메모리 관리 기법이지만, 클래스 인스턴스 간의 데이터 참조에도 적용될 수 있다.)
그런데 이러한 암시적 공유로 인해서 아래 그림과 같은 암시적 공유 문제가 발생할 수도 있다.
이해를 돕기 위해 암시적 공유 문제를 단순화해서 표현한 그림을 살펴보자.
A와 B는 서로 다른 인스턴스이지만, 동일한 데이터를 참조하고 있다. 이때 만약 A가 내부 상태를 변경한다고 하면 B에도 영향을 미치게 된다. B가 기존의 데이터를 얻기 위해 접근을 할 때, 기존의 데이터가 아닌 A에 의해 영향을 받은 데이터를 얻게 되기 때문이다.
이러한 암시적 공유 문제는 예상치 못한 데이터 변경과 동시성 문제로 인해 프로그램 예측 불가능성 및 성능 저하를 불러 일으킬 수 있다.
2. Inheritance All Up In Your Business
클래스의 상속에 대해서도 살펴보자.
(클래스 계층 구조를 설계할 때) 슈퍼클래스를 처음에 정확하게 선택하고 정의해야 하며, 나중에 변경하기 어렵다. (또한 스위프트에서는 단일 상속만 허용되기 때문에, 여러 가지 추상화를 모델링해야 할 경우, 클래스로 구현하기도 어렵다.)
슈퍼클래스는 파생된 클래스에 속성을 강제하며, 이것은 초기화를 처리하고 슈퍼클래스에 필요한 불변성을 깨뜨리지 않는 것을 복잡하게 만들 수 있다. 또한 상속이 계속해서 확장될수록 클래스의 복잡성이 증가하고 비대해진다.
클래스 상속과 관련하여 스위프트에서 재정의 가능한 요소에는 제한이 있고, 또 재정의 규칙을 따라야만 한다.
3. Lost Type Relationships
‘손실된 타입 관계’는 클래스의 참조 타입 특성으로 인해 발생하는 문제 중 하나로, 타입 간의 관계가 제대로 전달되지 않거나 손실되는 것을 말한다.
클래스의 ‘손실된 유형 관계’를 해결하기 위해서는 메서드를 재정의하거나 구체적인 파생 클래스로 다운캐스팅하는 작업을 추가적으로 진행해야 한다.
세션에 관해서 더 많은 정보를 얻고 싶다면 아래 글을 읽어보는 것을 추천한다.
https://melodiessim.netlify.app/power-of-protocols-medium/
https://www.infoq.com/news/2015/06/protocol-oriented-swift/
애플은 WWDC 2015에서 스위프트 2를 소개하면서 스위프트는 세계 최초의 프로토콜 지향 프로그래밍이라고 발표했다. 프로토콜은 클래스가 비대해져야 하는 필요성을 없애고 타입이 필요하지 않은 기능을 상속하는 것을 막을 수도 있다.
여기까지 내용을 읽고, 스위프트에서 프로토콜지향 프로그래밍이 객체지향 프로그래밍보다 월등할 것으로 생각할 수도 있지만, 여전히 클래스 계층과 상속이 필요한 경우도 있기 때문에 이러한 생각이 전적으로 옳은 것은 아니라고 할 수 있다.
📙 2. 스위프트의 프로그래밍 패러다임에 대한 정확한 이해
애플리케이션을 설계할 때에는 항상 최적의 일에 최적의 도구를 사용해야 함을 명심해야 한다. 2x4 판자 조각을 자르는 데 전기톱을 사용하지는 않을 것이며, 반대로 나무를 베어내는 데 회전 톱을 사용하지는 않을 것이다. 그러므로 승자는 한 가지에 국한되지 않고, 서로 다른 프로그래밍 패러다임을 조합해 사용할 수 있는 선택권을 가진 프로그래머다.
스위프트 4 프로토콜지향 프로그래밍 3/e, 존 호프만, p.195
스위프트는 불변성과 안정성, 예측 가능한 동작, 성능 개선 등을 강조하면서도 생산성과 코드의 유지보수성을 개선하는 것을 목표로 하고 있기 때문에, 개발자는 상황에 따라 적절한 패러다임을 조합하여 사용해야 한다.
스위프트는 명령형, 객체 지향, 함수형, 프로토콜 지향 프로그래밍 등의 원칙을 지원하고 특징을 갖추고 있다.
1. 명령형 프로그래밍 (Imperative Programming)
명령형 프로그래밍은 컴퓨터에게 실행해야 할 일련의 명령문을 순차적으로 제공하는 방식이다. 스위프트에서 명령형 프로그래밍은 변수, 조건문, 반복문과 같은 구조를 사용하여 코드의 흐름을 제어한다.
2. 객체 지향 프로그래밍 (Object-Oriented Programming)
객체 지향 프로그래밍은 현실 세계의 객체를 추상화하여 프로그래밍하는 패러다임이다. 스위프트는 클래스와 객체를 사용하여 데이터와 해당 데이터를 조작하는 메서드를 캡슐화할 수 있다. 또한, 상속, 다형성, 캡슐화와 같은 객체 지향 개념도 지원한다.
3. 함수형 프로그래밍 (Functional Programming)
함수형 프로그래밍은 함수를 기본적인 구성 요소로 사용하여 프로그래밍하는 패러다임이다. 스위프트에서 함수는 일급 객체로 취급되며, 함수를 변수나 상수에 할당하고 다른 함수에 매개변수로 전달할 수 있다. 또한, 함수형 프로그래밍은 불변성과 순수 함수 등의 개념을 강조한다.
4. 프로토콜 지향 프로그래밍 (Protocol-Oriented Programming)
프로토콜 지향 프로그래밍은 인터페이스와 구현을 분리하여 코드의 재사용성과 유연성을 높이는 패러다임이다. 스위프트에서 프로토콜은 특정 기능을 제공하기 위한 메서드, 프로퍼티, 이니셜라이저 등을 정의하는 역할을 한다. 타입이 하나 이상의 프로토콜을 채택함으로써 다중 상속과 비슷한 효과를 얻을 수 있다.
다시 처음으로 돌아와서 처음에 언급했던 스위프트의 프로그래밍 패러다임에 대한 설명을 살펴보자.
“스위프트는 여러 가지 프로그래밍 패러다임을 차용한 다중 패러다임 프로그래밍 언어입니다. 크게 보면 명령형 프로그래밍 패러다임, 객체지향 프로그래밍 패러다임, 함수형 프로그래밍 패러다임, 프로토콜 지향 프로그래밍 패러다임을 차용했습니다. 정확하게는 명령형과 객체지향 프로그래밍 패러다임을 기반으로 한 함수형 프로그래밍 패러다임과 프로토콜 지향 프로그래밍 패러다임을 지향합니다.”
스위프트 프로그래밍 3판, 야곰, p.42
위 설명에서 마지막 문장이 의미하는 바가 이제는 정확히 전달되었을 것이라고 생각한다.
📙 3. 스위프트에서의 명령형 프로그래밍과 객체 지향 프로그래밍
결국, 스위프트의 프로그래밍 패러다임에 대해 명확하게 정리하자면 아래와 같다.
스위프트는 기본적으로 프로토콜지향 프로그래밍과 함수형 프로그래밍을 지향하는 언어이다. 또한 스위프트는 다양한 프로그래밍 패러다임을 지원하기 때문에, 명령형 프로그래밍과 객체 지향 프로그래밍 역시 필요한 경우에 사용할 수 있다.
그렇다면 스위프트에서 명령형 프로그래밍과 객체 지향 프로그래밍을 언제 차용하면 좋을지 생각해보았다.
1. 명령형 프로그래밍
알고리즘을 단계별로 표현하고 구현하는 경우
상태의 변화를 추적하거나 변경해야 하는 경우
사용자 입력 처리, 파일 시스템 접근, 네트워크 통신 등 외부 자원을 다루는 작업
2. 객체 지향 프로그래밍
공통적인 속성과 동작을 가진 객체들을 추상화하고, 코드의 재사용성과 유지보수성을 높여야 할 경우
사용자 인터페이스를 구축하는 경우
(스위프트의 UI 프레임워크인 UIKit
과 AppleKit
은 기본적으로 클래스 계층과 상속에 기반한 아키텍처를 갖고 있기 때문)