Recent Posts
Recent Comments
반응형
«   2026/08   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
Archives
Today
Total
관리 메뉴

오늘도 공부

# 🚀 한눈에 정리하는 23가지 핵심 디자인 패턴 (Design Patterns for Humans) 본문

개발상식

# 🚀 한눈에 정리하는 23가지 핵심 디자인 패턴 (Design Patterns for Humans)

행복한 수지아빠 2026. 8. 5. 14:41
반응형

"디자인 패턴을 머릿속에 쏙쏙 들어오게!"
누구나 한번쯤 머리가 아팠을 디자인 패턴, 직관적인 현실 세계 비유JavaScript(Node.js) & Python 예제 코드로 가장 쉽고 명쾌하게 정리했습니다.


📌 목차 (Table of Contents)

🏗️ 생성 패턴 (Creational) 🌉 구조 패턴 (Structural) 🔄 행동 패턴 (Behavioral)
🏠 Simple Factory 🔌 Adapter 🔗 Chain of Responsibility
🏭 Factory Method 🚡 Bridge 👮 Command
🔨 Abstract Factory 🌿 Composite ➿ Iterator
👷 Builder ☕ Decorator 👽 Mediator
🐑 Prototype 📦 Facade 💾 Memento
💍 Singleton 🍃 Flyweight 😎 Observer
🎱 Proxy 🏃 Visitor
💡 Strategy
💢 State
📒 Template Method

📖 디자인 패턴 들어가기 (Introduction)

[!IMPORTANT]
디자인 패턴이란?
자주 발생하는 문제들에 대한 일반적이고 재사용 가능한 해결책(가이드라인)입니다. 가져다 바로 꽂아 넣는 플러그인이나 라이브러리가 아니라, 상황을 풀어나가기 위한 디자인 템플릿에 가깝습니다.

⚠️ 사용 시 주의사항 (Be Careful)

  • 💡 만능 해결책(Silver Bullet)이 아닙니다. 패턴을 억지로 적용하려고 하면 오히려 설계가 어그러집니다.
  • 💡 문제에 맞춘 해결책입니다. 해결책에 맞춰 문제를 발굴하는 과도한 설계를 경계하세요.
  • 💡 적재적소에 올바르게 쓰면 훌륭한 구원자가 되지만, 잘못 남용하면 코드가 악몽의 난장판이 될 수 있습니다.

🏗️ 생성 디자인 패턴 (Creational Design Patterns)

한 줄 요약: 객체 또는 연관된 객체 그룹을 어떻게 인스턴스화할 것인가에 집중하는 패턴입니다.


🏠 1. Simple Factory (쉬운 공장)

현실 예시:
집을 짓다가 문이 필요할 때 직접 목수 옷을 입고 나무와 톱을 들고 문을 짤 수도 있지만, 공장에 주문해서 배송받는 편이 훨씬 깔끔합니다. 문을 만드는 복잡한 과정이나 먼지를 뒤집어쓸 필요가 없기 때문입니다.

한 줄 정의:
클라이언트에게 인스턴스화 로직을 노출하지 않고 인스턴스를 만들어 제공합니다.

💻 프로그래밍 예제

JavaScript (Node.js) 예제 보기
class WoodenDoor {
  constructor(width, height) {
    this.width = width;
    this.height = height;
  }

  getWidth() {
    return this.width;
  }

  getHeight() {
    return this.height;
  }
}

class DoorFactory {
  static makeDoor(width, height) {
    return new WoodenDoor(width, height);
  }
}

// 사용 예시
const door = DoorFactory.makeDoor(100, 200);
console.log(`Width: ${door.getWidth()}, Height: ${door.getHeight()}`);
Python 예제 보기
from abc import ABC, abstractmethod

class Door(ABC):
    @abstractmethod
    def get_width(self) -> float: pass

    @abstractmethod
    def get_height(self) -> float: pass

class WoodenDoor(Door):
    def __init__(self, width: float, height: float):
        self._width = width
        self._height = height

    def get_width(self) -> float: return self._width
    def get_height(self) -> float: return self._height

class DoorFactory:
    @staticmethod
    def make_door(width: float, height: float) -> Door:
        return WoodenDoor(width, height)

# 사용 예시
door1 = DoorFactory.make_door(100, 200)
print(f"Width: {door1.get_width()}, Height: {door1.get_height()}")

💡 언제 사용하나요?
단순 생성을 넘어 약간의 초기화 로직이나 설정 작업이 필요할 때, 해당 생성을 전용 팩토리 클래스로 묶어 코드 중복을 방지할 때 사용합니다.


🏭 2. Factory Method (공장 메서드)

현실 예시:
총괄 채용 매니저 혼자 모든 직군의 면접을 진행하는 것은 불가능합니다. 면접 진행이라는 전체 큰 틀은 갖고 있되, 구체적으로 어떤 면접관을 세울지는 각 분야(개발, 마케팅) 팀장에게 위임합니다.

한 줄 정의:
객체 생성 인터페이스를 정의하지만, 어떤 클래스의 인스턴스를 생성할지는 서브클래스에 위임합니다.

💻 프로그래밍 예제

JavaScript (Node.js) 예제 보기
class Developer {
  askQuestions() {
    console.log('디자인 패턴에 대해 질문합니다!');
  }
}

class CommunityExecutive {
  askQuestions() {
    console.log('커뮤니티 빌딩에 대해 질문합니다!');
  }
}

class HiringManager {
  // Factory Method (하위 클래스에서 구현)
  makeInterviewer() {
    throw new Error('makeInterviewer() 메서드를 구현해야 합니다.');
  }

  takeInterview() {
    const interviewer = this.makeInterviewer();
    interviewer.askQuestions();
  }
}

class DevelopmentManager extends HiringManager {
  makeInterviewer() {
    return new Developer();
  }
}

class MarketingManager extends HiringManager {
  makeInterviewer() {
    return new CommunityExecutive();
  }
}

// 사용 예시
const devManager = new DevelopmentManager();
devManager.takeInterview(); // 디자인 패턴에 대해 질문합니다!

const marketingManager = new MarketingManager();
marketingManager.takeInterview(); // 커뮤니티 빌딩에 대해 질문합니다!
Python 예제 보기
from abc import ABC, abstractmethod

class Interviewer(ABC):
    @abstractmethod
    def ask_questions(self): pass

class Developer(Interviewer):
    def ask_questions(self):
        print("디자인 패턴에 대해 질문합니다!")

class CommunityExecutive(Interviewer):
    def ask_questions(self):
        print("커뮤니티 빌딩에 대해 질문합니다!")

class HiringManager(ABC):
    @abstractmethod
    def make_interviewer(self) -> Interviewer: pass

    def take_interview(self):
        interviewer = self.make_interviewer()
        interviewer.ask_questions()

class DevelopmentManager(HiringManager):
    def make_interviewer(self) -> Interviewer:
        return Developer()

# 사용 예시
dev_manager = DevelopmentManager()
dev_manager.take_interview()

💡 언제 사용하나요?
상위 클래스가 자신이 생성해야 하는 객체의 구체적인 클래스를 미리 알 수 없을 때 유용합니다.


🔨 3. Abstract Factory (추상 공장)

현실 예시:
목재 문에는 목수가 필요하고, 철제 문에는 용접공이 필요합니다. 문이라는 제품과 설치 기사라는 전문가 사이에 끈끈한 연관성/의존성이 존재할 때 이를 그룹 단위로 세트로 제공하는 매장 개념입니다.

한 줄 정의:
연관되거나 의존적인 객체들의 패밀리를 구체적인 클래스 지정 없이 생성할 수 있는 "공장들의 공장"입니다.

💻 프로그래밍 예제

JavaScript (Node.js) 예제 보기
class WoodenDoor {
  getDescription() { console.log('목재 문입니다.'); }
}
class IronDoor {
  getDescription() { console.log('철제 문입니다.'); }
}

class Carpenter {
  getDescription() { console.log('목재 문 전문가입니다.'); }
}
class Welder {
  getDescription() { console.log('철제 문 전문가입니다.'); }
}

// 추상 공장 역
class WoodenDoorFactory {
  makeDoor() { return new WoodenDoor(); }
  makeFittingExpert() { return new Carpenter(); }
}

class IronDoorFactory {
  makeDoor() { return new IronDoor(); }
  makeFittingExpert() { return new Welder(); }
}

// 사용 예시
const woodenFactory = new WoodenDoorFactory();
const door = woodenFactory.makeDoor();
const expert = woodenFactory.makeFittingExpert();

door.getDescription();   // 목재 문입니다.
expert.getDescription(); // 목재 문 전문가입니다.
Python 예제 보기
from abc import ABC, abstractmethod

class Door(ABC):
    @abstractmethod
    def get_description(self): pass

class WoodenDoor(Door):
    def get_description(self): print("목재 문입니다.")

class DoorFittingExpert(ABC):
    @abstractmethod
    def get_description(self): pass

class Carpenter(DoorFittingExpert):
    def get_description(self): print("목재 문 전문가입니다.")

class DoorFactory(ABC):
    @abstractmethod
    def make_door(self) -> Door: pass
    @abstractmethod
    def make_fitting_expert(self) -> DoorFittingExpert: pass

class WoodenDoorFactory(DoorFactory):
    def make_door(self) -> Door: return WoodenDoor()
    def make_fitting_expert(self) -> DoorFittingExpert: return Carpenter()

# 사용 예시
factory = WoodenDoorFactory()
door = factory.make_door()
expert = factory.make_fitting_expert()
door.get_description()
expert.get_description()

💡 언제 사용하나요?
연관된 제품군을 묶어서 생성해야 하고, 서로 호환되지 않는 제품들이 섞이는 것을 방지하고 싶을 때 사용합니다.


👷 4. Builder (빌더)

현실 예시:
패스트푸드점에서 "세트 하나 주세요"는 단순 생성(Simple Factory)이지만, 수제 샌드위치 매장에서 "빵은 아티잔, 치즈는 체다, 소스는 칠리 추가해주세요"처럼 복잡한 옵션을 단계별로 조합하는 것이 빌더입니다.

한 줄 정의:
복잡한 객체의 생성 과정과 표현 방법을 분리하여 동일한 생성 절차에서 서로 다른 표현 결과를 만들 수 있게 합니다 (망원경 생성자 안티패턴 방지).

💻 프로그래밍 예제

JavaScript (Node.js) 예제 보기
class Burger {
  constructor(builder) {
    this.size = builder.size;
    this.cheese = builder.cheese || false;
    this.pepperoni = builder.pepperoni || false;
    this.lettuce = builder.lettuce || false;
    this.tomato = builder.tomato || false;
  }
}

class BurgerBuilder {
  constructor(size) {
    this.size = size;
  }

  addCheese() {
    this.cheese = true;
    return this;
  }

  addPepperoni() {
    this.pepperoni = true;
    return this;
  }

  addTomato() {
    this.tomato = true;
    return this;
  }

  build() {
    return new Burger(this);
  }
}

// 사용 예시 (메서드 체이닝)
const burger = new BurgerBuilder(14)
  .addPepperoni()
  .addCheese()
  .build();
Python 예제 보기
class Burger:
    def __init__(self, builder):
        self.size = builder.size
        self.cheese = builder.cheese
        self.pepperoni = builder.pepperoni

class BurgerBuilder:
    def __init__(self, size: int):
        self.size = size
        self.cheese = False
        self.pepperoni = False

    def add_cheese(self):
        self.cheese = True
        return self

    def add_pepperoni(self):
        self.pepperoni = True
        return self

    def build(self) -> Burger:
        return Burger(self)

# 사용 예시
burger = BurgerBuilder(14).add_cheese().add_pepperoni().build()

💡 언제 사용하나요?
인자(Parameter)가 엄청 많거나 생성이 여러 단계로 나누어져 선택적 옵션이 많을 때 쓰면 코드가 훨씬 깔끔해집니다.


🐑 5. Prototype (프로토타입)

현실 예시:
복제 양 돌리를 생각해 보세요! 처음부터 하나하나 다시 키우고 설계하는 것보다 완성된 원본을 복제(Clone)해서 살짝 수정하는 것이 훨씬 빠르고 비용이 적게 듭니다.

한 줄 정의:
기존 객체를 복제(Cloning)하여 새로운 객체를 생성하는 패턴입니다.

💻 프로그래밍 예제

JavaScript (Node.js) 예제 보기
class Sheep {
  constructor(name, category = 'Mountain Sheep') {
    this.name = name;
    this.category = category;
  }

  setName(name) {
    this.name = name;
  }

  getName() {
    return this.name;
  }

  clone() {
    return Object.assign(Object.create(Object.getPrototypeOf(this)), this);
  }
}

// 사용 예시
const original = new Sheep('Jolly');
const cloned = original.clone();
cloned.setName('Dolly');

console.log(original.getName()); // Jolly
console.log(cloned.getName());   // Dolly
Python 예제 보기
import copy

class Sheep:
    def __init__(self, name: str, category: str = "Mountain Sheep"):
        self.name = name
        self.category = category

    def clone(self):
        return copy.deepcopy(self)

# 사용 예시
original = Sheep("Jolly")
cloned = original.clone()
cloned.name = "Dolly"

print(original.name) # Jolly
print(cloned.name)   # Dolly

💍 6. Singleton (싱글톤)

현실 예시:
한 나라에는 한 시점에 오직 단 한 명의 대통령만 존재해야 합니다. 필요할 때마다 대통령을 새로 찍어낼 수는 없습니다.

한 줄 정의:
클래스의 인스턴스가 애플리케이션 내에서 단 하나만 존재하도록 보장하고, 어디서든 접근할 수 있는 전역 접점을 제공합니다.

💻 프로그래밍 예제

JavaScript (Node.js) 예제 보기
class President {
  constructor() {
    if (President.instance) {
      return President.instance;
    }
    this.name = 'President';
    President.instance = this;
  }
}

// 사용 예시
const p1 = new President();
const p2 = new President();

console.log(p1 === p2); // true
Python 예제 보기
class President:
    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

# 사용 예시
p1 = President()
p2 = President()
print(p1 is p2) # True

🌉 구조 디자인 패턴 (Structural Design Patterns)

한 줄 요약: 객체들을 큰 구조로 합성하거나, 서로 호환되지 않는 인터페이스를 연결하여 효율적인 관계를 형성하는 것에 관한 패턴입니다.


🔌 1. Adapter (어댑터)

현실 예시:
SD 카드의 데이터를 노트북에 옮기고 싶은데 노트북에는 MicroSD 슬롯만 있다면? 카드 리더기(어댑터)를 끼워서 형태를 바꿔 통신합니다.

한 줄 정의:
호환되지 않는 인터페이스를 가진 객체들이 함께 일할 수 있도록 중간에서 변환 역할을 감싸주는(Wrapper) 패턴입니다.

💻 프로그래밍 예제

JavaScript (Node.js) 예제 보기
class AfricanLion {
  roar() {}
}

class Hunter {
  hunt(lion) {
    lion.roar();
  }
}

class WildDog {
  bark() {
    console.log('Woof woof!');
  }
}

// 어댑터 클래스
class WildDogAdapter {
  constructor(dog) {
    this.dog = dog;
  }

  roar() {
    this.dog.bark();
  }
}

// 사용 예시
const dog = new WildDog();
const adapter = new WildDogAdapter(dog);
const hunter = new Hunter();
hunter.hunt(adapter); // Woof woof!
Python 예제 보기
class Lion:
    def roar(self): pass

class Hunter:
    def hunt(self, lion: Lion):
        lion.roar()

class WildDog:
    def bark(self):
        print("Woof woof!")

class WildDogAdapter(Lion):
    def __init__(self, dog: WildDog):
        self.dog = dog

    def roar(self):
        self.dog.bark()

# 사용 예시
dog = WildDog()
adapter = WildDogAdapter(dog)
hunter = Hunter()
hunter.hunt(adapter) # Woof woof!

🚡 2. Bridge (브리지)

현실 예시:
웹사이트에 '소개 페이지', '홈 페이지'가 있고 각 페이지는 '다크 테마', '라이트 테마'를 적용할 수 있습니다. 상속으로 만들면 2x2=4개의 클래스를 만들어야 하지만, 페이지(추상화)와 테마(구현)를 브리지로 분리하여 합성하면 클래스가 2+2=4개로 확장성이 훨씬 좋아집니다.

한 줄 정의:
추상화(Abstraction)와 구현(Implementation)을 분리하여 두 계층이 독립적으로 변형될 수 있도록 만드는 패턴입니다.

💻 프로그래밍 예제

JavaScript (Node.js) 예제 보기
class DarkTheme {
  getColor() { return 'Dark Black'; }
}

class LightTheme {
  getColor() { return 'Off White'; }
}

class AboutPage {
  constructor(theme) {
    this.theme = theme;
  }

  getContent() {
    return `About page in ${this.theme.getColor()}`;
  }
}

// 사용 예시
const darkPage = new AboutPage(new DarkTheme());
console.log(darkPage.getContent()); // About page in Dark Black
Python 예제 보기
class DarkTheme:
    def get_color(self): return "Dark Black"

class AboutPage:
    def __init__(self, theme):
        self.theme = theme

    def get_content(self):
        return f"About page in {self.theme.get_color()}"

# 사용 예시
page = AboutPage(DarkTheme())
print(page.get_content())

🌿 3. Composite (컴포지트)

현실 예시:
회사는 개별 개발자(단일 객체)들로 구성되기도 하고, 개발팀(객체 그룹)으로 구성되기도 합니다. 대표님이 "이번 달 급여 총액 얼마야?"라고 물었을 때 단일 직원이나 팀 전체나 똑같이 getSalary()로 일관되게 조회할 수 있어야 합니다.

한 줄 정의:
객체들을 트리 구조로 구성하여 개별 객체와 복합 객체를 클라이언트 입장에서 동일하게 다룰 수 있게 합니다.

💻 프로그래밍 예제

JavaScript (Node.js) 예제 보기
class Developer {
  constructor(name, salary) {
    this.name = name;
    this.salary = salary;
  }

  getSalary() {
    return this.salary;
  }
}

class Organization {
  constructor() {
    this.employees = [];
  }

  addEmployee(employee) {
    this.employees.push(employee);
  }

  getSalary() {
    return this.employees.reduce((total, e) => total + e.getSalary(), 0);
  }
}

// 사용 예시
const org = new Organization();
org.addEmployee(new Developer('John', 1000));
org.addEmployee(new Developer('Jane', 1200));

console.log('Total Salary:', org.getSalary()); // 2200
Python 예제 보기
from abc import ABC, abstractmethod

class Employee(ABC):
    @abstractmethod
    def get_salary(self) -> float: pass

class Developer(Employee):
    def __init__(self, name: str, salary: float):
        self.name = name
        self.salary = salary

    def get_salary(self) -> float: return self.salary

class Organization(Employee):
    def __init__(self):
        self.employees = []

    def add_employee(self, employee: Employee):
        self.employees.append(employee)

    def get_salary(self) -> float:
        return sum(e.get_salary() for e in self.employees)

# 사용 예시
org = Organization()
org.add_employee(Developer("John", 1000))
org.add_employee(Developer("Jane", 1200))
print("Total:", org.get_salary())

☕ 4. Decorator (데코레이터)

현실 예시:
기본 에스프레소 커피에 우유 토핑을 얹으면 라떼가 되고, 휘핑크림을 또 얹으면 모카가 됩니다. 기본 제품의 틀을 변경하지 않고 필요한 옵션을 겉에 겹겹이 포장(감싸서) 추가하는 느낌입니다.

한 줄 정의:
객체에 동적으로 새로운 책임(기능)을 추가하는 패턴으로, 서브클래싱(상속)의 유연한 대안을 제공합니다.

💻 프로그래밍 예제

JavaScript (Node.js) 예제 보기
class SimpleCoffee {
  getCost() { return 10; }
  getDescription() { return 'Simple coffee'; }
}

class MilkCoffee {
  constructor(coffee) {
    this.coffee = coffee;
  }

  getCost() {
    return this.coffee.getCost() + 2;
  }

  getDescription() {
    return `${this.coffee.getDescription()}, milk`;
  }
}

// 사용 예시
let coffee = new SimpleCoffee();
coffee = new MilkCoffee(coffee);
console.log(coffee.getCost()); // 12
console.log(coffee.getDescription()); // Simple coffee, milk
Python 예제 보기
class Coffee:
    def get_cost(self) -> float: return 10.0
    def get_description(self) -> str: return "Simple coffee"

class MilkCoffee:
    def __init__(self, coffee: Coffee):
        self._coffee = coffee
    def get_cost(self) -> float: return self._coffee.get_cost() + 2.0
    def get_description(self) -> str: return self._coffee.get_description() + ", milk"

# 사용 예시
coffee = SimpleCoffee()
coffee_with_milk = MilkCoffee(coffee)
print(coffee_with_milk.get_cost()) # 12.0

📦 5. Facade (퍼사드)

현실 예시:
컴퓨터를 켤 때 메인보드 전원 공급, CPU 클럭 가동, 램 전압 체크 등 복잡한 과정을 일일이 조작하지 않습니다. 그저 전원 버튼 하나(Facade)만 누르면 내부 복잡한 서브시스템이 일괄 실행됩니다.

한 줄 정의:
서브시스템의 복잡한 인터페이스 집합에 대해 통합된 단순한 고수준 인터페이스를 제공합니다.

💻 프로그래밍 예제

JavaScript (Node.js) 예제 보기
class Computer {
  getElectricShock() { console.log('Ouch!'); }
  makeSound() { console.log('Beep beep!'); }
  showLoadingScreen() { console.log('Loading..'); }
}

class ComputerFacade {
  constructor(computer) {
    this.computer = computer;
  }

  turnOn() {
    this.computer.getElectricShock();
    this.computer.makeSound();
    this.computer.showLoadingScreen();
  }
}

// 사용 예시
const facade = new ComputerFacade(new Computer());
facade.turnOn();
Python 예제 보기
class Computer:
    def get_electric_shock(self): print("Ouch!")
    def make_sound(self): print("Beep beep!")
    def show_loading_screen(self): print("Loading..")

class ComputerFacade:
    def __init__(self, computer: Computer):
        self.computer = computer

    def turn_on(self):
        self.computer.get_electric_shock()
        self.computer.make_sound()
        self.computer.show_loading_screen()

# 사용 예시
facade = ComputerFacade(Computer())
facade.turn_on()

🍃 6. Flyweight (플라이웨이트)

현실 예시:
찻집에서 손님이 100명 왔을 때 매번 완전히 새로운 찻잔 객체를 100개 찍어내기보다, 동일한 종류의 차(상태가 같은 객체)는 재사용하여 메모리와 자원을 아끼는 방식입니다.

한 줄 정의:
많은 수의 유사한 객체를 효율적으로 공유하여 메모리 사용량을 절감시키는 패턴입니다.

💻 프로그래밍 예제

JavaScript (Node.js) 예제 보기
class KarakTea {}

class TeaMaker {
  constructor() {
    this.availableTea = {};
  }

  make(preference) {
    if (!this.availableTea[preference]) {
      this.availableTea[preference] = new KarakTea();
    }
    return this.availableTea[preference];
  }
}

// 사용 예시
const maker = new TeaMaker();
const tea1 = maker.make('less sugar');
const tea2 = maker.make('less sugar');

console.log(tea1 === tea2); // true (객체 공유)
Python 예제 보기
class KarakTea:
    pass

class TeaMaker:
    def __init__(self):
        self._available_tea = {}

    def make(self, preference: str) -> KarakTea:
        if preference not in self._available_tea:
            self._available_tea[preference] = KarakTea()
        return self._available_tea[preference]

# 사용 예시
maker = TeaMaker()
tea1 = maker.make("less sugar")
tea2 = maker.make("less sugar")
print(tea1 is tea2) # True

🎱 7. Proxy (프록시)

현실 예시:
연구실 문을 그냥 열 수도 있지만, 보안 출입 카드키(Proxy)를 거쳐야만 문이 열리도록 출입 통제 기능을 대리하는 것입니다.

한 줄 정의:
다른 객체에 대한 접근을 제어하거나 대리자 역할을 수행하는 객체를 제공합니다 (보안, 캐싱, 지연 로딩 등).

💻 프로그래밍 예제

JavaScript (Node.js) 예제 보기
class LabDoor {
  open() { console.log('Opening lab door'); }
  close() { console.log('Closing lab door'); }
}

class SecuredDoor {
  constructor(door) {
    this.door = door;
  }

  open(password) {
    if (password === 'secret') {
      this.door.open();
    } else {
      console.log('Access denied!');
    }
  }

  close() {
    this.door.close();
  }
}

// 사용 예시
const door = new SecuredDoor(new LabDoor());
door.open('wrong');  // Access denied!
door.open('secret'); // Opening lab door
Python 예제 보기
class LabDoor:
    def open(self): print("Opening lab door")

class SecuredDoor:
    def __init__(self, door: LabDoor):
        self.door = door

    def open(self, password: str):
        if password == "secret":
            self.door.open()
        else:
            print("Access denied!")

# 사용 예시
door = SecuredDoor(LabDoor())
door.open("wrong")  # Access denied!
door.open("secret") # Opening lab door

🔄 행동 디자인 패턴 (Behavioral Design Patterns)

한 줄 요약: 객체 간의 책임 분배, 알고리즘 제어 흐름, 그리고 통신 방식을 다루는 패턴입니다.


🔗 1. Chain of Responsibility (책임 사슬)

현실 예시:
결제 시스템에서 먼저 계좌 A(잔액 부족) ➔ 카드 B(잔액 부족) ➔ 카카오페이 C(성공)로 순차적으로 처리 권한을 넘겨가며 결제를 완료하는 구조입니다.

한 줄 정의:
요청을 처리할 수 있는 객체들을 사슬(Chain) 형태로 연결하여, 요청이 처리될 때까지 사슬을 따라 전달시킵니다.

💻 프로그래밍 예제

JavaScript (Node.js) 예제 보기
class Account {
  setNext(account) {
    this.successor = account;
  }

  pay(amountToPay) {
    if (this.canPay(amountToPay)) {
      console.log(`Paid ${amountToPay} using ${this.name}`);
    } else if (this.successor) {
      console.log(`Cannot pay using ${this.name}. Proceeding..`);
      this.successor.pay(amountToPay);
    } else {
      console.log('None of the accounts have enough balance');
    }
  }

  canPay(amount) {
    return this.balance >= amount;
  }
}

class Bank extends Account {
  constructor(balance) {
    super();
    this.name = 'Bank';
    this.balance = balance;
  }
}

class Paypal extends Account {
  constructor(balance) {
    super();
    this.name = 'Paypal';
    this.balance = balance;
  }
}

// 사용 예시
const bank = new Bank(100);
const paypal = new Paypal(200);

bank.setNext(paypal);
bank.pay(150); // Paid 150 using Paypal
Python 예제 보기
class Account:
    def __init__(self, balance: float):
        self.balance = balance
        self.next_account = None

    def set_next(self, account):
        self.next_account = account

    def pay(self, amount: float):
        if self.balance >= amount:
            print(f"Paid {amount} using {self.__class__.__name__}")
        elif self.next_account:
            self.next_account.pay(amount)
        else:
            print("Cannot pay!")

# 사용 예시
bank = Account(100)
paypal = Account(200)
bank.set_next(paypal)
bank.pay(150) # Paid 150 using Paypal

👮 2. Command (커맨드)

현실 예시:
손님이 주문서(Command)를 작성해서 웨이터(Invoker)에게 주면, 웨이터는 주문 내용을 신경 쓰지 않고 요리사(Receiver)에게 그대로 전달만 합니다. 요청 자체가 독립된 객체가 됩니다.

한 줄 정의:
요청 자체를 독립적인 객체로 캡슐화하여 요청 매개변수화, 작업 취소(Undo), 큐 저장 등을 가능하게 합니다.

💻 프로그래밍 예제

JavaScript (Node.js) 예제 보기
class Bulb {
  turnOn() { console.log('Light on'); }
  turnOff() { console.log('Light off'); }
}

class TurnOnCommand {
  constructor(bulb) {
    this.bulb = bulb;
  }

  execute() {
    this.bulb.turnOn();
  }
}

class RemoteControl {
  submit(command) {
    command.execute();
  }
}

// 사용 예시
const bulb = new Bulb();
const cmd = new TurnOnCommand(bulb);
const remote = new RemoteControl();
remote.submit(cmd); // Light on
Python 예제 보기
class Bulb:
    def turn_on(self): print("Light on")
    def turn_off(self): print("Light off")

class TurnOnCommand:
    def __init__(self, bulb: Bulb): self.bulb = bulb
    def execute(self): self.bulb.turn_on()

class RemoteControl:
    def submit(self, command): command.execute()

# 사용 예시
bulb = Bulb()
cmd = TurnOnCommand(bulb)
remote = RemoteControl()
remote.submit(cmd) # Light on

➿ 3. Iterator (반복자)

현실 예시:
라디오의 '다음 채널' 버튼을 누르면 내부 회로 구조가 복잡하게 어찌 되어 있든 단순히 순서대로 다음 채널로 넘어가서 청취할 수 있습니다.

한 줄 정의:
컬렉션의 내부 표현 방식을 노출하지 않고 그 안의 요소들에 순차적으로 접근할 수 있는 방법을 제공합니다.

💻 프로그래밍 예제

JavaScript (Node.js) 예제 보기
class StationList {
  constructor() {
    this.stations = [];
  }

  addStation(freq) {
    this.stations.push(freq);
  }

  // Symbol.iterator 구현
  *[Symbol.iterator]() {
    for (const station of this.stations) {
      yield station;
    }
  }
}

// 사용 예시
const stations = new StationList();
stations.addStation(89.0);
stations.addStation(101.5);

for (const station of stations) {
  console.log(station);
}
Python 예제 보기
class StationList:
    def __init__(self):
        self.stations = []

    def add_station(self, freq: float):
        self.stations.append(freq)

    def __iter__(self):
        return iter(self.stations)

# 사용 예시
stations = StationList()
stations.add_station(89.0)
stations.add_station(101.5)
for s in stations:
    print(s)

👽 4. Mediator (중재자)

현실 예시:
채팅방(Mediator)이 존재하여 모든 유저가 서로 1:1로 직접 연결될 필요 없이 채팅방 서버 하나만을 통해 메시지를 송수신합니다.

한 줄 정의:
객체 간의 복잡한 상호작용을 중재자 객체에 캡슐화하여 객체 간의 결합도(Coupling)를 획기적으로 낮춥니다.

💻 프로그래밍 예제

JavaScript (Node.js) 예제 보기
class ChatRoom {
  showMessage(user, message) {
    console.log(`[${user.getName()}]: ${message}`);
  }
}

class User {
  constructor(name, chatMediator) {
    this.name = name;
    this.chatMediator = chatMediator;
  }

  getName() {
    return this.name;
  }

  send(message) {
    this.chatMediator.showMessage(this, message);
  }
}

// 사용 예시
const mediator = new ChatRoom();
const john = new User('John', mediator);
john.send('Hello!'); // [John]: Hello!
Python 예제 보기
class ChatRoom:
    def show_message(self, user, message: str):
        print(f"[{user.name}]: {message}")

class User:
    def __init__(self, name: str, mediator: ChatRoom):
        self.name = name
        self.mediator = mediator

    def send(self, message: str):
        self.mediator.show_message(self, message)

# 사용 예시
chat = ChatRoom()
john = User("John", chat)
john.send("Hello!")

💾 5. Memento (메멘토)

현실 예시:
텍스트 에디터에서 글을 쓰다가 Ctrl + Z (Undo)를 누르면 이전 저장 시점(스냅샷)으로 글의 상태가 롤백됩니다.

한 줄 정의:
캡슐화를 저해하지 않으면서 객체의 내부 상태를 스냅샷으로 저장하여 나중에 복원할 수 있게 합니다.

💻 프로그래밍 예제

JavaScript (Node.js) 예제 보기
class Memento {
  constructor(content) {
    this.content = content;
  }

  getContent() {
    return this.content;
  }
}

class Editor {
  constructor() {
    this.content = '';
  }

  type(text) {
    this.content += text;
  }

  save() {
    return new Memento(this.content);
  }

  restore(memento) {
    this.content = memento.getContent();
  }
}

// 사용 예시
const editor = new Editor();
editor.type('Hello World');
const saved = editor.save();

editor.type(' Extra text');
editor.restore(saved);
console.log(editor.content); // Hello World
Python 예제 보기
class Memento:
    def __init__(self, state: str): self._state = state
    def get_state(self): return self._state

class Editor:
    def __init__(self): self.content = ""
    def type(self, text: str): self.content += text
    def save(self): return Memento(self.content)
    def restore(self, memento: Memento): self.content = memento.get_state()

# 사용 예시
editor = Editor()
editor.type("Hello World")
saved = editor.save()
editor.type(" Extra text")
editor.restore(saved)
print(editor.content) # Hello World

😎 6. Observer (옵저버)

현실 예시:
YouTube 채널 구독과 같습니다. 채널에 새 영상이 업로드(상태 변경)되면 구독(Subscriber/Observer) 중인 시청자들에게 알림이 자동으로 보내집니다.

한 줄 정의:
객체의 상태 변화가 있을 때 이를 구독하고 있는 관찰자들에게 자동으로 알림을 전달하는 일대다 의존 관계 패턴입니다.

💻 프로그래밍 예제

JavaScript (Node.js) 예제 보기
class JobSeeker {
  constructor(name) {
    this.name = name;
  }

  notify(jobTitle) {
    console.log(`Hi ${this.name}, new job posted: ${jobTitle}`);
  }
}

class EmploymentAgency {
  constructor() {
    this.observers = [];
  }

  attach(observer) {
    this.observers.push(observer);
  }

  addJob(jobTitle) {
    this.observers.forEach(obs => obs.notify(jobTitle));
  }
}

// 사용 예시
const agency = new EmploymentAgency();
agency.attach(new JobSeeker('Alice'));
agency.addJob('Backend Developer');
Python 예제 보기
class JobSeeker:
    def __init__(self, name: str): self.name = name
    def notify(self, job_title: str): print(f"Hi {self.name}, new job: {job_title}")

class EmploymentAgency:
    def __init__(self): self.observers = []
    def attach(self, observer: JobSeeker): self.observers.append(observer)
    def add_job(self, title: str):
        for obs in self.observers: obs.notify(title)

# 사용 예시
agency = EmploymentAgency()
agency.attach(JobSeeker("Alice"))
agency.add_job("Backend Developer")

🏃 7. Visitor (방문자)

현실 예시:
관광객(Visitor)이 해외 국가를 방문할 때 현지 법이나 규칙을 바꾸지 않고도 관광객 자신의 고유한 동작(사진 찍기, 쇼핑하기 등)을 수행합니다.

한 줄 정의:
객체 구조의 클래스 코드를 변경하지 않고 새로운 연산/기능을 외부 방문자 클래스에 추가할 수 있게 합니다.

💻 프로그래밍 예제

JavaScript (Node.js) 예제 보기
class Monkey {
  shout() {
    console.log('Ooh oo aa aa!');
  }

  accept(operation) {
    operation.visitMonkey(this);
  }
}

class SpeakOperation {
  visitMonkey(monkey) {
    monkey.shout();
  }
}

// 사용 예시
const monkey = new Monkey();
const speak = new SpeakOperation();
monkey.accept(speak); // Ooh oo aa aa!
Python 예제 보기
class Monkey:
    def shout(self): print("Ooh oo aa aa!")
    def accept(self, operation): operation.visit_monkey(self)

class SpeakOperation:
    def visit_monkey(self, monkey: Monkey): monkey.shout()

# 사용 예시
monkey = Monkey()
speak = SpeakOperation()
monkey.accept(speak)

💡 8. Strategy (전략)

현실 예시:
정렬할 데이터 양에 따라 작은 데이터는 버블 정렬, 큰 데이터는 퀵 정렬로 알고리즘 전략을 런타임에 유연하게 변경합니다.

한 줄 정의:
동일 계열의 알고리즘들을 개별 클래스로 캡슐화하여 런타임에 동적으로 교체해서 사용할 수 있게 합니다.

💻 프로그래밍 예제

JavaScript (Node.js) 예제 보기
class BubbleSort {
  sort(dataset) {
    console.log('Bubble sort applied');
    return dataset.sort();
  }
}

class QuickSort {
  sort(dataset) {
    console.log('Quick sort applied');
    return dataset.sort();
  }
}

class Sorter {
  constructor(strategy) {
    this.strategy = strategy;
  }

  sort(dataset) {
    return this.strategy.sort(dataset);
  }
}

// 사용 예시
const sorter = new Sorter(new BubbleSort());
sorter.sort([3, 1, 2]);
Python 예제 보기
class BubbleSort:
    def sort(self, data):
        print("Bubble sort applied")
        return sorted(data)

class QuickSort:
    def sort(self, data):
        print("Quick sort applied")
        return sorted(data)

class Sorter:
    def __init__(self, strategy): self.strategy = strategy
    def sort(self, data): return self.strategy.sort(data)

# 사용 예시
sorter = Sorter(BubbleSort())
sorter.sort([3, 1, 2])

💢 9. State (상태)

현실 예시:
스마트폰에서 똑같이 화면을 누르더라도 잠금 상태, 통화 상태, 카메라 작동 상태에 따라 각각 완전히 다른 동작을 수행하는 것과 같습니다.

한 줄 정의:
객체의 내부 상태 변경에 따라 객체의 행동을 변경하여, 마치 객체의 클래스가 바뀐 것처럼 동작하게 만듭니다.

💻 프로그래밍 예제

JavaScript (Node.js) 예제 보기
class StateIdle {
  pickUp() {
    console.log('Picked up');
    return new StatePickedUp();
  }
}

class StatePickedUp {
  pickUp() {
    console.log('Already picked up');
    return this;
  }
}

class Phone {
  constructor() {
    this.state = new StateIdle();
  }

  pickUp() {
    this.state = this.state.pickUp();
  }
}

// 사용 예시
const phone = new Phone();
phone.pickUp(); // Picked up
phone.pickUp(); // Already picked up
Python 예제 보기
class StateIdle:
    def pick_up(self):
        print("Picked up")
        return StatePickedUp()

class StatePickedUp:
    def pick_up(self):
        print("Already picked up")
        return self

class Phone:
    def __init__(self): self.state = StateIdle()
    def pick_up(self): self.state = self.state.pick_up()

# 사용 예시
phone = Phone()
phone.pick_up() # Picked up

📒 10. Template Method (템플릿 메서드)

현실 예시:
건축 공정에서 기초 공사 ➔ 벽 세우기 ➔ 지붕 얹기의 큰 틀(스켈레톤)은 일정하지만, 벽을 콘크리트로 할지 목재로 할지의 개별 세부 단계는 상황에 맞춰 다르게 구현합니다.

한 줄 정의:
알고리즘의 골격(뼈대)을 상위 클래스의 메서드에 정의하고, 일부 세부 단계는 하위 클래스에서 오버라이딩하도록 미룹니다.

💻 프로그래밍 예제

JavaScript (Node.js) 예제 보기
class Builder {
  // 템플릿 메서드
  build() {
    this.test();
    this.assemble();
  }

  test() {
    throw new Error('test() 메서드를 구현해야 합니다.');
  }

  assemble() {
    throw new Error('assemble() 메서드를 구현해야 합니다.');
  }
}

class AndroidBuilder extends Builder {
  test() {
    console.log('Testing Android code');
  }

  assemble() {
    console.log('Assembling APK');
  }
}

// 사용 예시
const builder = new AndroidBuilder();
builder.build();
Python 예제 보기
from abc import ABC, abstractmethod

class Builder(ABC):
    # Template method
    def build(self):
        self.test()
        self.assemble()

    @abstractmethod
    def test(self): pass
    @abstractmethod
    def assemble(self): pass

class AndroidBuilder(Builder):
    def test(self): print("Testing Android code")
    def assemble(self): print("Assembling APK")

# 사용 예시
builder = AndroidBuilder()
builder.build()

🚦 마무리하며 (Wrap-Up)

이것으로 핵심 23가지 디자인 패턴 정리를 마칩니다!
디자인 패턴은 코드를 무조건 복잡하게 만드는 도구가 아니라, 팀원 간의 공통 언어를 형성하고 유지보수성을 극대화하기 위한 약속입니다. 적재적소에 유연하게 활용해 보세요!


👬 기여 및 피드백 (Contribution)

  • 🐛 이슈 제보 (Report issues)
  • 🔀 개선 푸시 리퀘스트 (Open Pull Request)
  • 📣 주변 공유 및 전달 (Spread the word)
  • 🐦 작성자 인사하기: Twitter URL

📜 라이선스 (License)

License: CC BY 4.0

반응형