메뉴 닫기

PySide Qt for Python QUndoStack QUndoCommand 되돌리기 다시하기 단축키와 Command 스택 완전 가이드

PySide Qt for Python QUndoStack QUndoCommand 되돌리기 다시하기 단축키와 Command 스택 완전 가이드

🐍 작업 이력도 객체처럼 관리하는 법, QUndoStack과 QUndoCommand로 되돌리기와 다시하기를 안정적으로 구현합니다

프로젝트 막바지에 작은 실수를 만회하려고 손이 먼저 단축키로 향한 경험이 한두 번이 아니죠.
되돌리기와 다시하기가 매끄럽지 않으면 사용자 경험은 금세 불편해집니다.
PySide, 즉 Qt for Python에서는 명령 패턴을 바탕으로 이력을 관리하는 표준 도구를 제공합니다.
이 글은 그 핵심인 QUndoStack과 QUndoCommand를 중심으로, 실무 앱에서 신뢰할 수 있는 이력 관리 체계를 어떻게 구축하는지 친근한 예시와 함께 설명합니다.
단순히 동작만 하는 수준을 넘어, 복합 작업을 하나의 명령으로 묶고 단축키와 메뉴, 툴바까지 자연스럽게 연결하는 실전 포인트를 차근차근 다룹니다.

Qt의 Command 스택은 작업을 ‘명령’ 객체로 분리해 추가, 취소, 재실행을 일관되게 처리합니다.
QUndoStack은 명령 이력을 쌓고 이동하는 컨트롤 타워 역할을 하며, QUndoCommand는 각 작업의 수행과 취소 로직을 캡슐화합니다.
텍스트 편집처럼 미세한 변경이 많은 상황에서도 안정적으로 동작하게 만드는 beginMacro와 endMacro 같은 기능도 제공되어, 여러 동작을 하나의 이력으로 묶을 수 있습니다.
여기에 QKeySequence 기반 단축키 매핑과 QUndoView를 통한 시각화까지 결합하면 유지보수성과 확장성이 크게 좋아집니다.
이 글에서 바로 적용 가능한 패턴과 코드 스니펫을 담아 효율적으로 기능을 완성할 수 있도록 도와드립니다.



🧱 QUndoStack과 QUndoCommand 기본 개념

PySide(Qt for Python)의 되돌리기와 다시하기 기능은 명령(Command) 패턴을 중심으로 동작합니다.
핵심은 모든 변경을 QUndoCommand 객체로 캡슐화하고, 이 객체들을 QUndoStack에 쌓아 이력으로 관리한다는 점입니다.
사용자는 단축키나 메뉴를 통해 스택의 현재 인덱스를 앞뒤로 이동하며 작업을 되돌리거나 재실행합니다.
이 구조를 취하면 상태가 복잡한 UI에서도 로직을 분리해 테스트 가능성과 안정성을 높일 수 있습니다.

일반적인 흐름은 간단합니다.
변경이 발생할 때마다 해당 변경을 수행하는 redo()와 반대로 되돌리는 undo()를 구현한 명령 클래스를 만들고, 인스턴스를 stack.push(command)로 넣습니다.
스택은 내부 포인터를 기준으로 undo() 호출 시 한 칸 뒤로, redo() 호출 시 한 칸 앞으로 이동합니다.
또한 createUndoAction()createRedoAction()을 통해 메뉴/툴바 액션을 자동으로 만들고, 플랫폼 표준 QKeySequence.Undo / Redo 단축키와 연결할 수 있습니다.

CODE BLOCK
from PySide6.QtWidgets import QApplication, QMainWindow, QTextEdit
from PySide6.QtGui import QAction, QKeySequence
from PySide6.QtGui import QUndoStack, QUndoCommand

class InsertTextCommand(QUndoCommand):
    def __init__(self, editor: QTextEdit, text: str, pos: int, desc="Insert Text"):
        super().__init__(desc)
        self.editor = editor
        self.text = text
        self.pos = pos
        self._applied = False

    def redo(self):
        cursor = self.editor.textCursor()
        cursor.setPosition(self.pos)
        cursor.insertText(self.text)
        self.pos = cursor.position()  # 업데이트
        self._applied = True

    def undo(self):
        if not self._applied:
            return
        cursor = self.editor.textCursor()
        cursor.setPosition(self.pos)
        cursor.movePosition(cursor.PreviousCharacter, cursor.KeepAnchor, len(self.text))
        cursor.removeSelectedText()

app = QApplication([])

win = QMainWindow()
editor = QTextEdit()
win.setCentralWidget(editor)

stack = QUndoStack(win)

# 표준 액션과 단축키
undo_action = stack.createUndoAction(win, "Undo")
undo_action.setShortcut(QKeySequence.Undo)

redo_action = stack.createRedoAction(win, "Redo")
redo_action.setShortcut(QKeySequence.Redo)

menu = win.menuBar().addMenu("Edit")
menu.addAction(undo_action)
menu.addAction(redo_action)

# 명령 사용 예시
def insert_hello():
    cmd = InsertTextCommand(editor, "Hello", editor.textCursor().position())
    stack.push(cmd)

act = QAction("Insert Hello", win)
act.triggered.connect(insert_hello)
menu.addAction(act)

win.resize(640, 400)
win.show()
app.exec()

핵심 클래스 역할
QUndoCommand 단일 변경을 캡슐화하고 redo(), undo() 구현으로 재실행/되돌리기 로직을 보관.
QUndoStack 명령 이력을 저장하고 포인터 이동으로 undo/redo를 수행.
액션 생성, 제한 설정, 깨끗한 상태 표시 등을 제공.

💡 TIP: 명령의 설명은 setText() 또는 생성자 설명 문자열로 설정할 수 있습니다.
QUndoView를 사용하면 이 설명이 이력 리스트에 표시되어 디버깅과 사용자 이해에 도움이 됩니다.

  • 🧱모든 변경을 QUndoCommand로 만들기
  • 🗂️QUndoStack.push()로 이력에 추가하기
  • ⌨️메뉴/툴바는 createUndoAction / createRedoAction으로 표준화
  • 🧩복합 작업은 이후 beginMacro / endMacro로 묶기

⚠️ 주의: 명령에서 에디터나 모델의 참조를 보관할 때 객체 수명과 스레드 경계를 반드시 고려하세요.
파괴된 위젯에 접근하거나 다른 스레드에서 UI를 건드리면 예외나 크래시가 발생합니다.

⌨️ 되돌리기 다시하기 동작 원리와 단축키 매핑

QUndoStack의 가장 큰 장점은 되돌리기(Undo)다시하기(Redo)를 시스템 표준처럼 자동으로 연결할 수 있다는 점입니다.
사용자는 Ctrl+Z 또는 Ctrl+Shift+Z (macOS에서는 Cmd 조합)를 누르는 것만으로 즉시 이력 이동이 가능합니다.
이는 QKeySequence.UndoQKeySequence.Redo가 플랫폼별 표준 단축키를 자동으로 인식하기 때문입니다.

QUndoStack은 내부적으로 현재 인덱스를 관리하며, 새로운 명령이 push될 때 되돌린 상태 이후의 명령은 자동 삭제됩니다.
즉, 사용자가 되돌린 후 새로운 변경을 하면 그 시점부터 새로운 이력 분기가 시작됩니다.
이 덕분에 별도의 복잡한 이력 관리 로직 없이 명령 스택 구조만으로 깔끔한 undo/redo를 구현할 수 있습니다.
또한, QAction을 통해 연결된 UI는 setText()textChanged() 신호로 자동 업데이트되어, 메뉴나 툴바의 상태도 일관되게 반영됩니다.

💬 QUndoStack은 내부적으로 두 개의 주요 포인터를 관리합니다.
현재 명령 인덱스와 전체 스택 크기입니다.
되돌리기를 수행하면 인덱스가 한 단계 뒤로 이동하며, 다시하기는 반대로 진행됩니다.

CODE BLOCK
# 단축키와 액션 매핑 예시
undo_action = stack.createUndoAction(window, "되돌리기")
undo_action.setShortcut(QKeySequence.Undo)

redo_action = stack.createRedoAction(window, "다시하기")
redo_action.setShortcut(QKeySequence.Redo)

edit_menu = menu_bar.addMenu("편집")
edit_menu.addAction(undo_action)
edit_menu.addAction(redo_action)

# 단축키만 별도 지정 가능
shortcut_undo = QShortcut(QKeySequence("Ctrl+Z"), window)
shortcut_undo.activated.connect(stack.undo)

shortcut_redo = QShortcut(QKeySequence("Ctrl+Shift+Z"), window)
shortcut_redo.activated.connect(stack.redo)

이 방식의 강점은 스택이 UI 상태를 직접 관리하지 않는다는 점입니다.
명령이 스스로 실행과 취소 로직을 알고 있으므로, 명령의 재사용성과 테스트 편의성이 높아집니다.
GUI 위젯뿐 아니라 데이터 모델, 네트워크 작업 등 다양한 환경에서도 같은 원리로 적용 가능합니다.

💡 TIP: 메뉴 액션의 텍스트에는 자동으로 “되돌리기 입력”, “다시하기 삭제”처럼 마지막 명령의 이름이 반영됩니다.
이는 setText()로 지정한 커맨드 설명을 기반으로 하며, 사용자가 직관적으로 상태를 파악하도록 도와줍니다.

  • ⌨️QKeySequence.Undo / Redo를 사용해 플랫폼별 표준 단축키를 자동 매핑
  • ⚙️QUndoStack의 createUndoAction()createRedoAction()으로 QAction 자동 생성
  • 🔄되돌린 뒤 새 명령을 push하면 이전 redo 이력은 자동 제거됨
  • 🪄액션 텍스트는 명령의 설명 문자열을 자동 반영

💎 핵심 포인트:
되돌리기/다시하기 로직은 단축키 이벤트가 아니라 QUndoStack의 명령 포인터 이동으로만 이루어집니다.
즉, 한 줄의 undo() 호출만으로 전체 UI가 동기화되는 구조가 만들어집니다.



🧩 명령 정의 패턴과 beginMacro endMacro 활용

PySide의 QUndoCommand는 단일 작업을 캡슐화하는 기본 단위입니다.
하지만 실제 애플리케이션에서는 사용자의 한 번의 조작이 여러 내부 변경을 유발하는 경우가 많습니다.
예를 들어, 텍스트 입력과 포맷 변경이 동시에 일어날 수 있고, 여러 셀을 한 번에 수정하는 스프레드시트 명령도 있습니다.
이런 경우 각각의 세부 작업을 하나의 명령으로 묶어 처리하는 것이 필요합니다.
이때 사용하는 기능이 바로 beginMacro()endMacro()입니다.

이 두 메서드는 QUndoStack에서 여러 명령을 하나의 단일 명령처럼 묶어줍니다.
매크로가 시작되면 push된 모든 명령이 내부적으로 하나의 묶음으로 취급되어, undo() 또는 redo() 호출 시 전체가 한 번에 처리됩니다.
이 방식은 특히 폼 입력, 드로잉 애플리케이션, CAD, 에디터 등에서 자연스러운 사용자 경험을 제공합니다.

CODE BLOCK
# 여러 명령을 하나의 매크로로 묶기
stack.beginMacro("문단 추가 및 포맷 적용")
stack.push(InsertTextCommand(editor, "새 문단", pos))
stack.push(FormatCommand(editor, "bold", True))
stack.endMacro()

위 예시에서는 텍스트 추가와 포맷 적용 두 개의 명령을 하나의 이력 항목으로 묶었습니다.
사용자가 되돌리기를 누르면 두 명령이 동시에 취소되고, 다시하기를 하면 다시 두 명령이 연속적으로 재적용됩니다.
이처럼 매크로는 복잡한 명령을 단순화하고, UI 단위의 행동과 이력 단위를 일치시키는 핵심 도구입니다.

🧠 명령 계층 구조 설계

명령 패턴을 제대로 활용하려면 상속 구조를 명확히 하는 것이 중요합니다.
각 명령은 QUndoCommand를 상속받고, 동일한 인터페이스(redo/undo)를 유지해야 합니다.
이렇게 하면 여러 명령을 하나의 매크로로 묶을 때도 일관된 호출 순서를 보장할 수 있습니다.
또한 하위 클래스 간에 setText()로 명령 이름을 정의하면 QUndoView에서 각각의 동작 이름이 사용자에게 표시됩니다.

🪜 커맨드 중첩과 병합(mergeWith)

PySide의 QUndoCommand는 mergeWith() 메서드를 지원합니다.
이는 유사한 명령이 연속적으로 발생할 때 자동으로 병합하는 기능입니다.
예를 들어, 사용자가 연속으로 문자를 입력할 경우 각 문자마다 새로운 명령을 만드는 대신 하나의 “입력 명령”으로 묶을 수 있습니다.
이를 활용하면 불필요하게 긴 이력 스택을 줄이고, 사용자 입장에서 자연스러운 undo 동작을 구현할 수 있습니다.

CODE BLOCK
class InsertCharCommand(QUndoCommand):
    def __init__(self, editor, char, pos):
        super().__init__("문자 입력")
        self.editor = editor
        self.char = char
        self.pos = pos

    def redo(self):
        cursor = self.editor.textCursor()
        cursor.setPosition(self.pos)
        cursor.insertText(self.char)
        self.pos += 1

    def undo(self):
        cursor = self.editor.textCursor()
        cursor.setPosition(self.pos - 1)
        cursor.movePosition(cursor.NextCharacter, cursor.KeepAnchor)
        cursor.removeSelectedText()
        self.pos -= 1

    def mergeWith(self, other):
        if not isinstance(other, InsertCharCommand):
            return False
        self.char += other.char
        return True

이처럼 mergeWith를 구현하면 스택은 여러 개의 유사 명령을 하나의 묶음으로 인식합니다.
텍스트 입력뿐 아니라 좌표 드래그, 슬라이더 조정 등에서도 동일한 방식으로 적용할 수 있습니다.
명령 설계는 재사용성, 가독성, 사용자 경험 모두를 좌우하는 핵심 포인트입니다.

💎 핵심 포인트:
매크로와 병합 기능을 조합하면 복잡한 UI 조작도 단일 undo 단위로 제어할 수 있습니다.
사용자가 한 번의 되돌리기로 모든 연속 입력을 취소하는 경험을 구현할 수 있죠.

🗂️ 상태 관리 setUndoLimit cleanIndex QUndoView

PySide의 QUndoStack은 단순한 이력 저장소를 넘어, 현재 상태를 추적하고 시각적으로 표현할 수 있는 강력한 기능을 제공합니다.
특히 setUndoLimit(), cleanIndex(), QUndoView는 프로젝트 전체의 상태를 체계적으로 관리하는 데 핵심적인 역할을 합니다.

📏 setUndoLimit과 메모리 관리

복잡한 애플리케이션에서는 수천 개의 명령이 스택에 쌓일 수 있습니다.
이럴 때 setUndoLimit(n)으로 이력의 최대 개수를 제한하면 메모리 사용량을 일정하게 유지할 수 있습니다.
제한을 초과하면 가장 오래된 명령부터 자동으로 삭제됩니다.
다만 중요한 체크포인트 전후에는 제한값 조정이나 별도 스택 분리를 고려하는 것이 좋습니다.

🧮 cleanIndex와 저장 상태 추적

파일 저장 기능이 있는 프로그램에서는 사용자가 편집 후 저장했는지 여부를 판단해야 합니다.
이때 cleanIndex를 활용하면 현재 이력이 ‘저장된 상태’인지 쉽게 추적할 수 있습니다.
저장 시점에 stack.setClean()을 호출하면, 이후 변경이 발생할 때 stack.isClean()으로 저장 필요 여부를 확인할 수 있습니다.

CODE BLOCK
# 저장 후 상태 표시
stack.setClean()

# 변경 발생 여부 확인
if not stack.isClean():
    print("파일이 수정되었습니다. 저장이 필요합니다.")

이 방법은 텍스트 에디터, 그래픽 툴, 데이터 모델러 등 다양한 환경에서 “저장 필요 여부 표시” 기능을 구현할 때 표준적으로 사용됩니다.
예를 들어 제목 표시줄에 “*”를 붙이거나, 저장 버튼의 활성화 상태를 변경하는 로직에 연결할 수 있습니다.

🪟 QUndoView로 이력 시각화

QUndoView는 스택의 명령 이력을 트리뷰 형태로 표시해 주는 위젯입니다.
이를 통해 개발자는 명령 흐름을 실시간으로 시각적으로 확인할 수 있고, 사용자는 현재 상태와 되돌리기 가능 항목을 명확히 인식할 수 있습니다.
특히 디버깅이나 복잡한 매크로 동작 검증 시 매우 유용합니다.

CODE BLOCK
from PySide6.QtWidgets import QUndoView, QDockWidget

undo_view = QUndoView(stack)
undo_view.setWindowTitle("명령 이력")

dock = QDockWidget("Undo Stack", main_window)
dock.setWidget(undo_view)
main_window.addDockWidget(Qt.RightDockWidgetArea, dock)

QUndoView는 각 명령의 setText()로 지정한 설명을 자동으로 표시하며, 선택하면 해당 시점으로 이동할 수도 있습니다.
UI 설계에서 명령의 상태를 가시적으로 표현할 때 매우 유용한 도구로, 복잡한 편집기나 시뮬레이터에서 빠질 수 없는 구성 요소입니다.

💎 핵심 포인트:
setUndoLimit으로 메모리를 관리하고, cleanIndex로 저장 상태를 추적하며, QUndoView로 이력을 시각화하면 안정적인 이력 관리 시스템이 완성됩니다.

  • 🧮setUndoLimit()으로 명령 이력 개수 제한
  • 💾cleanIndex로 저장 상태 추적 및 표시
  • 🪟QUndoView로 명령 이력 시각화
  • 📊UI 상태와 명령 스택을 항상 동기화하도록 설계

⚠️ 주의: setClean() 호출 시점은 반드시 파일 저장이 완료된 이후여야 합니다.
그렇지 않으면 사용자가 저장되지 않은 변경을 놓칠 수 있습니다.



🧪 실전 예제 텍스트 에디터 구현 스니펫

지금까지 살펴본 QUndoStack과 QUndoCommand의 구조를 실제로 적용해보면 이해가 훨씬 쉽습니다.
다음은 PySide를 사용해 되돌리기와 다시하기가 완벽히 작동하는 간단한 텍스트 에디터 예제입니다.
이 예시는 단순하지만, 명령 스택 관리, 액션 매핑, 저장 상태 추적 등 실무적인 포인트가 모두 포함되어 있습니다.

CODE BLOCK
from PySide6.QtWidgets import QApplication, QMainWindow, QTextEdit, QMenuBar, QFileDialog, QMessageBox
from PySide6.QtGui import QUndoStack, QUndoCommand, QAction, QKeySequence

class TextEditCommand(QUndoCommand):
    def __init__(self, editor, old_text, new_text, desc="텍스트 변경"):
        super().__init__(desc)
        self.editor = editor
        self.old_text = old_text
        self.new_text = new_text

    def undo(self):
        self.editor.blockSignals(True)
        self.editor.setPlainText(self.old_text)
        self.editor.blockSignals(False)

    def redo(self):
        self.editor.blockSignals(True)
        self.editor.setPlainText(self.new_text)
        self.editor.blockSignals(False)

class Editor(QMainWindow):
    def __init__(self):
        super().__init__()
        self.editor = QTextEdit()
        self.setCentralWidget(self.editor)
        self.stack = QUndoStack(self)

        self.editor.textChanged.connect(self.track_changes)
        self.last_text = ""

        # 메뉴 구성
        menu = self.menuBar().addMenu("편집")
        undo_action = self.stack.createUndoAction(self, "되돌리기")
        undo_action.setShortcut(QKeySequence.Undo)
        redo_action = self.stack.createRedoAction(self, "다시하기")
        redo_action.setShortcut(QKeySequence.Redo)
        menu.addAction(undo_action)
        menu.addAction(redo_action)

        self.setWindowTitle("PySide6 텍스트 에디터 with UndoStack")
        self.resize(600, 400)

    def track_changes(self):
        new_text = self.editor.toPlainText()
        if new_text != self.last_text:
            cmd = TextEditCommand(self.editor, self.last_text, new_text)
            self.stack.push(cmd)
            self.last_text = new_text

if __name__ == "__main__":
    app = QApplication([])
    win = Editor()
    win.show()
    app.exec()

위 코드에서는 TextEditCommand가 텍스트 변경 이력을 관리하고,
track_changes() 슬롯이 실제 명령 스택에 변화를 push합니다.
되돌리기/다시하기 단축키는 QKeySequence를 이용해 자동으로 매핑되며,
에디터 내부에서 발생한 텍스트 이벤트를 blockSignals()로 제어해 루프를 방지합니다.

💡 TIP: 실무에서는 mergeWith() 기능을 추가해 연속적인 입력을 하나의 명령으로 합치면
텍스트 입력 중에도 자연스러운 되돌리기 동작을 구현할 수 있습니다.

또한 stack.cleanIndex()를 사용해 파일 저장 상태를 추적하고,
저장 완료 후 setClean()을 호출하면, 저장되지 않은 변경 여부를 시각적으로 표시할 수 있습니다.
이를 통해 사용자에게 명확한 피드백을 제공하면서, 명령 스택 중심의 일관된 편집 흐름을 유지할 수 있습니다.

  • 🧱QUndoCommand로 텍스트 변경 캡슐화
  • 🪄track_changes()로 변경 감지 및 push
  • ⌨️Ctrl+Z / Ctrl+Shift+Z 단축키로 스택 제어
  • 🧩blockSignals()로 무한 루프 방지

💎 핵심 포인트:
QUndoStack은 단순한 이력 관리가 아닌, “작업의 책임을 명령 객체로 분리”하는 설계 철학을 담고 있습니다.
이 개념을 제대로 익히면 PySide 기반의 모든 GUI 애플리케이션에서 안정적이고 직관적인 되돌리기 기능을 구현할 수 있습니다.

자주 묻는 질문 (FAQ)

QUndoStack과 QUndoGroup의 차이는 무엇인가요?
QUndoStack은 단일 이력 관리 스택이며, QUndoGroup은 여러 스택을 묶어 전체 앱 단위로 제어할 수 있게 해줍니다.
예를 들어 문서 편집기에서 탭마다 다른 QUndoStack을 두고, QUndoGroup으로 통합 관리할 수 있습니다.
QUndoView는 사용자 편집용으로도 사용할 수 있나요?
QUndoView는 주로 개발자나 고급 사용자용으로 시각적 이력 확인에 사용됩니다.
기본적으로 읽기 전용이지만, 선택 시 특정 시점으로 이동하도록 설정할 수 있습니다.
되돌리기 이후 새로운 작업을 하면 기존 redo가 사라지나요?
네. QUndoStack의 기본 동작은 되돌린 이후 새 명령이 push되면 redo 이력은 자동으로 제거됩니다.
이는 대부분의 에디터가 채택하는 표준 동작입니다.
명령 이름은 어떻게 지정하나요?
QUndoCommand의 생성자에서 문자열 인자를 전달하거나, setText() 메서드를 호출해 지정할 수 있습니다.
QUndoView나 QAction 텍스트에서 해당 이름이 표시됩니다.
beginMacro와 endMacro는 중첩이 가능한가요?
네. 중첩이 가능합니다. 다만 복잡한 매크로를 구성할 때는 명확한 시작과 종료를 관리해야 하며,
스택 구조가 꼬이지 않도록 반드시 짝을 맞춰야 합니다.
mergeWith를 사용하면 어떤 장점이 있나요?
비슷한 명령을 하나로 병합해 이력의 길이를 줄이고, 되돌리기 단위를 자연스럽게 만듭니다.
예를 들어 연속된 키 입력을 한 번의 “텍스트 입력” 명령으로 처리할 수 있습니다.
cleanIndex는 자동으로 업데이트되나요?
아닙니다. cleanIndex는 명시적으로 setClean()을 호출해야 갱신됩니다.
주로 파일 저장 직후에 호출하여, 저장 상태와 편집 상태를 구분하는 데 사용됩니다.
QUndoStack은 스레드 안전한가요?
아니요. QUndoStack과 QUndoCommand는 GUI 스레드에서만 안전하게 작동합니다.
백그라운드 스레드에서 사용하려면 별도의 시그널/슬롯으로 데이터만 전달해야 합니다.

🧭 PySide QUndoStack으로 완성하는 되돌리기 시스템 정복

PySide(Qt for Python)의 QUndoStack과 QUndoCommand는 단순한 ‘되돌리기’ 기능을 넘어, 작업 이력 관리의 설계 철학을 담고 있습니다.
각 작업을 명령 객체로 분리하면, 복잡한 변경도 안전하게 추적하고 재실행할 수 있습니다.
이를 통해 애플리케이션의 신뢰성과 유지보수성이 비약적으로 향상되며, 사용자 입장에서는 직관적이고 매끄러운 작업 경험을 제공합니다.

되돌리기와 다시하기는 단순히 ‘이전 상태로 돌아가기’가 아니라, 앱의 모든 동작을 일관된 인터페이스로 관리하기 위한 핵심 구조입니다.
PySide에서는 이 개념을 명령 패턴으로 완벽하게 구현하고 있으며,
QUndoStack을 중심으로 setUndoLimit, cleanIndex, beginMacro, mergeWith 같은 고급 기능까지 제공합니다.
이제 단순한 편집기를 넘어서, CAD, 그래픽 툴, 데이터 편집기에서도 동일한 구조를 그대로 활용할 수 있습니다.

이 글에서 다룬 핵심 요약은 다음과 같습니다.
명령은 QUndoCommand로 구현하고, 스택은 QUndoStack이 관리합니다.
되돌리기/다시하기 액션은 QKeySequence로 단축키를 표준화하며, QUndoView를 통해 시각화합니다.
매크로와 병합 기능으로 사용자 경험을 정교하게 다듬고, cleanIndex를 통해 저장 상태를 명확히 구분합니다.
이 모든 요소를 결합하면, 실무 수준의 Undo/Redo 시스템을 손쉽게 완성할 수 있습니다.


🏷️ 관련 태그 : PySide6, QtforPython, QUndoStack, QUndoCommand, 되돌리기, 다시하기, 단축키, Command패턴, GUI프로그래밍, Python앱개발