JavaScript 디자인 패턴 완전 정복|싱글턴, 팩토리, 옵저버 패턴 쉽게 배우기
🛠️ JavaScript 디자인 패턴 완전 정복|싱글턴, 팩토리, 옵저버 패턴 쉽게 이해하기
안녕하세요, 퍼블리셔 노미입니다!
지금까지 우리는 HTML, CSS, JavaScript 기본 문법, 비동기 처리, 객체지향 프로그래밍(OOP)까지 단계별로 완성해왔습니다.
이제 한 단계 더 나아가 복잡한 프로젝트를 더 깔끔하고 효율적으로 구성하는 방법을 알아야 할 때입니다.
바로 **디자인 패턴(Design Pattern)** 입니다.
디자인 패턴은 반복되는 문제를 효율적으로 해결하기 위한 코드 설계 방법입니다.
오늘은 가장 실전성이 높은 패턴인 **싱글턴, 팩토리, 옵저버 패턴**을 중심으로 아주 자세히 정리해드릴게요.
📌 디자인 패턴이란?
디자인 패턴(Design Pattern)이란 프로그래밍에서 자주 발생하는 문제를 해결하기 위한 검증된 설계 방식입니다.
- 반복적인 문제를 해결하는 모범 사례
- 코드 재사용성, 유지보수성 향상
- 팀 개발 시 일관된 코드 스타일 유지
→ '코드의 품질'을 높이는 최고의 무기입니다!
🧩 자바스크립트와 디자인 패턴
자바스크립트는 함수형 프로그래밍과 객체지향 프로그래밍을 모두 지원하기 때문에 다양한 패턴을 자유롭게 적용할 수 있습니다.
특히 모듈 패턴, 싱글턴 패턴, 팩토리 패턴, 옵저버 패턴은 프론트엔드 개발에서 매우 자주 등장합니다.
👑 싱글턴 패턴(Singleton Pattern)
**싱글턴 패턴**은 애플리케이션 전체에서 하나의 인스턴스만 존재하게 만드는 패턴입니다.
예를 들어, 사이트 전체 설정값이나 로그인한 사용자 정보는 여러 개가 아니라 하나만 존재해야겠죠?
싱글턴 패턴 기본 구조
class Singleton {
constructor() {
if (Singleton.instance) {
return Singleton.instance;
}
Singleton.instance = this;
}
log() {
console.log('싱글턴 인스턴스입니다.');
}
}
const a = new Singleton();
const b = new Singleton();
console.log(a === b); // true
- 처음 생성된 인스턴스를 저장하고
- 이후 생성 시 기존 인스턴스를 반환
→ 항상 하나만 존재하는 객체를 보장합니다!
🧰 싱글턴 패턴 실전 예제
예를 들어, 설정(Config) 객체를 싱글턴으로 만들 수 있습니다.
class Config {
constructor() {
if (Config.instance) {
return Config.instance;
}
this.apiBaseUrl = "https://api.example.com";
Config.instance = this;
}
}
const config1 = new Config();
const config2 = new Config();
console.log(config1.apiBaseUrl); // "https://api.example.com"
console.log(config1 === config2); // true
→ 어디서든 Config 인스턴스를 공유할 수 있습니다!
🏭 팩토리 패턴(Factory Pattern)
**팩토리 패턴**은 객체 생성을 담당하는 역할을 별도로 분리하는 디자인 패턴입니다.
"어떤 클래스의 인스턴스를 만들지"를 직접 new 키워드로 결정하는 것이 아니라, 팩토리 함수를 통해 일관된 방법으로 생성합니다.
팩토리 패턴 기본 구조
class Dog {
speak() {
console.log('멍멍!');
}
}
class Cat {
speak() {
console.log('야옹~');
}
}
function AnimalFactory(type) {
if (type === 'dog') return new Dog();
if (type === 'cat') return new Cat();
}
const pet1 = AnimalFactory('dog');
const pet2 = AnimalFactory('cat');
pet1.speak(); // "멍멍!"
pet2.speak(); // "야옹~"
- 객체 생성 로직을 숨기고
- 통일된 인터페이스 제공
→ new 키워드를 직접 사용할 필요 없이 쉽게 객체를 생성할 수 있습니다!
🔧 팩토리 패턴의 장점
- 코드를 유연하고 확장성 있게 만든다
- 객체 생성 방식을 캡슐화한다
- 나중에 객체 종류가 바뀌어도 클라이언트 코드를 수정할 필요가 없다
객체 추가 시 유지보수 편리
class Bird {
speak() {
console.log('짹짹!');
}
}
// Factory 수정만 하면 끝!
function AnimalFactory(type) {
if (type === 'dog') return new Dog();
if (type === 'cat') return new Cat();
if (type === 'bird') return new Bird();
}
→ 기존 사용 코드는 변경할 필요가 없습니다!
👀 옵저버 패턴(Observer Pattern)
**옵저버 패턴**은 한 객체의 상태가 변경되었을 때, 그 변경을 감지하고 자동으로 다른 객체에 통보하는 패턴입니다.
브라우저 이벤트 시스템, 리액트 상태 관리에서도 많이 사용돼요.
옵저버 패턴 기본 구조
class Subject {
constructor() {
this.observers = [];
}
subscribe(observer) {
this.observers.push(observer);
}
unsubscribe(observer) {
this.observers = this.observers.filter(sub => sub !== observer);
}
notify(data) {
this.observers.forEach(observer => observer(data));
}
}
// 사용 예시
const subject = new Subject();
function logger(data) {
console.log('Logger:', data);
}
function analytics(data) {
console.log('Analytics:', data);
}
subject.subscribe(logger);
subject.subscribe(analytics);
subject.notify('사용자 로그인');
// Logger: 사용자 로그인
// Analytics: 사용자 로그인
- subscribe: 옵저버 등록
- unsubscribe: 옵저버 해제
- notify: 모든 옵저버에 알림
→ 객체 간 의존성 없이 느슨하게 연결할 수 있습니다!
🧠 옵저버 패턴 활용 예제: 알림 시스템
class NotificationCenter {
constructor() {
this.subscribers = [];
}
subscribe(callback) {
this.subscribers.push(callback);
}
notify(message) {
this.subscribers.forEach(cb => cb(message));
}
}
const center = new NotificationCenter();
center.subscribe((msg) => {
console.log(`메시지 도착: ${msg}`);
});
center.subscribe((msg) => {
console.log(`[알림] ${msg}`);
});
center.notify('새 댓글이 달렸습니다.');
// 메시지 도착: 새 댓글이 달렸습니다.
// [알림] 새 댓글이 달렸습니다.
→ 실시간 알림 구현의 기초!
📦 모듈 패턴(Module Pattern)
**모듈 패턴**은 코드를 하나의 독립된 단위로 묶고, 외부에 필요한 부분만 노출하는 패턴입니다.
변수 충돌을 방지하고, 코드 가독성과 관리성을 높여줍니다.
기본 구조 (즉시 실행 함수 사용)
const CounterModule = (function() {
let count = 0;
function increase() {
count++;
console.log(count);
}
function decrease() {
count--;
console.log(count);
}
return {
up: increase,
down: decrease
};
})();
CounterModule.up(); // 1
CounterModule.up(); // 2
CounterModule.down(); // 1
- 즉시 실행 함수(IIFE)로 스코프를 분리
- 외부에는 필요한 메서드만 반환 (캡슐화)
→ 깔끔한 네임스페이스 관리와 데이터 보호 가능!
🎯 퍼사드 패턴(Facade Pattern)
**퍼사드 패턴**은 복잡한 서브시스템을 감추고, 간단한 인터페이스만 제공하는 패턴입니다.
복잡한 내부 로직을 숨기고, 사용자에게는 단순한 API만 노출하는 거죠.
퍼사드 패턴 예시
// 복잡한 내부 로직
function openDoor() {
console.log('문 열기');
}
function startEngine() {
console.log('엔진 켜기');
}
function drive() {
console.log('운전 시작');
}
// 퍼사드
function startCar() {
openDoor();
startEngine();
drive();
}
// 사용자는 startCar만 호출
startCar();
- 복잡한 과정을 숨기고 하나의 함수로 단순화
- 사용자 경험 향상
→ 퍼사드 패턴은 복잡한 시스템을 쉽게 사용할 수 있도록 만들어줍니다!
📚 디자인 패턴 요약 정리표
패턴 | 목적 | 주요 특징 |
---|---|---|
싱글턴 | 단 하나의 인스턴스만 유지 | 글로벌 상태 관리 |
팩토리 | 객체 생성 로직 분리 | 확장성, 유연성 향상 |
옵저버 | 변경 사항 알림 | 이벤트 기반 시스템 구축 |
모듈 | 코드 캡슐화 | 스코프 보호, 인터페이스 제공 |
퍼사드 | 복잡성 숨기기 | 단순한 API 제공 |
🚀 프론트엔드에서 디자인 패턴 실전 적용 방법
- 상태 관리 (ex. 싱글턴 패턴으로 전역 상태 저장소 구축)
- API 호출 모듈화 (ex. 모듈 패턴으로 API 함수 정리)
- 다크모드 전환 기능 (ex. 옵저버 패턴으로 상태 감지)
- 모바일 메뉴 열기/닫기 (ex. 퍼사드 패턴으로 복잡한 로직 숨기기)
실전 예시: 다크모드 옵저버 패턴 적용
class DarkModeSubject {
constructor() {
this.subscribers = [];
}
subscribe(fn) {
this.subscribers.push(fn);
}
toggle() {
document.body.classList.toggle('dark-mode');
this.subscribers.forEach(fn => fn(document.body.classList.contains('dark-mode')));
}
}
const darkMode = new DarkModeSubject();
darkMode.subscribe((isDark) => {
console.log('다크모드 상태:', isDark);
});
// 버튼 클릭 이벤트
document.getElementById('toggleBtn').addEventListener('click', () => {
darkMode.toggle();
});
→ 상태 변화를 여러 컴포넌트에 쉽게 전달할 수 있습니다!
⚡ 디자인 패턴 적용 시 주의사항
- 패턴 남용 금지 (불필요한 복잡성 초래 가능)
- 프로젝트 규모에 맞게 선택적으로 적용
- 코드 가독성 유지 우선
- 팀 규칙과 스타일 가이드라인 준수
→ 패턴은 문제를 해결하기 위한 도구이지, 목적이 아닙니다!
📚 마무리하며
디자인 패턴은 개발자의 무기입니다.
싱글턴, 팩토리, 옵저버, 모듈, 퍼사드 패턴을 잘 이해하고 적용하면 어떤 프로젝트를 하더라도 구조적이고 유지보수하기 쉬운 코드를 작성할 수 있습니다.
이번 글을 통해 디자인 패턴에 대한 감을 잡았다면, 다음에는 컴포지트 패턴, 상태 패턴, 전략 패턴 같은 고급 패턴도 함께 배워봅시다! 끝까지 함께 성장해요! 🚀
#JavaScript디자인패턴 #싱글턴패턴 #팩토리패턴 #옵저버패턴 #모듈패턴 #퍼사드패턴 #프론트엔드설계 #코드아키텍처 #퍼블리셔노미 #자바스크립트심화