본문 바로가기
차근차근

JavaScript 디자인 패턴 완전 정복|싱글턴, 팩토리, 옵저버 패턴 쉽게 배우기

by 아빠고미 2025. 5. 7.
반응형

🛠️ JavaScript 디자인 패턴 완전 정복|싱글턴, 팩토리, 옵저버 패턴 쉽게 이해하기

안녕하세요, 퍼블리셔 노미입니다!
지금까지 우리는 HTML, CSS, JavaScript 기본 문법, 비동기 처리, 객체지향 프로그래밍(OOP)까지 단계별로 완성해왔습니다.

이제 한 단계 더 나아가 복잡한 프로젝트를 더 깔끔하고 효율적으로 구성하는 방법을 알아야 할 때입니다.

바로 **디자인 패턴(Design Pattern)** 입니다.
디자인 패턴은 반복되는 문제를 효율적으로 해결하기 위한 코드 설계 방법입니다.

JavaScript 디자인 패턴 완전 정복|싱글턴, 팩토리, 옵저버 패턴 쉽게 배우기



오늘은 가장 실전성이 높은 패턴인 **싱글턴, 팩토리, 옵저버 패턴**을 중심으로 아주 자세히 정리해드릴게요.


📌 디자인 패턴이란?

디자인 패턴(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디자인패턴 #싱글턴패턴 #팩토리패턴 #옵저버패턴 #모듈패턴 #퍼사드패턴 #프론트엔드설계 #코드아키텍처 #퍼블리셔노미 #자바스크립트심화

반응형