메멘토 memento 패턴 - 타입스크립트 예시

SW설계
읽는데 5분 소요
처음 쓰여진 날: 2025-09-01
마지막 수정일: 2025-09-01

요약

메멘토 memento 패턴을 타입스크립트 코드와 함께 알아봅니다.

메멘토 (Memento) 패턴 요약

패턴 종류핵심 키워드
메멘토 (Memento)객체의 내부 상태를 외부에 노출시키지 않으면서, 객체의 특정 시점 상태를 스냅샷처럼 저장해 두었다가 필요할 때 다시 복원할 수 있게
메멘토 패턴 감자
메멘토 패턴 감자

메멘토 (Memento) 패턴

메멘토 패턴은 객체의 내부 상태를 외부에 노출시키지 않으면서, 객체의 특정 시점 상태를 스냅샷처럼 저장해 두었다가 필요할 때 다시 복원할 수 있게 하는 패턴입니다. 이 패턴의 가장 중요한 목표는 캡슐화(Encapsulation)를 유지하는 것입니다.

'게임 저장(Save Game)' 🎮 기능을 생각하면 완벽합니다. 플레이어는 게임의 현재 상태(주인공의 위치, 레벨, 아이템 등)를 저장했다가, 나중에 그 시점부터 다시 시작하고 싶을 때 불러올 수 있습니다. 이때 게임의 복잡한 내부 데이터 구조가 외부에 직접 노출되지 않고, '세이브 파일'이라는 불투명한 객체(메멘토)를 통해 안전하게 상태가 저장되고 복원됩니다.

기본 구조

  • Originator: 자신의 상태를 저장하고 복원해야 하는 원본 객체입니다. save() 메서드를 통해 현재 상태를 담은 Memento 객체를 생성하고, restore() 메서드를 통해 Memento 객체로부터 자신의 상태를 복원합니다.
  • Memento: Originator의 내부 상태를 저장하는 객체입니다. Originator만이 메멘토의 모든 데이터에 접근할 수 있어야 하며, 다른 객체들(특히 Caretaker)은 메멘토의 내부를 들여다볼 수 없습니다.
  • Caretaker: Memento 객체를 보관하고 관리하는 역할을 합니다. 하지만 메멘토의 내용을 검사하거나 수정하지는 않습니다. 그저 Originator로부터 메멘토를 받아 저장해 두었다가, 나중에 Originator에게 다시 돌려줄 뿐입니다. (예: 실행 취소 내역을 관리하는 History 객체)

예시: 간단한 텍스트 편집기 만들기

글을 작성하고, 특정 시점의 내용을 저장(스냅샷)했다가 되돌리는(undo) 간단한 텍스트 편집기를 만들어 보겠습니다.

먼저, 편집기의 상태를 저장할 Memento 클래스를 정의합니다.

typescript
// Memento: 편집기의 상태(내용)를 저장
class EditorMemento {
  // readonly를 사용하여 외부에서 수정 불가능하도록 함
  constructor(public readonly content: string) {}
}

다음으로, 상태를 저장하고 복원할 주체인 Originator, 즉 Editor 클래스를 만듭니다.

typescript
// Originator: 원본 객체
class Editor {
  private content: string = "";

  write(text: string): void {
    this.content += text;
  }

  getContent(): string {
    return this.content;
  }

  // 현재 상태를 메멘토에 저장
  save(): EditorMemento {
    console.log("상태 저장: ", this.content);
    return new EditorMemento(this.content);
  }

  // 메멘토로부터 상태를 복원
  restore(memento: EditorMemento): void {
    this.content = memento.content;
    console.log("상태 복원: ", this.content);
  }
}

마지막으로, 메멘토들을 관리할 CaretakerHistory 클래스를 만듭니다.

typescript
// Caretaker: 메멘토를 보관하지만 내용은 모름
class History {
  private mementos: EditorMemento[] = [];
  private editor: Editor;

  constructor(editor: Editor) {
    this.editor = editor;
  }

  push(): void {
    this.mementos.push(this.editor.save());
  }

  undo(): void {
    // 마지막 상태(현재 상태)를 버리기 위해 pop
    this.mementos.pop();

    // 그 이전 상태를 가져와 복원
    const lastMemento = this.mementos[this.mementos.length - 1];
    if (lastMemento) {
      this.editor.restore(lastMemento);
    } else {
      // 스택에 아무것도 없으면 초기 상태로 복원
      this.editor.restore(new EditorMemento(""));
    }
  }
}

// 클라이언트 코드
const editor = new Editor();
const history = new History(editor);

// 1. 첫 번째 문장 작성 및 저장
editor.write("안녕하세요. ");
history.push(); // "안녕하세요. " 상태 저장

// 2. 두 번째 문장 작성 및 저장
editor.write("메멘토 패턴입니다.");
history.push(); // "안녕하세요. 메멘토 패턴입니다." 상태 저장

// 3. 현재 내용 확인
console.log("현재 내용: ", editor.getContent());
// 출력: 현재 내용:  안녕하세요. 메멘토 패턴입니다.

// 4. 실행 취소 (Undo)
history.undo();
console.log("실행 취소 후 내용: ", editor.getContent());
// 출력:
// 상태 복원:  안녕하세요.
// 실행 취소 후 내용:  안녕하세요.

// 5. 한 번 더 실행 취소 (Undo)
history.undo();
console.log("두 번째 실행 취소 후 내용: ", editor.getContent());
// 출력:
// 상태 복원:
// 두 번째 실행 취소 후 내용:

History 객체는 EditorMemento 안에 content가 있는지조차 모릅니다. 그저 메멘토 객체를 스택에 넣고 빼는 역할만 충실히 수행합니다. 이 덕분에 Editor의 내부 상태(content)는 외부로부터 안전하게 보호되면서(캡슐화), 실행 취소와 같은 강력한 상태 관리 기능을 구현할 수 있습니다.

메멘토 패턴 중요 키워드

  • 캡슐화를 위반하지 않고 객체의 내부 상태를 외부에 저장합니다.
  • 스냅샷과 복원: 특정 시점의 상태를 저장하고, 나중에 그 상태로 되돌릴 수 있습니다.
  • 실행 취소(Undo/Redo), 트랜잭션, 상태 저장/불러오기 기능에 매우 유용합니다.
  • 역할 분리: 상태를 만드는 Originator, 상태를 저장하는 Memento, 상태를 관리하는 Caretaker로 역할이 명확히 나뉩니다.
문제
객체의 내부 상태를 외부에 노출시키지 않으면서 캡슐화를 위반하지 않고, 객체의 특정 시점 상태를 스냅샷처럼 저장해 두었다가 필요할 때 다시 복원할 수 있게 하는 패턴으로 실행 취소(Undo/Redo) 기능에 매우 유용한 디자인 패턴은?
보기
답변
정답정답 보기