티스토리 뷰

반응형

Swift로 배우는 전략 패턴 (Strategy Pattern) – Head First 디자인 패턴 기반 설명

『Head First Design Patterns』는 디자인 패턴을 쉽고 직관적으로 설명하는 명저입니다.

이번 글에서는 그중에서도 전략 패턴(Strategy Pattern)을 iOS 개발 환경에 맞게 Swift로 구현해보며 학습해보겠습니다.


🧠 전략 패턴이란?

전략 패턴(Strategy Pattern)은 알고리즘(또는 행위)을 객체로 캡슐화하여 상호 교환 가능하게 만드는 디자인 패턴입니다.

핵심 개념:

"행동을 클래스로 분리해서, 실행 중에 바꿀 수 있게 만든다."


✅ 전략 패턴 요약 정리

| 항목 | 내용 |

| ------ | ------ |

| 패턴명 | 전략(Strategy) 패턴 |

| 목적 | 알고리즘을 런타임에 자유롭게 교체 가능하게 만들기 |

| 특징 | 1개의 인터페이스 + 여러 전략 구현체 + Context 객체 |

| 장점 | OCP 만족, 중복 제거, 행동 교체 가능 |

| 자주 쓰이는 경우 | 로그인 방식, 정렬 기준, 렌더링 전략 등 |


📖 책 속 예시: 오리 시뮬레이터

Head First 디자인 패턴에서 소개된 전략 패턴의 대표 예시는 오리 시뮬레이터입니다.

  • Duck 객체는 fly()quack() 같은 행동을 가짐
  • 행동은 상황에 따라 달라지므로, 서브클래싱보다 구성(Composition)을 통해 캡슐화
  • FlyBehavior, QuackBehavior라는 인터페이스로 행동을 외부에서 주입

🧪 iOS에서의 실제 예: 로그인 전략

iOS 앱에서 다음과 같은 로그인 기능을 제공한다고 가정해봅시다:

  • Apple ID 로그인
  • Google 계정 로그인
  • 게스트 로그인

이 3가지 로그인 방식은 서로 방식이 다르지만, "로그인"이라는 기능은 같기 때문에 전략 패턴이 유용합니다.


1. LoginStrategy 프로토콜 정의


protocol LoginStrategy {

    func login(completion: @escaping (Bool) -> Void)

}

2. 전략 클래스 구현


struct AppleLoginStrategy: LoginStrategy {

    func login(completion: @escaping (Bool) -> Void) {

        print("🍎 Apple ID로 로그인 시도")

        // Apple 로그인 로직

        completion(true)

    }

}



struct GoogleLoginStrategy: LoginStrategy {

    func login(completion: @escaping (Bool) -> Void) {

        print("🔍 Google 계정으로 로그인 시도")

        // Google 로그인 로직

        completion(true)

    }

}



struct GuestLoginStrategy: LoginStrategy {

    func login(completion: @escaping (Bool) -> Void) {

        print("👤 게스트 로그인")

        completion(true)

    }

}

3. Context 객체: LoginManager


final class LoginManager {

    private var strategy: LoginStrategy



    init(strategy: LoginStrategy) {

        self.strategy = strategy

    }



    func setStrategy(_ strategy: LoginStrategy) {

        self.strategy = strategy

    }



    func login(completion: @escaping (Bool) -> Void) {

        strategy.login(completion: completion)

    }

}

4. ViewController에서 사용


class LoginViewController: UIViewController {

    let loginManager = LoginManager(strategy: GuestLoginStrategy())



    @IBAction func appleLoginTapped() {

        loginManager.setStrategy(AppleLoginStrategy())

        loginManager.login { success in

            print("로그인 성공 여부: \(success)")

        }

    }



    @IBAction func googleLoginTapped() {

        loginManager.setStrategy(GoogleLoginStrategy())

        loginManager.login { success in

            print("로그인 성공 여부: \(success)")

        }

    }



    @IBAction func guestLoginTapped() {

        loginManager.setStrategy(GuestLoginStrategy())

        loginManager.login { success in

            print("로그인 성공 여부: \(success)")

        }

    }

}

🧩 iOS에서 자주 쓰이는 전략 패턴 예시

| 상황 | 전략이 필요한 이유 |

| ------ | ------------------- |

| 이미지 필터 처리 | 다양한 필터 적용 방식(흑백, 세피아 등)을 런타임에 변경 가능 |

| 정렬 기준 선택 | 리스트 정렬 기준을 이름순 / 날짜순 등으로 교체 |

| 광고 A/B 전략 | 유저 그룹별 광고 표시 전략 분리 |

| 데이터 저장 방식 | UserDefaults, Keychain, File 방식 전략적으로 교체 |


💡 고급 팁: 클로저로도 전략을 표현할 수 있다

Swift에서는 꼭 클래스로만 전략을 구현하지 않아도 됩니다.

간단한 전략은 클로저로 대체할 수 있습니다.


typealias LoginClosure = (@escaping (Bool) -> Void) -> Void



let appleLogin: LoginClosure = { completion in

    print("🍎 Apple로 로그인")

    completion(true)

}



let guestLogin: LoginClosure = { completion in

    print("👤 게스트로 로그인")

    completion(true)

}

이런 방식은 간단한 상황에서는 더욱 가볍게 전략을 구성할 수 있어 유용합니다.


🎯 결론

전략 패턴은 다음과 같은 경우에 아주 효과적입니다:

  • 동작을 바꿔야 하지만, 코드를 수정하고 싶지 않을 때
  • 여러 조건에 따라 다른 처리를 해야 할 때
  • 테스트 가능성과 코드 확장성을 확보하고 싶을 때

Swift와 iOS 프로젝트에서 전략 패턴을 적극적으로 활용해보세요!

한층 더 유연하고 유지보수하기 쉬운 앱을 만들 수 있습니다.


📚 참고

『Head First Design Patterns』, O’Reilly Media

Swift 공식 문서, Apple Developer Guide

반응형
댓글