커맨드 패턴

2025. 5. 20. 21:35·디자인패턴

🎯 커맨드 패턴이란 무엇일까??

요청을 객체의 형태로 캡슐화하여 매개변수화하는 행동 디자인 패턴

수행할 동작을 객체 안에 캡슐화해 요청자(호출자)와 수신자(실행자) 사이를 분리합니다.

어떤 작업 => 객체가 되는거고 필요에 따라 객체에게 전달.

📺 리모콘 예시로 이해하기

버튼 → 누르기 → 명령 → TV (On/Off)
  • 버튼은 TV에 대해 아무것도 모름 (삼성TV인지, LG TV인지)
  • 연결된 명령을 통해 TV를 제어
  • 요청자와 수신자가 완전히 분리

🔧 해결하는 문제들

1. 코드 결합도 감소

  • 요청자와 수신자 간의 직접 연결 제거

2. 작업의 지연 및 예약 실행

  • 명령을 즉시 실행하지 않고 저장했다가 나중에 실행

3. 작업 취소 기능

  • 이전 상태를 저장하여 실행 취소 기능을 쉽게 구현

4. 작업 로깅

  • 모든 변경사항을 추적하고 필요시 재실행 가능

구성 요소

 

  • Command (명령): 작업 수행에 필요한 인터페이스를 선언합니다.
  • ConcreteCommand (구체적 명령): Command 인터페이스를 구현하고 Receiver와 작업을 연결합니다.
  • Invoker (호출자): 명령 객체를 저장하고 실행을 요청합니다.
  • Receiver (수신자): 실제 작업을 수행하는 객체입니다.
  • Client (클라이언트): ConcreteCommand를 생성하고 Receiver를 설정합니다.
// Command: 명령 인터페이스
protocol Command {
    func execute()
    func undo()
}

// Receiver: 실제 작업 수행
class Light {
    var isOn = false
    
    func turnOn() {
        isOn = true
        print("💡 불이 켜졌습니다!")
    }
    
    func turnOff() {
        isOn = false
        print("🌑 불이 꺼졌습니다.")
    }
}

// ConcreteCommand: 구체적인 명령 구현
class LightOnCommand: Command {
    private let light: Light
    
    init(light: Light) {
        self.light = light
    }
    
    func execute() {
        light.turnOn()
    }
    
    func undo() {
        light.turnOff()
    }
}

// Invoker: 명령 실행을 요청
class RemoteControl {
    private var command: Command?
    
    func setCommand(_ command: Command) {
        self.command = command
    }
    
    func pressButton() {
        command?.execute()
    }
    
    func pressUndoButton() {
        command?.undo()
    }
}

// Client: 객체들을 조합하여 사용
let light = Light()                  // 수신자 생성
let lightOn = LightOnCommand(light: light)  // 명령 생성
let remote = RemoteControl()         // 호출자 생성

remote.setCommand(lightOn)           // 명령 설정
remote.pressButton()                 // 실행 → 💡 불이 켜졌습니다!
remote.pressUndoButton()             // 취소 → 🌑 불이 꺼졌습니다.

 

언제사용할까?

1. 📱 UI와 비즈니스 로직 분리

복잡한 UI 시스템에서 다양한 요소가 동일한 작업을 트리거해야 할 때

  • UI Layer와 비즈니스 로직 레이어를 깔끔하게 분리
  • 테스트 및 유지보수 용이성 향상

2. ⏰ 작업 지연, 대기열, 스케줄링

  • API 제한: 작업을 대기열에 넣고 순차적으로 처리
  • 네트워크 불안정: 모바일 앱에서 작업을 로컬 저장 후 연결 복원 시 실행

3. 💰 트랜잭션 시스템 구현

금융 앱이나 다단계 프로세스에서 롤백이 필요한 경우

// 송금 트랜잭션 처리
class TransferMoneyTransaction: Command {
    private let sourceAccount: Account
    private let targetAccount: Account
    private let amount: Decimal
    private let commands: [Command]
    
    init(sourceAccount: Account, targetAccount: Account, amount: Decimal) {
        self.sourceAccount = sourceAccount
        self.targetAccount = targetAccount
        self.amount = amount
        
        // 트랜잭션을 구성하는 개별 커맨드들
        self.commands = [
            DeductAmountCommand(account: sourceAccount, amount: amount),
            AddAmountCommand(account: targetAccount, amount: amount),
            LogTransactionCommand(sourceId: sourceAccount.id, targetId: targetAccount.id, amount: amount)
        ]
    }
    
    func execute() {
        // 모든 단계를 실행하되, 하나라도 실패하면 롤백
        for command in commands {
            do {
                try command.execute()
            } catch {
                // 오류 발생 시 이전에 실행된 모든 커맨드 취소
                for i in stride(from: commands.firstIndex(where: { $0 === command })! - 1, through: 0, by: -1) {
                    commands[i].undo()
                }
                throw error
            }
        }
    }
    
    func undo() {
        // 역순으로 모든 커맨드 취소
        for command in commands.reversed() {
            command.undo()
        }
    }
}

4. 작업 기록 및 로깅이 필요할 때 📝

특히 효과적인 상황:

  • 감사 추적이 필요한 앱: 사용자의 중요한 작업을 기록해야 하는 경우
  • 진단 목적: 문제 해결을 위해 사용자 작업 순서를 재현해야 하는 경우

커맨드 패턴 사용의 주의사항

효과적인 커맨드 패턴 구현을 위한 주의사항:

  1. 과도한 사용 지양: 모든 작업을 커맨드로 만들면 코드가 불필요하게 복잡해질 수 있습니다.
  2. 메모리 관리: 많은 커맨드를 저장할 경우 메모리 사용량이 증가할 수 있으므로 적절한 제한이 필요합니다.
  3. 직렬화 고려: 커맨드를 저장하거나 전송할 경우 직렬화/역직렬화 메커니즘이 필요합니다.
  4. 복잡한 상태 관리: 상태 변경이 복잡한 경우 메멘토 패턴과 결합하는 것이 좋습니다

요약

 

요청을 객체의 형태로 캡슐화하는 행동 디자인 패턴입니다. 특정 작업을 객체로 변환해서 요청자와 실행자를 분리하는 패턴이죠. 텍스트 에디터의 undo/redo 기능이나 네트워크 연결이 불안정할때 작업을 대기열에 저장했다가 나중에 실행하는 경우에 유용합니다. 코드의 결합도를 낮추면서도 새로운 명령을 추가하기도 용이해 확장성이 좋습니다. 

 

참고자료 

https://refactoring.guru/ko/design-patterns/command

 

커맨드 패턴

/ 디자인 패턴들 / 행동 패턴 커맨드 패턴 다음 이름으로도 불립니다: 액션, 트랜잭션, Command 의도 커맨드는 요청을 요청에 대한 모든 정보가 포함된 독립실행형 객체로 변환하는 행동 디자인

refactoring.guru

 

'디자인패턴' 카테고리의 다른 글

데코레이터패턴  (1) 2025.05.19
파사드 패턴(Facade Pattern)  (0) 2025.03.25
팩토리 패턴  (0) 2025.03.25
Adaptor Pattern (구조적 디자인패턴)  (0) 2025.03.24
'디자인패턴' 카테고리의 다른 글
  • 데코레이터패턴
  • 파사드 패턴(Facade Pattern)
  • 팩토리 패턴
  • Adaptor Pattern (구조적 디자인패턴)
2료일
2료일
좌충우돌 모든것을 다 정리하려고 노력하는 J가 되려고 하는 세미개발자의 블로그입니다. 편하게 보고 가세요
  • 2료일
    GPT에게서 살아남기
    2료일
  • 전체
    오늘
    어제
    • 분류 전체보기 (120)
      • SWIFT개발 (29)
      • 알고리즘 (25)
      • Design (6)
      • ARkit (1)
      • 면접준비 (30)
      • UIkit (2)
      • Vapor-Server with swift (3)
      • 디자인패턴 (5)
      • 반응형프로그래밍 (12)
      • CS (3)
      • 도서관 (1)
  • 인기 글

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
2료일
커맨드 패턴
상단으로

티스토리툴바