⚔️ 전략 패턴 (Strategy Pattern)

2025. 9. 9. 16:00·Computer Science/Design Pattern

객체의 행위(Algorithm)를 바꾸고 싶은 경우, 해당 객체의 코드를 직접 수정하지 않고 '전략'이라고 부르는 캡슐화한 알고리즘(행위)을 컨텍스트(Context) 안에서 바꿔주면, 행위가 상호 교체될 수 있게 만드는 패턴입니다.


📁 전략 패턴 비유(ft. 얄코 용사)

영상(링크)에서는 전략 패턴을 게임 캐릭터(용사)에 비유하여 설명합니다.

 

1. 문제가 되는 방식 (상속의 함정)

만약 Warrior 클래스 안에 attack() 메서드(행위)가 '칼로 공격'이라고 고정되어 있다고 가정해 보겠습니다.

이때 '활 쏘는 용사'가 필요하면 어떻게 할까요? Warrior를 상속받아 Archer를 만들고 attack()을 '활 쏘기'로 덮어씌울(Override) 수 있습니다. 만약 '불 마법 쓰는 용사'가 필요하면 또 상속받아 FireWarrior를 만듭니다.

이 방식은 당장은 편해 보이지만, '불을 쏘는 궁수'나 '얼음 칼을 쓰는 용사'처럼 조합이 늘어나면 상속 관계는 걷잡을 수 없이 복잡해집니다. (싱글톤의 '얄코 버거' 비유에서 본 상속의 문제와 같습니다.)

2. 전략 패턴의 해결책 (상속 대신 조합) 

전략 패턴은 이 문제를 "조합(Composition)"으로 해결합니다. "캐릭터가 공격 한다"가 아니라 "캐릭터가 무기를 사용한다"라고 개념을 바꿉니다.

  1. 전략 인터페이스 (무기 규격): 먼저 모든 무기(전략)의 공통 규격인 WeaponStrategy 인터페이스를 만듭니다. (예: "모든 무기는 executeAttack() 메서드를 가져야 한다.")
  2. 전략 구현체 (실제 무기들): 이 규격에 맞춰 실제 무기(캡슐화된 알고리즘)들을 만듭니다. (예: SwordStrategy, BowStrategy 클래스)
  3. 컨텍스트 (캐릭터): 캐릭터 클래스(Character)는 공격 로직을 직접 갖는 대신, WeaponStrategy 타입의 슬롯(참조 변수)을 하나 가집니다.
  4. 작동: 캐릭터의 attack()이 호출되면, 캐릭터는 자신이 아는 게 없으므로 그냥 자기 슬롯에 꽂힌 무기를 향해 "네가 알아서 executeAttack() 해!"라고 실행을 위임(Delegate)합니다.

핵심: 이 구조 덕분에, 우리는 캐릭터의 코드는 전혀 건드리지 않고, 실행 중(Runtime)에 캐릭터의 무기 슬롯에 SwordStrategy를 꽂아주거나 BowStrategy로 갈아 끼우기만 하면 캐릭터의 '공격 행위'가 완전히 바뀌게 됩니다.


💻 코드 예시

1. 자바(Java)의 전략 패턴 (장바구니 결제)

이 비유는 사용자가 제공한 자바 코드 예시와 완벽하게 일치합니다.

  • 컨텍스트 (캐릭터): ShoppingCart (장바구니)
  • 전략 인터페이스 (무기 규격): PaymentStrategy (결제 수단 규격 - 반드시 pay()를 가져야 함)
  • 전략 구현체 (실제 무기들): KAKAOCardStrategy, LUNACardStrategy (실제 결제 알고리즘)

ShoppingCart(컨텍스트)는 스스로 결제하는 방법을 모릅니다. 대신 pay() 메서드가 호출되면, 파라미터로 넘어온 paymentMethod(전략)에게 총액을 알려주며 "네가 알아서 이 금액을 pay() 해!"라고 위임할 뿐입니다.

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;

// 1. 전략 인터페이스 (Strategy Interface): "결제 수단은 pay(금액) 기능이 있어야 한다"는 규격
interface PaymentStrategy {
    public void pay(int amount);
}

// 2. 전략 구현체 A (Concrete Strategy): 카카오카드 결제 알고리즘
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.");
    }
}

// 2. 전략 구현체 B (Concrete Strategy): 루나카드 결제 알고리즘
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.");
    }
}

// 아이템 클래스 (전략 패턴과 직접적 관계 X)
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;
    }
}

// 3. 컨텍스트 (Context): 장바구니
// (개발자가 어떠한 작업을 완료하는 데 필요한 모든 관련 정보 = 여기서는 'items'와 'total' 정보)
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;
    }

    // 4. 행위 실행
    // ShoppingCart는 결제 방법(Kakao, Luna)을 모름.
    // 그저 규격(PaymentStrategy)에 맞는 '전략'을 받아 실행만 위임함.
    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);

        // A행위: LUNACardStrategy (전략)을 장착(주입)하여 pay() 실행
        cart.pay(new LUNACardStrategy("kundol@example.com", "pukubababo"));

        // B행위: KAKAOCardStrategy (전략)을 장착(주입)하여 pay() 실행
        cart.pay(new KAKAOCardStrategy("Ju hongchul", "123456789", "123", "12/01"));
    }
}

실행 결과:

400 paid using LUNACard.
400 paid using KAKAOCard.

2. passport.js의 전략 패턴

Node.js의 인증 라이브러리인 passport는 전략 패턴의 교과서적인 예시입니다.

passport 모듈 자체는 인증을 처리하는 방법(컨텍스트)의 뼈대만 제공할 뿐, 어떻게(Strategy) 인증할지는 모릅니다.

개발자는 서비스 정책에 맞춰 필요한 '전략(무기)'을 npm에서 다운로드하여 장착(use)합니다.

  • 서비스 내 ID/PW로 인증하려면: LocalStrategy를 장착합니다.
  • 페이스북/네이버/구글 기반으로 인증하려면: FacebookStrategy, NaverStrategy 등 OAuth 전략을 장착합니다.
var passport = require('passport'),
  LocalStrategy = require('passport-local').strategy; // '로컬 인증' 전략(무기)을 불러옴

// passport(캐릭터)에게 "LocalStrategy(무기)"를 장착(use)시킨다.
passport.use(
  new LocalStrategy(
    // 이 부분은 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 코드를 수정하는 것이 아니라, 그저 new FacebookStrategy(...)라는 새로운 전략(무기)을 추가로 장착(passport.use(...))하기만 하면 됩니다.

'Computer Science > Design Pattern' 카테고리의 다른 글

⛓️ 프록시 패턴과 프록시 서버  (1) 2025.09.16
📡 옵저버 패턴 (Observer Pattern)  (0) 2025.09.09
🏭 팩토리 패턴 (Factory Pattern)  (0) 2025.09.09
💡 싱글톤 패턴 (Singleton Pattern)  (0) 2025.09.09
👨‍💻 디자인 패턴과 주요 원칙  (0) 2025.09.09
'Computer Science/Design Pattern' 카테고리의 다른 글
  • ⛓️ 프록시 패턴과 프록시 서버
  • 📡 옵저버 패턴 (Observer Pattern)
  • 🏭 팩토리 패턴 (Factory Pattern)
  • 💡 싱글톤 패턴 (Singleton Pattern)
TECHNING
TECHNING
Hi! I'm techning
  • TECHNING
    TECHNING
    TECHNING
    • 분류 전체보기 (54)
      • Computer Science (45)
        • Design Pattern (11)
        • Programming Paradigm (4)
        • Network (15)
        • Operating System (6)
        • Database (6)
        • Data Structure (3)
      • Algorithm (5)
        • Python (3)
        • Java (1)
      • IT Insight (4)
  • hELLO· Designed By정상우.v4.10.4
TECHNING
⚔️ 전략 패턴 (Strategy Pattern)
상단으로

티스토리툴바