[Design Pattern] 명령(Command) 패턴

장민제·2025년 4월 10일

Design Pattern

목록 보기
1/2

✅ 명령(Command) 패턴이란?

Command 패턴은 요청을 객체로 캡슐화하여, 명령의 실행, 취소, 재실행 등을 유연하게 처리 할 수 있게 해주는 디자인 패턴.

🎮 게임에서의 활용 예시

활용 상황설명
플레이어 이동방향키 입력을 명령으로 만들고, 이동을 명령 객체로 실행
실행 취소 / 다시 실행이전 위치로 되돌아가는 Undo 기능 구현
키 바인딩 시스템Space → Alt 키처럼 키를 자유롭게 바꿔도, 행동 로직은 재사용
튜토리얼 리플레이명령 기록을 저장해 재생하면 행동을 그대로 보여줄 수 있음

💡 언제 쓰면 좋을까?

  • 실행한 작업을 되돌리기(Undo) 또는 재실행(Redo) 해야 할 때
  • 요청을 큐에 저장하거나, 작업 이력을 기록해야 할 때
  • 실행할 명령을 나중에 스케줄링하거나, 로깅할 때
  • 명령을 실행하는 객체와 명령을 요청하는 객체를 분리하고 싶을 때

✨ 특징

  • 명령을 객체화하여, 실행 로직을 분리하고 관리하기 쉬움
  • 실행 취소(Undo), 다시 실행(Redo) 기능 구현이 쉬움
  • 요청과 실행을 분리하여 유연한 설계 가능

🧪 간단한 예제 구현

Unity에서 Command 패턴을 적용해
격자 기반 타일 맵에서 플레이어를 이동시키고,
되돌리기(Z), 재실행(Y) 기능을 구현해보았다.

  • 플레이어는 W/A/S/D 키로 한 칸씩 움직일 수 있으며
  • 움직인 경로를 Command 객체로 저장해서
  • Z 키로 되돌리기, Y 키로 다시 실행할 수 있음

ICommand 인터페이스

public interface ICommand
{
    void Execute();
    void Undo();
}
  • Excute()로 실행;
  • Undo()로 되돌리기;

MoveCommand.cs - 이동 명령

// 플레이어를 격자 형태로 이동시키는 명령
public class MoveCommand : ICommand
{
    private Transform player;	// 움직일 오브젝트
    private Vector2Int from, to; // 이동 전 / 후 위치
    private GridManager gridManager; // 격자 관리

    public MoveCommand(Transform player, Vector2Int from, Vector2Int to, GridManager gridManager)
    {
        this.player = player;
        this.from = from;
        this.to = to;
        this.gridManager = gridManager;
    }

    public void Execute()
    {
    	// 이동 실행 목표위치로 이동
        player.position = gridManager.GetWorldPosition(to);
    }

    public void Undo()
    {
    	// 이전 위치로 되돌리기
        player.position = gridManager.GetWorldPosition(from);
    }
}

CommandInvoker.cs - 명령 저장 & 관리

public class CommandInvoker
{
    private Stack<ICommand> undoStack = new Stack<ICommand>();
    private Stack<ICommand> redoStack = new Stack<ICommand>();
	
    // 명령 실행
    public void ExecuteCommand(ICommand command)
    {
        command.Execute();
        undoStack.Push(command);
        redoStack.Clear(); // 새로운 명령 실행 시 Redo 초기화
    }
	
    // 되돌리기
    public void Undo()
    {
        if (undoStack.Count > 0)
        {
            var command = undoStack.Pop(); // 이전 명령 위치 가져오기
            command.Undo();	// 되돌리기
            redoStack.Push(command); // 이번 명령 다시 실행 스택에 추가
        }
    }
	
    // 다시 실행
    public void Redo()
    {
        if (redoStack.Count > 0)
        {
            var command = redoStack.Pop(); // 취소 시켰던 명령 가져오기
            command.Execute();	// 다시 실행
            undoStack.Push(command); // 되돌리기 스택에 추가
        }
    }
}

PlayerController.cs - 입력 처리 & 명령 실행

public class PlayerController : MonoBehaviour
{
    public Text inputKeyText;   // 입력키 확인 용 UI
    public GridManager gridManager;
    public Vector2Int currentGridPos = new Vector2Int(0, 0);

    private CommandInvoker invoker = new CommandInvoker();

    void Update()
    {
    	// 키 입력 감지(WASD)
        Vector2Int move = Vector2Int.zero;
        if (Input.GetKeyDown(KeyCode.W)){ move = Vector2Int.up; inputKeyText.text = "현재 입력 키: W";}
        if (Input.GetKeyDown(KeyCode.S)){ move = Vector2Int.down; inputKeyText.text = "현재 입력 키: S";}
        if (Input.GetKeyDown(KeyCode.A)){ move = Vector2Int.left; inputKeyText.text = "현재 입력 키: A";}
        if (Input.GetKeyDown(KeyCode.D)){ move = Vector2Int.right; inputKeyText.text = "현재 입력 키: D";}
		
        // 키 입력이 감지되면
        if (move != Vector2Int.zero)
        {
            Vector2Int target = currentGridPos + move;
			
            // 이동 명령 생성 후 실행
            ICommand moveCommand = new MoveCommand(transform, currentGridPos, target, gridManager);
            invoker.ExecuteCommand(moveCommand);
          
            currentGridPos = target;
        }
		
        // Z -> 되돌리기(Undo) , Y -> 다시실행(Redo)
        if (Input.GetKeyDown(KeyCode.Z)) {invoker.Undo(); inputKeyText.text = "현재 입력 키: Z";}
        if (Input.GetKeyDown(KeyCode.Y)) {invoker.Redo(); inputKeyText.text = "현재 입력 키: Y";}
    }
}

🔧테스트

profile
Unity, C#

0개의 댓글