자바 디자인 패턴 용어 사전
행위 패턴메멘토 · 실행 취소

Memento

캡슐화를 깨지 않고 객체 상태를 저장·복원하는 패턴. 실행 취소의 기반이다.

캡슐화를 깨지 않고 객체의 내부 상태를 저장했다가 복원하는 패턴.

세 역할

  • Originator — 상태를 가진 객체. 메멘토를 만들고 메멘토로 복원한다
  • Memento — 상태 스냅샷. 내부를 Originator 만 볼 수 있다
  • Caretaker — 메멘토를 보관한다. 내용은 들여다보지 않는다
class Editor {                                  // Originator
    private String text;

    Snapshot save() { return new Snapshot(text); }
    void restore(Snapshot s) { this.text = s.text; }

    class Snapshot {                            // Memento (내부 클래스)
        private final String text;              // 밖에서 못 본다
        private Snapshot(String t) { this.text = t; }
    }
}

Deque<Editor.Snapshot> history = new ArrayDeque<>();   // Caretaker
history.push(editor.save());
editor.restore(history.pop());                          // 실행 취소

핵심은 '캡슐화를 깨지 않는다'

단순히 getter 로 상태를 다 꺼내 저장하면

  • 내부 구조가 밖으로 노출된다
  • 필드가 바뀌면 저장하는 쪽도 전부 바뀐다

Memento 는 상태를 '불투명한 객체' 로 넘긴다

  • Caretaker 는 보관만 하고 내용은 모른다

대가 — 메모리

스냅샷마다 전체 상태를 복사하면 메모리가 빠르게 는다

완화

  • 변경분만 저장 (delta·명령 기록)
  • 히스토리 개수 상한
  • 불변 객체면 구조 공유가 가능해 비용이 낮다

Command와 함께 쓴다

Command 로 '무엇을 했는지' 를 기록하고 Memento 로 '되돌릴 상태' 를 보관하면 완전한 undo/redo 가 된다

또는 Command 에 undo() 를 두어 역연산으로 되돌리는 방식도 있다 (역연산이 가능하면 메모리가 훨씬 적게 든다)

면접 함정

  • "getter로 저장하면 같다" → 캡슐화가 깨진다는 점이 이 패턴의 존재 이유다.
  • 직렬화와 동일시 → 직렬화는 저장 형식, Memento는 캡슐화 유지 설계다.

되돌리기의 두 전략

  • ① 상태 저장 (Memento)

    • 복원이 단순하다 / 메모리를 많이 쓴다
  • ② 역연산 (Command.undo)

    • 메모리가 적게 든다 / 모든 연산에 역연산이 있어야 한다
    • 되돌릴 수 없는 것도 있다 (삭제한 파일 내용 · 외부 API 호출)
  • 실무에서는 섞는다 — 되돌릴 수 있으면 역연산, 아니면 스냅샷

스냅샷 크기 줄이기

  • 전체가 아니라 변경분만 (delta)
  • 일정 간격 전체 스냅샷 + 사이는 delta (체크포인트)
  • 불변 자료구조면 구조 공유로 비용이 크게 준다
    • (바뀐 노드만 새로 만들고 나머지는 참조 공유)

직렬화와 혼동하지 않는다

  • 직렬화 — 객체를 바이트로 바꾸는 '형식' 의 문제 (저장·전송)

  • Memento — 캡슐화를 유지하며 상태를 넘기는 '설계' 의 문제

  • Memento 를 직렬화로 구현할 수는 있으나, 직렬화한다고 Memento 인 것은 아니다

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 행위 패턴 ② — 돌고, 알리고, 되돌린다