[Design Pattern] Strategy

이성우·2026년 1월 31일

개발을 하다보면 기능이 추가되며 if/else, switch, 특정 mode등의 옵션 조합이 늘어날 수 있습니다. 이럴 때 적용하면 좋을 디자인 패턴인 Strategy(전략) 패턴에 대해서 알아보도록 하겠습니다.

❓Strategy란?

특정한 규칙을 객체/함수로 분리하고 런타임에 교체 가능하게 만드는 패턴
규칙(무엇을 할지)을 분리해두고 상황에 따라 갈아 끼워 쓰는 구조

도입 기준

  • 규칙이 2개 이상이고 앞으로 늘어날 가능성이 높음
  • 기능 추가 시 기존 분기(if/switch) 수정이 반복됨
  • 같은 흐름인데 규칙만 바뀌는 경우가 많음 (예: 정렬/할인/검증/계산 방식)
  • 분기 로직이 여러 파일로 퍼져 추적이 어려워짐

장점

  • 분기 감소: if/else,switch가 늘어나는 대신 전략이 늘어나는 구조로 전환

  • 확장에 유리: 새 규칙 추가가 기존 코드 수정이 아니라 전략 추가로 끝남

  • 테스트 용이: 전략 단위로 독립 테스트 가능

  • 책임 분리: 컨텍스트(흐름)와 규칙(알고리즘)이 분리되어 코드가 읽기 쉬움

  • 런타임 교체: 사용자 선택, 환경, 플래그에 따라 동작을 쉽게 바꿀 수 있음

📝 사용 예시

적용 전 (분기 증가)

type MemberGrade = 'NORMAL' | 'VIP';
type Coupon = 'NONE' | 'RATE10' | 'FIXED3000';

function calcPrice(price: number, grade: MemberGrade, coupon: Coupon) {
  let discounted = price;

  // 등급 할인
  if (grade === 'VIP') discounted *= 0.9;

  // 쿠폰 할인
  switch (coupon) {
    case 'RATE10':
      discounted *= 0.9;
      break;
    case 'FIXED3000':
      discounted -= 3000;
      break;
  }

  return Math.max(0, Math.floor(discounted));
}

규칙이 늘수록 calcPrice가 계속 커지고, VIP + 특정 쿠폰처럼 조합 버그가 생기기 쉬워진다.

적용 후 (전략 분리 + 조합)

// 전략(함수 타입 또는 인터페이스) 정의
type PricingStrategy = (price: number) => number;

// 전략 구현
const noDiscount: PricingStrategy = (p) => p;
const vipDiscount: PricingStrategy = (p) => p * 0.9;
const couponRate10: PricingStrategy = (p) => p * 0.9;
const couponFixed3000: PricingStrategy = (p) => p - 3000;

// 컨텍스트에서 전략 선택/조합
type MemberGrade = 'NORMAL' | 'VIP';
type Coupon = 'NONE' | 'RATE10' | 'FIXED3000';

function compose(...strategies: PricingStrategy[]): PricingStrategy {
  return (price) => strategies.reduce((p, s) => s(p), price);
}

// 쿠폰/정책처럼 종류가 늘어나는 값은 Record(Map)로 관리하는 게 확장에 유리
const couponStrategyMap: Record<Coupon, PricingStrategy> = {
  NONE: noDiscount,
  RATE10: couponRate10,
  FIXED3000: couponFixed3000,
};

function getPricingStrategy(grade: MemberGrade, coupon: Coupon): PricingStrategy {
  const gradeStrategy = grade === 'VIP' ? vipDiscount : noDiscount;
  return compose(gradeStrategy, couponStrategyMap[coupon]);
}

function calcPrice(price: number, grade: MemberGrade, coupon: Coupon) {
  const strategy = getPricingStrategy(grade, coupon);
  const result = strategy(price);
  return Math.max(0, Math.floor(result));
}

여기서 전략을 선택하는 로직까지 switch,if/else로 커지면 다시 분기 코드가 비대해질 수 있습니다.
쿠폰처럼 종류가 늘어나는 값은 Record(Map)으로 관리하면 확장에 더 유리합니다.

👍 결론

Strategy 패턴은 규칙이 늘어나는 지점을 격리시켜 유지보수 비용을 낮추는 방법입니다.

profile
안녕하세요!

0개의 댓글