오늘도 공부
# 🚀 한눈에 정리하는 23가지 핵심 디자인 패턴 (Design Patterns for Humans) 본문
"디자인 패턴을 머릿속에 쏙쏙 들어오게!"
누구나 한번쯤 머리가 아팠을 디자인 패턴, 직관적인 현실 세계 비유와 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)
📜 라이선스 (License)
'개발상식' 카테고리의 다른 글
| 메인은 GPT-5.6, 서브에이전트는 DeepSeek V4 Flash로 설정하기 (0) | 2026.08.06 |
|---|---|
| Cerebras는 어떻게 사내 지식 검색 시스템을 구축했을까? (0) | 2026.08.05 |
| Codex에서 GPT-5.6 Luna를 서브에이전트로 사용하는 방법 (0) | 2026.08.01 |
| AI가 코드를 쓰는 시대, 개발자는 무엇을 설계해야 할까 (0) | 2026.07.18 |
| 이제는 “뭘 만들까?”보다 “누구의 어떤 문제를 풀까?”가 더 중요하다 (1) | 2026.03.24 |
