전략 strategy 패턴 - 자바스크립트 예제
요약
전략 strategy 패턴을 타입스크립트 코드와 함께 알아봅니다.
전략 패턴 요약
패턴 종류 | 핵심 키워드 |
---|---|
전략 (Strategy) | 알고리즘을 캡슐화 , 동적으로 교체 |

전략 (Strategy) 패턴
전략 패턴은 알고리즘군(a family of algorithms)을 정의하고, 각 알고리즘을 캡슐화하여, 필요에 따라 서로 교체해서 사용할 수 있게 만드는 디자인 패턴입니다. 즉, '무엇을' 할 것인지는 Context
(문맥) 객체에 그대로 두고, '어떻게' 할 것인지를 다양한 Strategy
(전략) 객체에 위임하는 방식입니다.
현실 세계의 예로 '경로 찾기' 를 들 수 있습니다. 서울에서 부산까지 가는 방법에는 KTX, 버스, 비행기 등 여러 가지가 있습니다. 어떤 교통수단을 선택하든 '서울에서 부산으로 이동한다'는 목표(Context)는 동일하지만, 이동 방식(Strategy)은 달라집니다. 전략 패턴은 이처럼 다양한 '방법(알고리즘)'들을 부품처럼 쉽게 갈아 끼울 수 있도록 해줍니다.
기본 구조
- Context: 전략을 사용하는 주체입니다. 내부에
Strategy
인터페이스에 대한 참조를 가지고 있으며, 클라이언트의 요청을 받으면 자신이 가진 전략 객체에게 작업을 위임합니다. - Strategy: 모든 구체적인 전략 클래스들이 구현해야 하는 공통 인터페이스입니다. 보통 알고리즘을 실행하는 단일 메서드(예:
execute()
)를 정의합니다. - ConcreteStrategy:
Strategy
인터페이스를 구현하여 실제 알고리즘을 정의하는 클래스입니다. (예:BusStrategy
,TrainStrategy
)
예시: 결제 시스템 구현하기
온라인 쇼핑몰에서 다양한 결제 방법을 지원하는 시스템을 만든다고 가정해봅시다. 사용자는 신용카드, 카카오페이, 네이버페이 등 여러 방법으로 결제할 수 있어야 합니다.
먼저, 모든 결제 전략이 따라야 할 PaymentStrategy
인터페이스를 정의합니다.
// Strategy: 공통 결제 인터페이스
class PaymentStrategy {
pay(amount) {
throw new Error("pay() 메서드는 반드시 구현되어야 합니다.");
}
}
이제 구체적인 결제 방법들을 ConcreteStrategy
로 구현합니다.
// ConcreteStrategy A: 신용카드 결제
class CreditCardStrategy extends PaymentStrategy {
constructor(name, cardNumber) {
super();
this.name = name;
this.cardNumber = cardNumber;
}
pay(amount) {
console.log(
`${amount}원을 ${this.name}님의 신용카드(${this.cardNumber})로 결제합니다.`
);
// 실제 카드사 연동 로직...
}
}
// ConcreteStrategy B: 카카오페이 결제
class KakaoPayStrategy extends PaymentStrategy {
constructor(email) {
super();
this.email = email;
}
pay(amount) {
console.log(`${amount}원을 ${this.email} 카카오페이 계정으로 결제합니다.`);
// 실제 카카오페이 API 연동 로직...
}
}
다음으로, 결제 프로세스를 담당할 ShoppingCart
(장바구니) 클래스를 Context
로 만듭니다.
// Context: 결제 전략을 사용하는 장바구니
class ShoppingCart {
constructor(amount) {
this.amount = amount;
this.paymentStrategy = null; // 처음에는 전략이 설정되지 않음
}
// 클라이언트가 동적으로 전략을 변경할 수 있도록 setter를 제공
setPaymentStrategy(strategy) {
this.paymentStrategy = strategy;
}
// 결제 실행
checkout() {
if (!this.paymentStrategy) {
console.log("결제 방법이 선택되지 않았습니다.");
return;
}
this.paymentStrategy.pay(this.amount);
}
}
// 클라이언트 코드
const cart = new ShoppingCart(10000); // 10,000원짜리 상품
// 신용카드로 결제하기
const creditCard = new CreditCardStrategy("김재현", "1234-5678-9012-3456");
cart.setPaymentStrategy(creditCard);
cart.checkout();
// 출력: 10000원을 김재현님의 신용카드(1234-5678-9012-3456)로 결제합니다.
console.log("
--- 결제 방법을 변경합니다 ---");
// 카카오페이로 결제하기
const kakaoPay = new KakaoPayStrategy("test@kakao.com");
cart.setPaymentStrategy(kakaoPay);
cart.checkout();
// 출력: 10000원을 test@kakao.com 카카오페이 계정으로 결제합니다.
ShoppingCart
클래스는 어떤 결제 방법이 선택되었는지 구체적으로 알 필요가 없습니다. 그저 paymentStrategy
에 할당된 객체의 pay()
메서드를 호출할 뿐입니다. 만약 '네이버페이'라는 새로운 결제 수단이 추가되어도, NaverPayStrategy
클래스를 새로 만들기만 하면 ShoppingCart
코드는 전혀 수정할 필요가 없습니다.
이처럼 전략 패턴은 **'개방-폐쇄 원칙(OCP)'**을 잘 따르는 설계입니다. 새로운 기능(전략) 추가에는 열려 있고, 기존 코드의 수정에는 닫혀 있습니다.
전략 패턴 중요 키워드 🔑
- 알고리즘을 캡슐화하고 동적으로 교체할 수 있습니다.
- '어떻게' 하는가(How)를 '무엇을' 하는가(What)로부터 분리합니다.
- 위임(Delegation) 을 통해 문제를 해결합니다. (Context가 Strategy에게 위임)
if-else
나switch
문으로 가득 찬 코드를 리팩토링하는 좋은 방법입니다.- 개방-폐쇄 원칙(OCP)을 만족시킵니다.
정처기 실기 대비 문제
문제 | 알고리즘 군을 정의하고(추상 클래스) 같은 알고리즘을 각각 하나의 클래스로 캡슐화한 다음, 필요할 때 서로 교환해서 사용할 수 있게 하는 패턴을 보기에서 고르시오 |
보기 | |
답변 | |
정답 | 정답 보기 |