옵저버 observer 패턴 - 자바스크립트 예제

SW설계
읽는데 5분 소요
처음 쓰여진 날: 2025-09-01
마지막 수정일: 2025-09-01

요약

옵저버 observer 패턴을 타입스크립트 코드와 함께 알아봅니다.

옵저버 패턴 요약

패턴 종류핵심 키워드
옵저버 (Observer)한 객체 바뀌면 의존하는 다른 객체에 연락 가고 자동으로 갱신, 일대다, 발행(Publish)/수신(Subscribe)
옵저버 패턴 감자
옵저버 패턴 감자

옵저버 (Observer) 패턴

옵저버 패턴은 한 객체의 상태가 변하면, 그 객체에 의존하는 다른 객체들에게 자동으로 알림(notification)을 보내고 상태를 갱신할 수 있게 하는 일대다(one-to-many) 의존성 관계를 정의하는 패턴입니다. 상태를 가진 객체를 Subject(주제), 상태의 변화를 통지받는 객체들을 Observer(관찰자)라고 합니다.

현실 세계의 예로 '유튜브 채널 구독' 을 들 수 있습니다. 유튜버(Subject)가 새로운 영상을 올리면, 그 채널을 구독(register)한 모든 구독자(Observer)들에게 자동으로 알림이 갑니다. 구독을 취소(unregister)하면 더 이상 알림을 받지 않습니다. 옵저버 패턴은 이처럼 느슨하게 연결된(loosely coupled) 객체들 간의 자동화된 소통 방식을 구현할 때 매우 유용합니다.

기본 구조

  • Subject: 관찰 대상이 되는 객체입니다. 내부에 옵저버 목록을 가지고 있으며, 옵저버를 등록(register)하고 제거(unregister)하는 메서드를 제공합니다. 상태가 변경되면 notify() 메서드를 호출하여 모든 옵저버에게 변경 사실을 알립니다.
  • Observer: Subject의 상태 변화를 통지받는 객체들이 구현해야 할 인터페이스입니다. 보통 update()와 같은 메서드를 정의하며, Subject로부터 알림이 오면 이 메서드가 호출됩니다.
  • ConcreteSubject: Subject 인터페이스를 구현한 구체적인 클래스입니다. 상태를 저장하고, 상태가 변경될 때 옵저버들에게 알리는 역할을 합니다.
  • ConcreteObserver: Observer 인터페이스를 구현한 구체적인 클래스입니다. update() 메서드가 호출되면 특정 동작을 수행합니다.

예시: 뉴스레터 구독 시스템

새로운 기사가 발행될 때마다 구독자들에게 이메일로 알려주는 뉴스레터 시스템을 만들어 보겠습니다.

먼저, 모든 옵저버(구독자)가 구현할 Observer 인터페이스를 정의합니다.

javascript
// Observer: 구독자 인터페이스
class Subscriber {
  update(article) {
    throw new Error("update() 메서드는 반드시 구현되어야 합니다.");
  }
}

다음으로, 관찰 대상인 NewsPublisher(뉴스 발행사)를 Subject로 만듭니다.

javascript
// Subject: 뉴스 발행사
class NewsPublisher {
  constructor() {
    this.subscribers = []; // 옵저버(구독자) 목록
    this.latestArticle = null;
  }

  // 옵저버 등록
  register(subscriber) {
    this.subscribers.push(subscriber);
    console.log(`${subscriber.name}님이 구독을 시작했습니다.`);
  }

  // 옵저버 제거
  unregister(subscriber) {
    this.subscribers = this.subscribers.filter((sub) => sub !== subscriber);
    console.log(`${subscriber.name}님이 구독을 취소했습니다.`);
  }

  // 모든 옵저버에게 알림
  notify() {
    console.log("\n--- 새로운 기사 발행! 모든 구독자에게 알림 전송 ---");
    this.subscribers.forEach((subscriber) => {
      subscriber.update(this.latestArticle);
    });
  }

  // 새로운 기사 발행 (상태 변경)
  publishNewArticle(article) {
    this.latestArticle = article;
    this.notify();
  }
}

이제 구체적인 구독자, EmailSubscriberConcreteObserver로 구현합니다.

javascript
// ConcreteObserver: 이메일 구독자
class EmailSubscriber extends Subscriber {
  constructor(name) {
    super();
    this.name = name;
  }

  update(article) {
    console.log(
      `[${this.name}님에게 이메일 전송] 새로운 기사: "${article.title}"`
    );
  }
}

// 클라이언트 코드
const publisher = new NewsPublisher();

const sub1 = new EmailSubscriber("김재현");
const sub2 = new EmailSubscriber("이영희");

publisher.register(sub1); // 김재현님 구독 시작
publisher.register(sub2); // 이영희님 구독 시작

publisher.publishNewArticle({
  title: "AI, 인간의 일자리를 대체할까?",
  content: "...",
});
// 출력:
// --- 새로운 기사 발행! 모든 구독자에게 알림 전송 ---
// [김재현님에게 이메일 전송] 새로운 기사: "AI, 인간의 일자리를 대체할까?"
// [이영희님에게 이메일 전송] 새로운 기사: "AI, 인간의 일자리를 대체할까?"

publisher.unregister(sub2); // 이영희님 구독 취소

publisher.publishNewArticle({
  title: "2025년 기술 트렌드 전망",
  content: "...",
});
// 출력:
// --- 새로운 기사 발행! 모든 구독자에게 알림 전송 ---
// [김재현님에게 이메일 전송] 새로운 기사: "2025년 기술 트렌드 전망"

NewsPublisher는 어떤 종류의 구독자가 있는지, 구독자가 이메일을 받는지 앱 푸시를 받는지 전혀 알 필요가 없습니다. 그저 subscribers 목록에 있는 각 객체의 update() 메서드를 호출할 뿐입니다. 만약 '앱 푸시 알림' 기능이 필요하다면, AppPushSubscriber라는 새로운 옵저버 클래스를 만들어 등록하기만 하면 됩니다.

이처럼 옵저버 패턴은 SubjectObserver 간의 결합도를 낮춰, 시스템을 유연하고 확장 가능하게 만듭니다.

옵저버 패턴 중요 키워드

  • 일대다(one-to-many) 의존 관계를 정의합니다.
  • 느슨한 결합(Loose Coupling): Subject는 Observer의 구체적인 클래스를 몰라도 됩니다.
  • 상태 변경 시 자동으로 의존 객체에 전파(broadcast)됩니다.
  • 이벤트 기반 시스템, MVC(Model-View-Controller) 아키텍처 등에서 널리 사용됩니다.
  • 발행/구독(Publish/Subscribe) 모델이라고도 불립니다.

정처기 실기 기출 문제

기출
문제
한 객체의 상태가 변화하면 객체에 상속되어 있는 다른 객체들에게 변화된 상태를 전달해주는 패턴을 보기에서 고르시오
보기
답변
정답정답 보기
기출
문제
일대다 관계를 가지고, ​분산된 시스템 간에 이벤트 발행(Publish) 및 수신(Subscribe)이 필요할 때 이용하는 패턴을 보기에서 고르시오
보기
답변
정답정답 보기