Design Pattern_Behavioral Patterns_Memento

박지홍·2026년 5월 4일

DesignPattern

목록 보기
11/12

메멘토 패턴

캡슐화를 유지하면서 객체 내부 상태를 외부에 저장.

예제) '세이브 파일' 게임 하다가 보스로 돌아가듯. 객체의 현재 상태를 스냅샷으로 저장해뒀다가, 나중에 그 시점으로 되돌리는 패턴.
핵심 : Originator 원본, Memento 스냅샷, Caretaker 보관자

before

Game.java — 점수 두 개를 가진 평범한 클래스야. getter/setter

public class Game implements Serializable {
    private int redTeamScore;
    private int blueTeamScore;

    public int getRedTeamScore() { return redTeamScore; }
    public void setRedTeamScore(int redTeamScore) { this.redTeamScore = redTeamScore; }
    public int getBlueTeamScore() { return blueTeamScore; }
    public void setBlueTeamScore(int blueTeamScore) { this.blueTeamScore = blueTeamScore; }
}

Client.java — 저장하고 복원하려고 별짓

Game game = new Game();
game.setRedTeamScore(10);
game.setBlueTeamScore(20);

// 저장: 필드를 일일이 꺼내서 변수에 담아둠
int blueTeamScore = game.getBlueTeamScore();
int redTeamScore = game.getRedTeamScore();

// 복원: 새 Game 만들고 setter 일일이 호출
Game restoredGame = new Game();
restoredGame.setBlueTeamScore(blueTeamScore);
restoredGame.setRedTeamScore(redTeamScore);

문제점

  1. 캡슐화가 깨짐. Client가 Game 내부 필드를 전부 알아야함. 객체 지향에서 객체 내부 상태는 본인이 관리해야하는데 외부가 다 끄집어냄
  2. 확장성이 안좋음. Game에 필드 하나 추가되면 Cleint도 그만큼 다 수정해야함.
  3. 저장 정보를 함부로 바꿀 수 있음. 변수 두 개를 들고 있으니, 누가 중간에 바꿔버릴 수 있음

after - 세이브 파일

GameSave.java — 새로 등장한 스냅샷 클래스

public final class GameSave {  // ← final 클래스: 상속 불가

    private final int blueTeamScore;  // ← final 필드: 한 번 정해지면 못 바꿈
    private final int redTeamScore;

    public GameSave(int blueTeamScore, int redTeamScore) {
        this.blueTeamScore = blueTeamScore;
        this.redTeamScore = redTeamScore;
    }

    public int getBlueTeamScore() { return blueTeamScore; }
    public int getRedTeamScore() { return redTeamScore; }
    // setter는 없음! 한 번 만들어지면 영원히 그 상태
}

Memento 패턴의 핵심.
1. final 클래스 + final 필드. 한 번 만들어진 스냅샷은 절대 바뀌면 안됨
2. setter가 없음. 생성자로 한 번 값을 박제하면 끝 외부에서 변경 못 함.
3. 그냥 점수 두 개 묶은 작은 객체

Game.java — Originator의 두 메서드

public class Game {
    private int redTeamScore;
    private int blueTeamScore;
    // ... getter/setter 동일 ...

    // 자기 상태를 박제해서 GameSave로 반환
    public GameSave save() {
        return new GameSave(this.blueTeamScore, this.redTeamScore);
    }

    // GameSave를 받아서 자기 상태를 그 시점으로 되돌림
    public void restore(GameSave gameSave) {
        this.blueTeamScore = gameSave.getBlueTeamScore();
        this.redTeamScore = gameSave.getRedTeamScore();
    }
}

저장과 복원을 Game 본인이 함

Client.java — 코드가 깔끔해짐

Game game = new Game();
game.setBlueTeamScore(10);
game.setRedTeamScore(20);

GameSave save = game.save();  // 저장!

// 점수 막 바꿔도 상관없음
game.setBlueTeamScore(12);
game.setRedTeamScore(22);

game.restore(save);  // 저장 시점으로 복원!

System.out.println(game.getBlueTeamScore());  // 10
System.out.println(game.getRedTeamScore());   // 20

저장은 save 복원은 restore

장단점

  • 장점

    • 캡슐화를 지키면서 상태 객체 상태 스냅샷 만들 수 있음.
    • 객체 상태 저장 또는 복원하는 역할을 CareTaker에게 위임할 수 있음.
    • 객체 상태가 바뀌어도 클라이언트 코드는 변경되지 않음.
  • 단점

    • 많은 정보를 저장하는 Mementor를 자주 생성하는 경우 메모리 사용량에 많은 영향을 줄 수 있음

실제 예제

  • 게임 세이브/로드
  • Spring Security의 SecurityContext도 인증 정보를 스레드별로 저장해뒀다가 필요할 때 꺼내 쓰는데, 이것도 비슷한 발상

0개의 댓글