디자인 패턴 in JS — Observer, Strategy, Factory, Singleton
디자인 패턴은 반복되는 설계 문제에 대한 검증된 해결책입니다. 자바스크립트에서 가장 실용적인 네 가지 패턴을 면접에서 설명할 수 있도록 정리했습니다.
Observer 패턴 — 이벤트 기반 설계
하나의 객체 상태가 변경되면 연관된 모든 객체에 자동 통지하는 패턴입니다. DOM 이벤트 시스템, React의 상태 관리가 이 패턴입니다.
class EventEmitter {
constructor() {
this.listeners = new Map();
}
on(event, callback) {
if (!this.listeners.has(event)) {
this.listeners.set(event, []);
}
this.listeners.get(event).push(callback);
return () => this.off(event, callback); // 구독 해제 함수 반환
}
off(event, callback) {
const callbacks = this.listeners.get(event);
if (callbacks) {
this.listeners.set(event, callbacks.filter((cb) => cb !== callback));
}
}
emit(event, data) {
const callbacks = this.listeners.get(event) || [];
callbacks.forEach((cb) => cb(data));
}
}
// 사용
const store = new EventEmitter();
const unsubscribe = store.on("userLogin", (user) => {
console.log(`${user.name}님이 로그인했습니다.`);
});
store.emit("userLogin", { name: "정훈" });
unsubscribe(); // 구독 해제
Strategy 패턴 — 알고리즘 교체
같은 인터페이스로 알고리즘을 교체할 수 있는 패턴입니다.
// 결제 전략
const paymentStrategies = {
creditCard: (amount) => {
console.log(`신용카드로 ${amount}원 결제`);
return { success: true, method: "creditCard" };
},
bankTransfer: (amount) => {
console.log(`계좌이체로 ${amount}원 결제`);
return { success: true, method: "bankTransfer" };
},
kakaoPay: (amount) => {
console.log(`카카오페이로 ${amount}원 결제`);
return { success: true, method: "kakaoPay" };
},
};
function processPayment(amount, strategy) {
const pay = paymentStrategies[strategy];
if (!pay) throw new Error(`지원하지 않는 결제 방식: ${strategy}`);
return pay(amount);
}
processPayment(10000, "kakaoPay");
// 정렬 전략
const sortStrategies = {
price: (a, b) => a.price - b.price,
name: (a, b) => a.name.localeCompare(b.name),
date: (a, b) => new Date(b.date) - new Date(a.date),
rating: (a, b) => b.rating - a.rating,
};
function sortProducts(products, strategy) {
return [...products].sort(sortStrategies[strategy]);
}
Factory 패턴 — 객체 생성 위임
객체 생성 로직을 별도로 분리하여 유연성을 높이는 패턴입니다.
// 알림 팩토리
class Notification {
constructor(message) {
this.message = message;
}
send() {
throw new Error("send()를 구현해야 합니다.");
}
}
class EmailNotification extends Notification {
send() { console.log(`이메일 발송: ${this.message}`); }
}
class SMSNotification extends Notification {
send() { console.log(`SMS 발송: ${this.message}`); }
}
class PushNotification extends Notification {
send() { console.log(`푸시 알림: ${this.message}`); }
}
// 팩토리 함수
function createNotification(type, message) {
const types = {
email: EmailNotification,
sms: SMSNotification,
push: PushNotification,
};
const NotificationClass = types[type];
if (!NotificationClass) throw new Error(`알 수 없는 타입: ${type}`);
return new NotificationClass(message);
}
const notification = createNotification("email", "회의 알림");
notification.send();
// 함수형 팩토리 (자바스크립트에서 더 자연스러운 방식)
function createLogger(prefix) {
return {
log: (msg) => console.log(`[${prefix}] ${msg}`),
warn: (msg) => console.warn(`[${prefix}] ${msg}`),
error: (msg) => console.error(`[${prefix}] ${msg}`),
};
}
const apiLogger = createLogger("API");
apiLogger.log("요청 시작");
Singleton 패턴 — 단일 인스턴스 보장
애플리케이션 전체에서 하나의 인스턴스만 사용하는 패턴입니다.
// 클래스 기반 싱글톤
class Database {
static instance = null;
static getInstance() {
if (!Database.instance) {
Database.instance = new Database();
}
return Database.instance;
}
constructor() {
if (Database.instance) {
throw new Error("getInstance()를 사용하세요.");
}
this.connection = null;
}
connect(url) {
this.connection = url;
console.log(`DB 연결: ${url}`);
}
}
const db1 = Database.getInstance();
const db2 = Database.getInstance();
console.log(db1 === db2); // true
// ES Module 싱글톤 (가장 자바스크립트다운 방식)
// config.js
const config = {
apiUrl: "https://api.example.com",
timeout: 5000,
};
export default Object.freeze(config);
// ESM은 모듈 캐싱으로 자연스럽게 싱글톤
패턴 선택 가이드
| 패턴 | 사용 시점 | JS 예시 |
|---|---|---|
| Observer | 이벤트, 상태 변경 알림 | EventEmitter, DOM 이벤트 |
| Strategy | 알고리즘 교체 | 정렬, 결제, 검증 전략 |
| Factory | 조건에 따른 객체 생성 | 컴포넌트 생성, 로거 |
| Singleton | 전역 단일 인스턴스 | DB 연결, 설정, 캐시 |
**기억하기 **: 자바스크립트에서는 클래스 기반보다 함수와 모듈을 활용한 패턴이 더 자연스럽습니다. Observer는 이벤트 시스템, Strategy는 객체/Map으로 전략 교체, Factory는 팩토리 함수, Singleton은 ES Module 캐싱으로 구현하는 것이 관용적입니다.
댓글 로딩 중...