개발블로그

[디자인 패턴] 전략패턴 본문

STUDY/CS

[디자인 패턴] 전략패턴

devmel 2026. 9. 4. 10:59
Contents 접기
 

개념

필요성

프로그램을 만들다 보면 같은 흐름 안에서 일부 동작만 달라지는 경우가 있음.

 

의미

실행할 알고리즘이나 정책을 별도의 객체로 분리하고, 실행 시점에 필요한 전략을 선택해서 사용하는 패턴

 

ex) 할인 예시에서의 전략

정액할인 - 일정 금액을 할인하는 방식

정률 할인 - 주문 금액의 일정 비율을 할인하는 방식

VIP 할인 - 회원 등급에 따라 더 높은 비율을 할인하는 방식

쿠폰 할인 - 쿠폰 조건에 따라 할인 금액을 계산하는 방식 

 

핵심 구조

구성 요소 역할 예시
Context 전략을 사용하는 객체 주문 가격 계산기
Strategy 공통으로 따라야 할 전략의 형태 할인 정책
Concrete Strategy 실제 동작을 구현한 구체적인 전략 정액 할인, 정률 할인, VIP 할인

 

 


 

예시

 

ex) 주문금액에 할인을 적용

전체흐름 : 주문 금액을 계산하고, 할인 금액을 반영하고, 최종 금액을 반환한다

 

하지만, 할인 방식은 상황에 따라 달라질 수 있음


할인 방식 설명
정액 할인 일정 금액을 할인한다.
정률 할인 주문 금액의 일정 비율을 할인한다.
등급 할인 회원 등급에 따라 할인율이 달라진다.
쿠폰 할인 쿠폰 조건에 따라 할인 금액이 달라진다.

 

처음에는 조건문으로 할인 방식을 처리할 수 있음

def calculate_price(price, discount_type):
    if discount_type == "fixed":
        return price - 1000
    elif discount_type == "rate":
        return price * 0.9
    elif discount_type == "vip":
        return price * 0.8

    return price

 

할인 방식이 적고 변경 가능성이 낮다면 충분히 단순한 해결 방법이 될 수 있지만, 

할인 방식이 계속 추가되거나 각 할인 정책의 계산 규칙이 복잡해지면 문제가 생김

 

ex)


문제 설명
수정 범위 증가 할인 정책이 추가될 때마다 기존 함수를 수정해야 한다.
책임 혼합 가격 계산 흐름과 할인 정책별 계산 로직이 한곳에 섞인다.
테스트 어려움 각 할인 정책을 독립적으로 테스트하기 어렵다.
실수 가능성 증가 조건문을 추가하면서 기존 조건 순서나 기본값을 잘못 건드릴 수 있다.

=> 바뀔 가능성이 높은 할인 정책이 하나의 함수 안에 계속 쌓이게 됨

 

예를들어 이벤트 할인 정책만 수정하고 싶어도 calculate_price()전체를 열어야 한다

이 함수 안에는 다른 할인 정책도 함께 들어 있기 때문에, 의도하지 않게 기존 로직을 건드릴 가능성이 생김

또한 할인 정책별 규칙이 복잡해지면 함수의 역할도 흐려진다. 

(가격 계산 함수인지? 할인 정책을 선택하는 함수인지?)

 

이런 상황에서는 바뀌는 부분을 분리할 필요가 있음. 

이런 할인 정책들을 별도의 객체로 분리하기

 

먼저 모든 할인 정책이 공통으로 따라야 할 형태를 정함

class DiscountStrategy:
    def discount(self, price):
        pass

=> 구체적으로 할인 계산을 직접 하지 않는다. 

대신 할인 정책이라면 discount()라는 동작을 제공해야 한다는 기준을 만든다. 

 

이제 각 할인 방식을 별도의 클래스로 구현

# 정액 할인 계산
class FixedDiscount(DiscountStrategy):
    def discount(self, price):
        return price - 1000

# 정률 할인 계산
class RateDiscount(DiscountStrategy):
    def discount(self, price):
        return price * 0.9

# VIP 할인 계산
class VipDiscount(DiscountStrategy):
    def discount(self, price):
        return price * 0.8

 => 각 클래스는 자신이 맡은 할인 방식만 책임 짐

 

이제 가격 계산 흐름은 구체적인 할인 방식에 직접 의존하지 않고, 할인 전략을 받아서 사용

class PriceCalculator:
    def __init__(self, discount_strategy):
        self.discount_strategy = discount_strategy

    def calculate(self, price):
        return self.discount_strategy.discount(price)

 

PriceCalculator는 어떤 할인 정책이 들어오는지 알 필요가 없음.

그저 전달받은 전략 객체의 discount()를 호출함

calculator = PriceCalculator(FixedDiscount())
print(calculator.calculate(10000))  # 9000

calculator = PriceCalculator(RateDiscount())
print(calculator.calculate(10000))  # 9000

calculator = PriceCalculator(VipDiscount())
print(calculator.calculate(10000))  # 8000

 

구분 역할
PriceCalculator 가격 계산 흐름을 담당
DiscountStrategy 할인 정책의 공통 형태를 정의
FixedDiscount, RateDiscount, VipDiscount 실제 할인 계산을 담당

 

새로운 할인 정책이 추가되어도 기존 PriceCalculator를 수정하지 않아도 됨

 => 쿠폰 할인을 사용하고 싶다면 새로운 전략 객체를 전달하면 됨 

class CouponDiscount(DiscountStrategy):
    def discount(self, price):
        return price - 3000

 

 

 


 

장점

장점

장점 설명
분기 로직 감소 여러 동작을 하나의 조건문에 계속 추가하지 않아도 된다
책임 분리 공통 흐름과 세부 동작을 분리할 수 있다
확장 용이 새로운 동작을 기존 코드 수정 대신 새로운 전략으로 추가할 수 있다
교체 용이 실행 시점에 필요한 전략을 바꿔 사용할 수 있다
테스트 용이 각 전략을 독립적으로 테스트할 수 있다. 

 

 

효과적인 상황

상황 이유
같은 목적을 가진 여러 동작이 있는 경우 동작별로 전략 객체를 나눌 수 있다.
세부 동작이 자주 추가되거나 변경되는 경우 기존 흐름을 덜 수정하고 확장할 수 있다.
실행 시점에 동작을 선택해야 하는 경우 필요한 전략을 외부에서 전달해 사용할 수 있다.
동작별 테스트가 필요한 경우 전략 단위로 테스트하기 쉽다.

 

 

 


 

주의점

 

조건문이 있다는 이유만으로 무조건 전략 패턴을 적용하면 오히려 구조가 복잡해질 수 있음

 

질문 의미
바뀌는 동작이 명확한가? 무엇을 전략으로 분리할지 확인한다.
전략이 여러 개이거나 늘어날 가능성이 있는가? 패턴 적용의 이점이 있는지 판단한다.
전략별 로직이 독립적으로 관리될 만큼 복잡한가? 클래스 분리 비용보다 얻는 이점이 큰지 본다.
전략 선택 책임은 어디에 둘 것인가? 전략 생성과 선택 로직이 흩어지지 않게 한다.
조건문이 더 단순하지 않은가? 과한 설계를 피한다.