[Swift Apple ] Swift 프로토콜 기반 설계 패턴

 프로토콜 기반 설계 패턴을 집중적으로 다뤄보겠습니다. Swift에서는 “상속”보다 프로토콜을 중심으로 유연하고 확장성 있는 코드를 설계하는 것이 핵심입니다.  이 글에서는 실전에서 자주 사용되는 대표적인 패턴 Strategy, Delegate, Factory 패턴을 프로토콜을 활용해 어떻게 설계하고, 왜 이렇게 하면 더 좋은지를 예제와 함께 설명해드릴게요!


[Swift Apple ] Swift 프로토콜 기반 설계 패턴
 [Swift Apple ] Swift 프로토콜 기반 설계 패턴




🚀 Swift 프로토콜 기반 설계 패턴

– 유연하고 확장 가능한 코드 작성을 위한 전략!


✅ 왜 프로토콜 기반으로 설계할까?

  • 상속(OOP) 은 계층 구조가 복잡해지고, 유연성이 떨어짐

  • 프로토콜 기반 설계는:

    • 다형성을 쉽게 구현

    • 코드 재사용성과 테스트 효율을 높임

    • 의존성을 낮추고 확장성 향상

    • Swift의 프로토콜 지향 프로그래밍(POP) 철학에 부합



1️⃣ Strategy 패턴 ✨

알고리즘을 런타임에 교체할 수 있는 패턴

프로토콜을 사용해 다양한 전략(방법)을 정의하고,
상황에 따라 전략을 쉽게 변경 가능!


예제: 결제 방식 선택하기

protocol PaymentStrategy {
    func pay(amount: Int)
}

struct CardPayment: PaymentStrategy {
    func pay(amount: Int) {
        print("💳 카드로 \(amount)원 결제")
    }
}

struct CashPayment: PaymentStrategy {
    func pay(amount: Int) {
        print("💵 현금으로 \(amount)원 결제")
    }
}

class PaymentContext {
    var strategy: PaymentStrategy

    init(strategy: PaymentStrategy) {
        self.strategy = strategy
    }

    func executePayment(amount: Int) {
        strategy.pay(amount: amount)
    }
}

사용하기:

let context = PaymentContext(strategy: CardPayment())
context.executePayment(amount: 5000)   // 💳 카드로 5000원 결제
✅ 새로운 결제 방식이 추가돼도 코드 수정 없이 확장 가능!


 

Strategy 패턴
Strategy 패턴






2️⃣ Delegate 패턴 🔄

객체 간 소통을 위한 대표적인 패턴

프로토콜을 통해 역할(Delegate) 을 정의하고,
이벤트나 데이터를 전달하는 구조.


예제: 다운로드 완료 알림

protocol DownloadDelegate: AnyObject {
    func downloadDidFinish()
}

class Downloader {
    weak var delegate: DownloadDelegate?

    func startDownload() {
        print("📥 다운로드 중...")
        // 다운로드 완료 후 호출
        delegate?.downloadDidFinish()
    }
}

class ViewController: DownloadDelegate {
    func downloadDidFinish() {
        print("✅ 다운로드가 완료되었습니다!")
    }
}

사용하기:

let downloader = Downloader()
let vc = ViewController()
downloader.delegate = vc
downloader.startDownload()
✅ iOS 개발(UITableView, UICollectionView 등)에서 가장 많이 활용!


Delegate 패턴
Delegate 패턴



3️⃣ Factory 패턴 🏭


객체 생성을 프로토콜로 추상화


생성 로직을 감추고, 프로토콜 기반으로 유연하게 객체를 반환.


예제: 동물 객체 생성기

protocol Animal {
    func speak()
}

struct Dog: Animal {
    func speak() { print("멍멍!") }
}

struct Cat: Animal {
    func speak() { print("야옹~") }
}

class AnimalFactory {
    static func createAnimal(type: String) -> Animal {
        switch type {
        case "Dog": return Dog()
        case "Cat": return Cat()
        default: fatalError("알 수 없는 동물 타입")
        }
    }
}

사용하기:

let pet = AnimalFactory.createAnimal(type: "Dog")
pet.speak()   // 멍멍!
✅ 객체 생성 방식이 바뀌어도 호출부는 변경 없음!



Factory 패턴
Factory 패턴



💡 프로토콜 기반 패턴의 장점

  • 유연성: 새로운 기능 추가 시 기존 코드 변경 최소화

  • 의존성 감소: 구체 타입이 아닌, 추상화(Protocol) 에 의존

  • 테스트 용이성: Mock 객체로 쉽게 테스트 가능

  • 확장성: Open-Closed Principle (확장에 열려 있고, 수정에는 닫혀 있음)




✏️ 실습 아이디어

  1. Strategy 패턴으로 배송 방법 선택 시스템 만들기

    (예: 빠른 배송, 일반 배송, 무료 배송)

  2. Delegate 패턴으로 사용자 입력 이벤트 처리기 구현하기

    (예: 버튼 클릭 시 이벤트 전달)

  3. Factory 패턴으로 UI 요소 생성기 설계하기

    (예: 테마별 버튼 생성)





✅ 1. Strategy 패턴 – 배송 방법 선택 시스템 만들기

// 1️⃣ 배송 전략 프로토콜
protocol DeliveryStrategy {
    func deliver(item: String)
}

// 2️⃣ 전략 구현체들
struct FastDelivery: DeliveryStrategy {
    func deliver(item: String) {
        print("🚚 빠른 배송으로 \(item) 배송 중!")
    }
}

struct StandardDelivery: DeliveryStrategy {
    func deliver(item: String) {
        print("📦 일반 배송으로 \(item) 배송 중!")
    }
}

struct FreeDelivery: DeliveryStrategy {
    func deliver(item: String) {
        print("🐌 무료 배송으로 \(item) 배송 중 (시간 오래 걸림)")
    }
}

// 3️⃣ 컨텍스트
class DeliveryContext {
    var strategy: DeliveryStrategy

    init(strategy: DeliveryStrategy) {
        self.strategy = strategy
    }

    func send(item: String) {
        strategy.deliver(item: item)
    }
}

// 사용 예시
let fast = DeliveryContext(strategy: FastDelivery())
fast.send(item: "아이폰")

let free = DeliveryContext(strategy: FreeDelivery())
free.send(item: "책")



Strategy 패턴 – 배송 방법 선택 시스템 만들기
Strategy 패턴 – 배송 방법 선택 시스템 만들기



✅ 2. Delegate 패턴 – 사용자 입력 이벤트 처리기 구현하기

// 1️⃣ 델리게이트 프로토콜 정의
protocol ButtonDelegate: AnyObject {
    func didTapButton()
}

// 2️⃣ 버튼 클래스
class CustomButton {
    weak var delegate: ButtonDelegate?

    func tap() {
        print("🔘 버튼이 눌렸습니다.")
        delegate?.didTapButton()
    }
}

// 3️⃣ 델리게이트 구현체
class ViewController: ButtonDelegate {
    func didTapButton() {
        print("✅ 버튼 클릭 이벤트 처리 완료!")
    }
}

// 사용 예시
let button = CustomButton()
let controller = ViewController()
button.delegate = controller
button.tap()
weak 키워드로 순환 참조 방지 (클래스에서만 사용 가능)



Delegate 패턴 – 사용자 입력 이벤트 처리기 구현하기
Delegate 패턴 – 사용자 입력 이벤트 처리기 구현하기



✅ 3. Factory 패턴 – 테마별 UI 버튼 생성기

import UIKit

// 1️⃣ 버튼 스타일 프로토콜
protocol ThemedButton {
    func createButton() -> UIButton
}

// 2️⃣ 구체적인 버튼 스타일
struct DarkThemeButton: ThemedButton {
    func createButton() -> UIButton {
        let button = UIButton(type: .system)
        button.setTitle("Dark Button", for: .normal)
        button.backgroundColor = .black
        button.setTitleColor(.white, for: .normal)
        return button
    }
}

struct LightThemeButton: ThemedButton {
    func createButton() -> UIButton {
        let button = UIButton(type: .system)
        button.setTitle("Light Button", for: .normal)
        button.backgroundColor = .white
        button.setTitleColor(.black, for: .normal)
        return button
    }
}

// 3️⃣ 팩토리
class ButtonFactory {
    static func makeButton(theme: String) -> UIButton {
        switch theme.lowercased() {
        case "dark": return DarkThemeButton().createButton()
        case "light": return LightThemeButton().createButton()
        default: return UIButton(type: .system)
        }
    }
}

// 사용 예시 (UIKit 기반 프로젝트에서 ViewController 내부에 추가 가능)
let darkButton = ButtonFactory.makeButton(theme: "dark")
let lightButton = ButtonFactory.makeButton(theme: "light")
⚠️ 이 코드는 UIKit 환경에서만 작동합니다 (import UIKit 필요)





✅ 요약

패턴

기능

핵심 개념

Strategy

동적으로 배송 전략 선택

알고리즘 교체

Delegate

버튼 클릭 이벤트 처리

역방향 통신

Factory

스타일별 버튼 생성

객체 생성 분리





🎯 마무리 정리

패턴명

프로토콜 역할

활용 포인트

Strategy

다양한 알고리즘 정의

런타임 전략 변경

Delegate

이벤트 전달 규칙 정의

객체 간 소통

Factory

생성 방식 추상화

유연한 객체 생성

Swift 개발에서 프로토콜 기반 설계 패턴은

깔끔하고 확장성 있는 아키텍처를 만드는 필수 도구입니다.



댓글 쓰기