OCP (open-closed principle) & 전략 패턴

정태수·2025년 4월 22일

조건문 나열 + 함수 호출 방식

function calculateMembershipDiscount(tier) {
  if (tier === 'BRONZE') return 0.01;
  if (tier === 'SILVER') return 0.02;
  if (tier === 'GOLD') return 0.03;
  if (tier === 'PLATINUM') return 0.05;
  if (tier === 'DIAMOND') return 0.1;
  return 0;
}

function calculateSeasonDiscount(date) {
  const month = date.getMonth();
  if (month >= 2 && month <= 4) return 0.02; // 봄
  if (month >= 5 && month <= 7) return 0.03; // 여름
  if (month >= 8 && month <= 10) return 0.02; // 가을
  return 0.04; // 겨울
}

// 나머지 할인 함수들...

function calculateDiscount(user, product, date, paymentMethod) {
  let discount = 0;

  discount += calculateMembershipDiscount(user.tier);
  discount += calculateSeasonDiscount(date);
  discount += calculateDayOfWeekDiscount(date.getDay());
  discount += calculateCategoryDiscount(product.category);
  discount += calculatePaymentMethodDiscount(paymentMethod);

  return discount;
}

✔️장점:
간단하고 직관적임.
소규모 프로젝트에서는 빠르다.

❌ 단점:
할인 항목이 늘어난다면 로직이 길어진다.
각 할인함수가 calculateDiscount에 강하게 의존 (결합도가 높다)
새로운 할인 정책을 넣으려면 calculateDiscount에 또 추가 하는데 OCP 위반.
단위 테스트는 calculateHolidayDiscount()도, calculateDiscount()도 모두 테스트해야 함

OCP = "열려 있고, 닫혀 있어야 한다"

확장(Extension)에는 열려 있어야 함

수정(Modification)에는 닫혀 있어야 함

전략 패턴

// 할인 전략 함수들
const membershipDiscount = (user, product, date, paymentMethod) => {
  const rates = {
    'BRONZE': 0.01,
    'SILVER': 0.02,
    'GOLD': 0.03,
    'PLATINUM': 0.05,
    'DIAMOND': 0.1,
  };
  return rates[user.tier] || 0;
};

const seasonDiscount = (_, __, date) => {
  const month = date.getMonth();
  if (month >= 2 && month <= 4) return 0.02; // 봄
  if (month >= 5 && month <= 7) return 0.03; // 여름
  if (month >= 8 && month <= 10) return 0.02; // 가을
  return 0.04; // 겨울
};

const holidayDiscount = (_, __, date) => {
  const holidays = ['2025-01-01', '2024-12-25'];
  const today = date.toISOString().slice(0, 10);
  return holidays.includes(today) ? 0.05 : 0;
};

위 코드는 할인 방법, 아래 코드는 계산함수

// 전략들을 배열로 관리
const discountStrategies = [
  membershipDiscount,
  seasonDiscount,
  holidayDiscount,
  // 필요한 전략 함수들 추가
];

// 총 할인율 계산
function calculateDiscount(user, product, date, paymentMethod) {
  return discountStrategies.reduce((total, strategy) => {
    return total + strategy(user, product, date, paymentMethod);
  }, 0);
}

✔️ 장점:
전략을 객체화함으로써 할인 정책을 각각 캡슐화
새로운 할인 정책 추가 시 기존 코드를 수정할 필요가 없음 (OCP 충족)
테스트와 재사용성 극대화 (전략 하나만 독립적으로 테스트 가능)
동적으로 전략을 추가/제외 가능 (사용자나 상품에 따라 전략 조정 가능)
유지보수가 쉬움: 한 전략의 로직 수정이 다른 전략에 영향을 주지 않음

❌ 단점:
초기에 설계와 클래스 구조가 약간 복잡해짐
간단한 할인 정책이라면 오히려 과설계(overengineering)일 수 있음

요즘 설계에 대한 생각이 많아지면서 이런부분을 기록으로 남기는게 좋다고 생각했다.

profile
프론트엔드 개발자

0개의 댓글