개요
전략 패턴(strategy pattern)은 정책 패턴(policy pattern)이라고도 하며, 객체의 행위를 바꾸고 싶은 경우 '직접' 수정하지 않고 전략이라고 부르는 '캡슐화한 알고리즘'을 컨텍스트 안에서 바꿔주면서 상호 교체가 가능하게 만드는 패턴이다.
간단히 말해서 객체가 할 수 있는 행위들 각각을 전략으로 만들어 놓고, 동적으로 행위의 수정이 필요한 경우 전략을 바꾸는 것만으로 행위의 수정이 가능하도록 만드는 패턴이다.
즉, 어떤 일을 수행하는 알고리즘이 여러가지 일 때, 동작들을 미리 전략으로 정의함으로써 손쉽게 교체할 수 있는, 알고리즘 변형이 빈번하게 필요한 경우에 적합하다.
구조

- 전략 인터페이스: 모든 전략 구현체에 대한 공용 인터페이스
- 전략 알고리즘 객체들: 알고리즘, 행위, 동작을 객체로 정의한 구현체
- 컨텍스트: 알고리즘을 실행해야 할 때마다 해당 알고리즘과 연결된 전략 객체의 메서드 호출
- 클라이언트: 특정 전략 객체를 컨텍스트에 전달함으로써 전략을 등록하거나 변경하여 전략 알고리즘을 실행한 결과를 누린다.
프로그래밍에서의 컨텍스트(Context)란 콘텐츠(Contents)를 담는 그 무언가를 뜻하며, 어떤 객체를 핸들링하기 위한 접근 수단이다.
OOP의 집합체
GoF의 디자인 패턴 책에서는 전략 패턴을 다음과 같이 정의한다.
- 동일 계열의 알고리즘군을 정의하고
- 각각의 알고리즘을 캡슐화하여
- 이들이 상호 교환이 가능하도록 만든다.
- 알고리즘을 사용하는 클라이언트와 상관없이 독립적으로
- 알고리즘을 다양하게 변경할 수 있다.
사실 전략 패턴은 여러 객체 지향 문법 기법들인 SOLID 원칙의 OCP 원칙, DIP 원칙과 합성(composition), 다형성(polymorphism), 캡슐화(encapsulation) 등 OOP 기술들의 총 집합 버전이라고 보면 된다.

따라서 위의 전략 패턴의 정의를 다음과 같이 빗대어 설명하면 이해하기 쉽다.
- 동일 계열의 알고리즘군을 정의하고 → 전략 구현체로 정의
- 각각의 알고리즘을 캡슐화하여 → 인터페이스로 추상화
- 이들이 상호 교환이 가능하도록 만든다. → 합성(composition)으로 구성
- 알고리즘을 사용하는 클라이언트와 상관없이 독립적으로 → 컨텍스트 객체 수정없이
- 알고리즘을 다양하게 변경할 수 있다. → 메소드를 통해 전략 객체를 실시간으로 변경함으로써 전략을 변경
전략 패턴 흐름
클래스 구성

// 전략(추상화된 알고리즘)
interface IStrategy {
void doSomething();
}
// 전략 알고리즘 A
class ConcreteStrateyA implements IStrategy {
public void doSomething() {}
}
// 전략 알고리즘 B
class ConcreteStrateyB implements IStrategy {
public void doSomething() {}
}
// 컨텍스트(전략 등록/실행)
class Context {
IStrategy Strategy; // 전략 인터페이스를 합성(composition)
// 전략 교체 메소드
void setStrategy(IStrategy Strategy) {
this.Strategy = Strategy;
}
// 전략 실행 메소드
void doSomething() {
this.Strategy.doSomething();
}
}
클래스 흐름

// 클라이언트(전략 교체/전략 실행한 결과를 얻음)
class Client {
public static void main(String[] args) {
// 1. 컨텍스트 생성
Context c = new Context();
// 2. 전략 설정
c.setStrategy(new ConcreteStrateyA());
// 3. 전략 실행
c.doSomething();
// 4. 다른 전략 설정
c.setStrategy(new ConcreteStrateyB());
// 5. 다른 전략 시행
c.doSomething();
}
}
특징
사용 시기
- 전략 알고리즘의 여러 버전 또는 변형이 필요할 때 클래스화를 통해 관리
- 알고리즘 코드가 노출되어서는 안 되는 데이터에 액세스하거나 데이터를 활용할 때 (캡슐화)
- 알고리즘의 동작이 런타임에 실시간으로 교체되어야 할 때
장점
- 알고리즘을 쉽게 변경 및 대체할 수 있으므로 유연하다.
- 알고리즘 추가 및 수정을 할 때 코드 수정이 최소화되므로 확장성이 높아진다.
- 알고리즘을 캡슐화했기에 코드 재사용성이 좋다.
- 각각의 알고리즘을 독립적으로 테스트할 수 있으므로 용이하다.
단점
- 알고리즘이 많아질수록 관리해야 할 객체가 늘어난다는 단점이 있다.
- 만일 애플리케이션 특성이 알고리즘이 많지 않고 자주 변경되지 않는다면, 새로운 클래스와 인터페이스를 만들어 프로그램을 복잡하게 만들 이유가 없다.
- 개발자는 적절한 전략을 선택하기 위해 전략 간의 차이점을 파악하고 있어야 한다. (복잡도↑)
예시
passport
전략 패턴을 활용한 라이브러리로는 passport가 있다.
passport는 Node.js에서 인증 모듈을 구현할 때 쓰는 미드웨이 라이브러리로, 여러 가지 '전략'을 기반으로 인증할 수 있게 한다. 서비스 내의 회원가입된 아이디와 비밀번호를 기반으로 인증하는 LocalStrategy 전략과 페이스북, 네이버 등 다른 서비스를 기반으로 인증하는 OAuth 전략 등을 지원한다.
다음 코드처럼 '전략'만 바꾸어 인증하는 것을 볼 수 있다.
var passport = require('passport')
, LocalStrategy = require('passport-local').Strategy;
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({ username: username }, function (err, user) {
if (err) { return done(err); }
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
if (!user.validPassword(password)) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user);
});
}
));
`passport.use(new LocalStragety(...))`처럼 `passport.use()`라는 메서드에 '전략'을 매개 변수로 넣어서 로직을 수행하는 것을 볼 수 있다.
클래스 필드 매개변수
// 전략 - 추상화된 알고리즘
interface Weapon {
void offensive();
}
class Sword implements Weapon {
@Override
public void offensive() {
System.out.println("칼을 휘두르다");
}
}
class Shield implements Weapon {
@Override
public void offensive() {
System.out.println("방패로 밀친다");
}
}
class CrossBow implements Weapon {
@Override
public void offensive() {
System.out.println("석궁을 발사하다");
}
}
// 컨텍스트 - 전략을 등록하고 실행
class TakeWeaponStrategy {
Weapon wp;
void setWeapon(Weapon wp) {
this.wp = wp;
}
void attack() {
wp.offensive();
}
}
// 클라이언트 - 전략 제공/설정
class User {
public static void main(String[] args) {
// 플레이어 손에 무기 착용 전략을 설정
TakeWeaponStrategy hand = new TakeWeaponStrategy();
// 플레이어가 검을 들도록 전략 설정
hand.setWeapon(new Sword());
hand.attack(); // "칼을 휘두르다"
// 플레이어가 방패를 들도록 전략 변경
hand.setWeapon(new Shield());
hand.attack(); // "방패로 밀친다"
// 플레이어가 석궁을 들도록 전략 변경
hand.setWeapon(new Crossbow());
hand.attack(); // "석궁을 발사하다"
}
}
전략 패턴에선 인스턴스를 넣어 알고리즘을 수행하도록 한 것이다.
행위 전략
// Run / Walk 전략(추상화된 알고리즘)
interface MoveStrategy {
void move();
}
class Walk implements MoveStrategy {
public void move() {
System.out.println("걸어서 배달합니다 삐-빅");
}
}
class Run implements MoveStrategy {
public void move() {
System.out.println("뛰러서 배달합니다 삐-빅");
}
}
// 한국어 / 일본어 번역 전략(추상화된 알고리즘)
interface TranslateStrategy {
void translate();
}
class Korean implements TranslateStrategy {
public void translate() {
System.out.println("한국어로 번역합니다 삐-비-빅");
}
}
class Japanese implements TranslateStrategy {
public void translate() {
System.out.println("일본어로 번역합니다 삐-비-빅");
}
}
// 컨텍스트(전략 등록/실행)
public class Robot {
MoveStrategy moveStrategy;
TranslateStrategy translateStrategy;
Robot(MoveStrategy moveStrategy, TranslateStrategy translateStrategy) {
this.moveStrategy = moveStrategy;
this.translateStrategy = translateStrategy;
}
void move() {
moveStrategy.move();
}
void translate() {
translateStrategy.translate();
}
void setMove(MoveStrategy moveStrategy) {
this.moveStrategy = moveStrategy;
}
void setTranslate(TranslateStrategy translateStrategy) {
this.translateStrategy = translateStrategy;
}
}
// 클라이언트(전략 교체/전략 실행한 결과를 얻음)
class User {
public static void main(String[] args) {
Robot robot = new Robot(new Walk(), new Korean());
robot.move(); // 걸어서 배달합니다 삐-빅
robot.translate(); // 한국어로 번역합니다 삐-비-빅
// 로봇의 전략(기능)을 run과 Japanese 번역으로 변경
robot.setMove(new Run());
robot.setTranslate(new Japanese());
robot.move(); // 뛰러서 배달합니다 삐-빅
robot.translate(); // 일본어로 번역합니다 삐-비-빅
}
}
메서드 매개변수
우리가 어떤 것을 살 때 네이버페이, 카카오페이 등 다양한 방법으로 결제하듯이 어떤 아이템을 살 때 LUNACard로 사는 것과 KAKAOCard로 사는 것을 구현한 예제이다. 결제 방식의 '전략'만 바꿔서 두 가지 방식으로 결제하는 것을 구현했다.
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
interface PaymentStrategy {
public void pay(int amount);
}
class KAKAOCardStrategy implements PaymentStrategy {
private String name;
private String cardNumber;
private String cvv;
private String dateOfExpiry;
public KAKAOCardStrategy(String nm, String ccNum, String cvv, String expiryDate){
this.name=nm;
this.cardNumber=ccNum;
this.cvv=cvv;
this.dateOfExpiry=expiryDate;
}
@Override
public void pay(int amount) {
System.out.println(amount +" paid using KAKAOCard.");
}
}
class LUNACardStrategy implements PaymentStrategy {
private String emailId;
private String password;
public LUNACardStrategy(String email, String pwd){
this.emailId=email;
this.password=pwd;
}
@Override
public void pay(int amount) {
System.out.println(amount + " paid using LUNACard.");
}
}
class Item {
private String name;
private int price;
public Item(String name, int cost){
this.name=name;
this.price=cost;
}
public String getName() {
return name;
}
public int getPrice() {
return price;
}
}
class ShoppingCart {
List<Item> items;
public ShoppingCart(){
this.items=new ArrayList<Item>();
}
public void addItem(Item item){
this.items.add(item);
}
public void removeItem(Item item){
this.items.remove(item);
}
public int calculateTotal(){
int sum = 0;
for(Item item : items){
sum += item.getPrice();
}
return sum;
}
public void pay(PaymentStrategy paymentMethod){
int amount = calculateTotal();
paymentMethod.pay(amount);
}
}
public class HelloWorld{
public static void main(String []args){
ShoppingCart cart = new ShoppingCart();
Item A = new Item("kundolA",100);
Item B = new Item("kundolB",300);
cart.addItem(A);
cart.addItem(B);
// pay by LUNACard
cart.pay(new LUNACardStrategy("kundol@example.com", "pukubababo"));
// pay by KAKAOBank
cart.pay(new KAKAOCardStrategy("Ju hongchul", "123456789", "123", "12/01"));
}
}
/*
400 paid using LUNACard.
400 paid using KAKAOCard.
*/
전략 인터페이스인 `PaymentStrategy`를 클래스 필드로 합성(composition)하지 않고 컨텍스트의 `pay()` 메소드의 매개변수로 합성(composition)한다.
메서드의 입력값으로 객체를 할당하는 방식이 좋은 점은, 각 전략에 따라 초기화하는 생성자 매개변수 갯수가 다를 수 있기 때문이다. 예를 들어 LUNA 카드는 메일과 비밀번호만 필요하지만, KAKAO 카드는 회사 정책에 따라 여러 가지 정보들이 더 필요할 수 있기 때문이다.
Strategy Pattern vs Template Method
유사점
- 둘 다 알고리즘을 때에 따라 적용한다는 컨셉이다.
- 개방형 폐쇄 원칙을 충족하고 코드를 변경하지 않고 소프트웨어 모듈을 쉽게 확장할 수 있도록 하는 데 사용할 수 있다.
차이점
- 전략 패턴은 합성(composition)을 통해 해결책을 강구하며, 템플릿 메서드 패턴은 상속(inheritance)을 통해 해결책을 제시한다.
- 그래서 전략 패턴은 클라이언트와 객체 간의 결합이 느슨한 반면, 템플릿 메서드 패턴에서는 두 모듈이 더 밀접하게 결합된다.
- 전략 패턴에서는 대부분 인터페이스를 사용하지만, 템플릿 메서드 패턴에서는 주로 추상 클래스나 구체적인 클래스를 사용한다.
- 전략 패턴에서는 전체 전략 알고리즘을 변경할 수 있지만, 템플릿 메서드 패턴에서는 알고리즘의 일부만 변경되고 나머지는 변경되지 않은 상태로 유지된다. (템플릿에 종속)
- 따라서 단일 상속만이 가능한 자바에서 상속 제한이 있는 템플릿 메서드 패턴보다는, 다양하게 많은 전략을 `implements`할 수 있는 전략 패턴이 협업에서 많이 사용되는 편이다.
참조
'CS' 카테고리의 다른 글
| [CS] Proxy Pattern(프록시 패턴) (0) | 2026.03.22 |
|---|---|
| [CS] Observer Pattern(옵저버 패턴) (0) | 2026.01.22 |
| [CS] Factory Pattern(팩토리 패턴) (0) | 2026.01.03 |
| [CS] Singleton Pattern(싱글톤 패턴) (0) | 2025.12.17 |
| Facade Pattern (0) | 2025.10.23 |