본문 바로가기
Artificial Intelligence/60. Python

[PYTHON] 전략(Strategy) 패턴을 파이썬의 일급 객체 특성으로 구현하는 3가지 방법과 클래스와의 결정적 차이 7가지

by Papa Martino V 2026. 4. 1.
728x90

전략 패턴(Strategy Pattern)
전략 패턴 (Strategy Pattern)

 

소프트웨어 디자인 패턴의 고전으로 불리는 전략 패턴(Strategy Pattern)은 특정 알고리즘을 캡슐화하여 런타임에 교체할 수 있게 만드는 강력한 도구입니다. 하지만 Java나 C++ 같은 정적 타입 언어에서 익힌 '인터페이스-구현체' 방식의 엄격한 클래스 구조를 파이썬에 그대로 대입하는 것은 파이썬이 가진 잠재력을 절반만 사용하는 것과 같습니다. 파이썬에서 함수는 '일급 객체(First-class Object)'입니다. 즉, 함수를 변수에 할당하고, 인자로 전달하며, 반환값으로 사용할 수 있습니다. 이 특성을 활용하면 복잡한 추상 베이스 클래스(ABC) 없이도 훨씬 간결하고 유지보수가 쉬운 전략 패턴을 완성할 수 있습니다. 본 가이드에서는 실무 개발자가 즉시 활용할 수 있는 테크닉과 7가지 구체적인 해결 사례를 제시합니다.


1. 클래스 기반 전략 vs 파이썬 함수형 전략의 핵심 차이

전통적인 방식은 알고리즘마다 클래스를 정의해야 하므로 코드 비대화(Boilerplate)가 발생합니다. 반면 파이썬의 일급 객체 특성을 활용하면 함수 그 자체를 전략으로 취급하여 구조를 단순화할 수 있습니다.

비교 항목 전통적 클래스 전략 패턴 파이썬 함수형 전략 패턴
구현 방식 인터페이스 상속 및 메서드 오버라이딩 함수 참조 또는 Callable 객체 전달
코드 길이 비교적 길고 구조적임 매우 짧고 직관적임
유연성 정해진 인터페이스 구조를 따라야 함 람다(Lambda) 등 익명 함수 결합 가능
상태 저장 인스턴스 속성(self)에 저장 클로저(Closure)를 통해 캡슐화
결합도 상위 타입에 대한 의존성 존재 덕 타이핑(Duck Typing) 기반 낮은 결합도

2. 실무 해결을 위한 7가지 Sample Examples

이 섹션에서는 다양한 비즈니스 로직 상황에서 함수형 전략 패턴을 적용하여 문제를 해결하는 구체적인 사례를 소개합니다.

Example 1: 동적 할인 정책 시스템 (기본 함수 참조)

가장 기본적인 형태로, 결제 모듈에서 할인 알고리즘을 함수 객체로 갈아끼우는 방식입니다.


def bulk_discount(order):
    return order.total() * 0.1 if order.items >= 10 else 0

def loyalty_discount(order):
    return order.total() * 0.05

class Order:
    def __init__(self, price, items, strategy=None):
        self.price = price
        self.items = items
        self.strategy = strategy

    def total(self):
        return self.price * self.items

    def due(self):
        discount = self.strategy(self) if self.strategy else 0
        return self.total() - discount

# 사용 예시
order = Order(100, 12, strategy=bulk_discount)
print(f"결제 금액: {order.due()}")
    

Example 2: 데이터 직렬화 엔진 (Serializer Strategy)

데이터를 JSON, XML, YAML 등 다양한 포맷으로 출력할 때 유용합니다.


import json

def to_json(data):
    return json.dumps(data)

def to_plain_text(data):
    return ", ".join(f"{k}:{v}" for k, v in data.items())

def export_data(data, format_func):
    return format_func(data)

raw_info = {"name": "Gemini", "role": "Assistant"}
print(export_data(raw_info, to_json))
    

Example 3: 파일 압축 매니저 (Closure 활용)

특정 옵션이 포함된 전략을 런타임에 생성하여 전달하는 클로저 패턴입니다.


def make_compression_strategy(level):
    def compress(data):
        return f"Compressing {data} with level {level}"
    return compress

fast_compress = make_compression_strategy(level=1)
deep_compress = make_compression_strategy(level=9)

print(fast_compress("MyData"))
    

Example 4: 유효성 검사 파이프라인 (Lambda 결합)

간단한 로직은 람다를 사용하여 클래스 선언 없이 즉석에서 전략을 수립합니다.


def validate_user(user, constraint):
    return constraint(user)

user_data = {"age": 25, "premium": True}

# 18세 이상이며 프리미엄 회원인지 검사하는 전략
is_eligible = validate_user(user_data, lambda u: u['age'] > 18 and u['premium'])
print(f"승인 여부: {is_eligible}")
    

Example 5: 로그 기록 대상 제어 (Callable 객체)

함수뿐만 아니라 __call__ 메서드가 구현된 인스턴스를 전략으로 활용합니다.


class DatabaseLogger:
    def __init__(self, db_url):
        self.url = db_url
    def __call__(self, message):
        print(f"Log to {self.url}: {message}")

def console_logger(message):
    print(f"Console: {message}")

def log_event(msg, strategy):
    strategy(msg)

db_log = DatabaseLogger("mysql://localhost:3306")
log_event("System Start", db_log)
    

Example 6: 정렬 기준의 동적 변경 (Higher-order Function)

파이썬의 내장 sorted 함수는 전략 패턴이 함수형으로 녹아든 완벽한 사례입니다.


products = [('apple', 2500), ('banana', 1500), ('cherry', 4000)]

# 가격 순으로 정렬하는 전략 제공
by_price = sorted(products, key=lambda x: x[1])
# 이름 길이 순으로 정렬하는 전략 제공
by_name_len = sorted(products, key=lambda x: len(x[0]))
    

Example 7: 비즈니스 규칙 엔진 (Registry Pattern 연동)

딕셔너리에 함수를 등록하여 조건에 맞는 전략을 자동으로 선택하게 합니다.


def route_air(package): return f"Flying {package}"
def route_sea(package): return f"Shipping {package}"

shipping_methods = {
    'express': route_air,
    'standard': route_sea
}

def ship(package, priority):
    strategy = shipping_methods.get(priority, route_sea)
    return strategy(package)

print(ship("Smartphone", "express"))
    

3. 결론: 왜 함수형 전략을 선택해야 하는가?

파이썬의 철학은 "단순한 것이 복잡한 것보다 낫다(Simple is better than complex)"입니다. 수많은 클래스 파일을 생성하고 상속 관계를 추적하는 대신, 일급 객체로서의 함수를 활용하면 다음과 같은 이득을 얻을 수 있습니다.

  • 가독성: 로직이 분산되지 않고 호출부 근처에서 확인 가능합니다.
  • 성능: 클래스 인스턴스화 오버헤드가 줄어듭니다.
  • 테스트: 함수는 독립적이므로 모킹(Mocking)과 유닛 테스트가 매우 간단해집니다.

4. 내용의 출처 및 참고 자료

본 포스팅의 전문 지식은 다음의 자료들을 바탕으로 재구성되었습니다.

  • Fluent Python (2nd Edition) by Luciano Ramalho - "Function as First-Class Objects"
  • Design Patterns: Elements of Reusable Object-Oriented Software by GoF
  • Python Official Documentation: functools and abc modules
  • Refactoring.Guru - "Strategy Pattern Implementation in Python"
728x90