Computer Science

전략 vs 템플릿 메서드 vs 커맨드(Strategy·Template Method·Command), 알고리즘 교체 3형제 총정리

혹시 디자인 패턴 공부하다가 이런 순간 겪어 보신 적 있나요?

이석우iOS Developer5분 읽기
전략 vs 템플릿 메서드 vs 커맨드(Strategy·Template Method·Command), 알고리즘 교체 3형제 총정리 대표 이미지

혹시 디자인 패턴 공부하다가 이런 순간 겪어 보신 적 있나요?

“전략 패턴이랑 템플릿 메서드랑 뭐가 다르지? 커맨드는 또 왜 여기 껴 있는 거야?”

세 패턴 다 동작을 갈아 끼운다는 느낌이라 계속 헷갈리기 딱 좋습니다.

핵심부터 말씀드릴게요.

전략은 알고리즘 전체를 통째로 교체하고,

템플릿 메서드는 뼈대는 고정한 채 일부 단계만 바꾸며,

커맨드는 ’실행할 동작’을 객체로 포장해 나중에 실행합니다.

이 한 줄만 잡고 가도 절반은 끝난 셈이에요. 오늘은 이 3형제를 코드와 함께 확실히 구분해 드리겠습니다.

알고리즘 교체 3형제, 이 그림 하나로 감이 옵니다
알고리즘 교체 3형제, 이 그림 하나로 감이 옵니다

세 패턴, 한눈에 뭐가 다를까?

큰 그림부터 표로 정리해 볼게요.

구분 전략 패턴 템플릿 메서드 커맨드 패턴
핵심 알고리즘 통째 교체 뼈대 고정, 일부 단계 교체 동작을 객체로 포장
재사용 방식 구성(위임) 상속 구성(위임)
교체 시점 런타임 컴파일 타임(상속) 런타임
대표 관심사 “어떻게 계산할까” “순서는 같고 세부만 다를 때” “언제, 무엇을 실행할까”

세 패턴이 헷갈리는 이유가 여기 있어요.

셋 다 결국 바뀌는 부분을 분리한다는 목적은 똑같거든요.

하지만 분리하는 방법과 목적이 다릅니다. 하나씩 뜯어볼게요.


전략 패턴은 알고리즘을 통째로 갈아 끼운다

전략 패턴은 같은 일을 하는 여러 방법을 각각 독립된 객체로 만들어 두고, 필요할 때 바꿔 끼우는 방식이에요.

예를 들어 결제 수단을 떠올리면 딱 맞습니다.

카드 결제, 카카오페이, 무통장. 하는 일(결제)은 같은데 방법이 다르죠.

전략 패턴은 이렇게 통째로 갈아 끼우는 구조예요
전략 패턴은 이렇게 통째로 갈아 끼우는 구조예요
protocol PayStrategy {
    func pay(_ amount: Int) -> String
}
struct CardPay: PayStrategy {
    func pay(_ amount: Int) -> String { "카드로 \(amount)원 결제" }
}
struct KakaoPay: PayStrategy {
    func pay(_ amount: Int) -> String { "카카오페이로 \(amount)원 결제" }
}

var strategy: PayStrategy = CardPay()
print(strategy.pay(10000))
// 출력: 카드로 10000원 결제

핵심은 strategy 변수를 런타임에 KakaoPay()로 바꾸면 동작이 통째로 교체된다는 점이에요.

상속이 아니라 구성(composition)으로 위임한다는 게 포인트입니다.

결제 전략 코드를 직접 짜보니 위임 구조가 확 와닿더라고요
결제 전략 코드를 직접 짜보니 위임 구조가 확 와닿더라고요

템플릿 메서드는 뼈대를 두고 일부만 바꾼다

템플릿 메서드는 결이 조금 달라요.

전체 흐름의 순서는 고정돼 있는데, 그 안의 몇몇 단계만 하위 클래스가 채워 넣는 방식입니다.

라면 끓이는 순서를 떠올려 보세요. 물 끓이고 → 재료 넣고 → 완성. 순서는 같은데 ‘재료 넣기’ 단계만 달라지죠.

class Ramen {
    func cook() {           // 이게 템플릿 메서드
        boilWater()
        addIngredients()    // 이 단계만 하위에서 교체
        print("완성!")
    }
    func boilWater() { print("물을 끓인다") }
    func addIngredients() { print("기본 재료") }
}
class CheeseRamen: Ramen {
    override func addIngredients() { print("치즈 추가") }
}
CheeseRamen().cook()
// 출력: 물을 끓인다 / 치즈 추가 / 완성!

전략 패턴과 결정적으로 다른 점은 상속을 쓴다는 것, 그리고 전체가 아니라 일부 단계만 바꾼다는 것이에요.

흐름(cook)의 주도권은 부모 클래스가 쥐고 있습니다.


커맨드 패턴은 실행할 동작 자체를 포장한다

커맨드는 앞의 둘과 관심사가 아예 다릅니다.

어떻게 계산하느냐가 아니라, 이 동작을 객체로 만들어 나중에 실행하거나 취소하거나 큐에 쌓겠다는 목적이에요.

리모컨 버튼을 떠올리면 쉽습니다. 버튼은 “불 켜라”는 명령을 객체로 담고 있다가, 눌렀을 때 실행돼요.

protocol Command { func execute() }
struct LightOn: Command {
    func execute() { print("불을 켠다") }
}

let button: Command = LightOn()
button.execute()   // 실행 시점을 내가 정한다
// 출력: 불을 켠다

요청을 객체로 감쌌기 때문에 실행 취소(undo), 로그 기록, 작업 큐 같은 게 자연스럽게 붙습니다.

전략은 방법을 고르는 패턴, 커맨드는 요청을 저장하고 다루는 패턴이라고 정리하면 깔끔해요.


언제 쓰고 언제 피해야 할까

실무에서 저는 이런 기준으로 고릅니다.

  • 전략 패턴: 같은 목적의 알고리즘이 여러 개이고, 런타임에 바꿔야 할 때 (정렬 방식, 할인 정책, 결제 수단)
  • 템플릿 메서드: 처리 순서는 항상 같고 일부 단계만 달라질 때 (데이터 파싱 흐름, 게임 턴 진행)
  • 커맨드: 실행을 미루거나, 취소·재실행·큐잉이 필요할 때 (되돌리기, 작업 스케줄러)

반대로 피해야 할 신호도 있어요.

분기가 두세 개뿐이고 앞으로 늘 일도 없다면, 굳이 패턴을 씌우지 마세요. 단순한 if 하나가 더 읽기 좋습니다.

특히 템플릿 메서드는 상속에 묶이는 구조라, 유연함이 중요하면 전략 패턴(구성)을 먼저 고려하는 편이 안전합니다.


면접에서는 이렇게 물어봅니다

Q. 전략 패턴과 템플릿 메서드의 가장 큰 차이는?

전략은 알고리즘 전체를 구성(위임)으로 런타임에 교체하고, 템플릿 메서드는 상속으로 뼈대를 고정한 채 일부 단계만 오버라이드합니다. 유연성은 전략이, 흐름 통제는 템플릿 메서드가 강합니다.

Q. 커맨드 패턴이 전략과 다른 지점은?

전략은 어떤 방법으로 할지를 고르는 데 집중하고, 커맨드는 요청 자체를 객체로 포장해 실행 취소·큐잉·로깅 같은 부가 조작을 가능하게 합니다. 관심사가 알고리즘 선택이냐, 요청 관리냐로 갈립니다.


세 패턴이 비슷해 보여도, 무엇을 왜 분리하는가를 기준으로 보면 확실히 구분됩니다.

전략은 방법, 템플릿 메서드는 순서 속 빈칸, 커맨드는 요청 그 자체. 이 세 단어만 기억하셔도 앞으로 헷갈리지 않으실 거예요. 오늘도 즐거운 공부 되시길 응원합니다!

함께 보면 좋은 글