[TIL] Pub-Sub 패턴

기성·2024년 9월 10일
1

TIL

목록 보기
54/81

Publish-Subscribe 패턴

Pub-Sub는 퍼블리셔라고 하는 메시지 발신자가 특정 수신자(구독자)에게 직접 메시지를 보내도록 프로그래밍하지 않고, 대신 어떤 구독자가 있는지 알 수 없는 상태에서 게시된 메시지를 클래스로 분류하는 메시징 패턴입니다. 마찬가지로 구독자는 하나 이상의 클래스에 관심을 표명하고 관심 있는 메시지만 수신하며, 어떤 게시자가 있는지 알지 못합니다. 출처: Wikipedia

간단하게 말하면 발행자와 구독자는 서로 인식하지 않는다는 뜻이다. 발행자와 구독자는 서로 인식하지 않는데 어떻게 통신을 하고 있을까?

위 그림처럼 중간에 브로커 하나를 두고 중간 다리 역할을 해서 메세지들을 필터링해준다.

Observer 패턴과 다른점

  • Topic/Event channel의 존재
  • 옵저버 패턴은 한 subject와 옵저버끼리 서로를 알고 있지만 발행구독 패턴은 서로를 모른다.
  • 옵저버 패턴은 주로 동기적으로 동작하지만 발행구독 패턴은 비동기적으로 동작한다.

발행 구독 패턴 구현

Publisher

//ModalPublisher.jsx
import { event } from "./event";
/** 이벤트 발행하는 곳*/
export const ModalPublisher = {
  /** message라는 이름으로 구독이 가능함, content를 인자로 가지는 showModal이라는 이벤트를 등록함 */
  message: (content) => {
    event.emit("showModal", content);
  },
  /** 메세지를 지우는 이벤트를 등록  */
  clearAllMessage: () => {
    event.emit("clearAllMessage");
  },
};
//App.jsx
import "./App.css";
import Modal from "./components/Modal";
import { ModalPublisher } from "./components/ModalPublisher";

function App() {
  return (
    <div className="App">
      <Modal showModal={true} />
      <h1
        onClick={() => {
          ModalPublisher.message("요건 앱에서 온 컨텐츠"); // publisher에서 발행한 메세지 사용
        }}
      >
        구독 발행 테스트!
      </h1>
    </div>
  );
}

export default App;

발행된 message()를 사용하는 App.jsx이다.

Modal 퍼블리셔에서 message와 clearAllMessage라는 이름의 함수를 만들고 event라는 브로커에서 동작을 꺼내오고 message와 clearAllMessage 뿌려준다.

Subscribe

import { useEffect, useState } from "react";
import { event } from "./event";

const Modal = () => {
  const [content, setContent] = useState("no content");
  const [showModal, setShowModal] = useState(false);

  const setMessage = (message) => {
    setContent(message);
    setShowModal(true);
  };
  const clearMessage = () => {
    setContent("");
    setShowModal(false);
  };

  useEffect(() => {
    /** 첫 렌더링 시에 showModal, clearAllMessage 이벤트를 등록한다.*/
    event.on("showModal", setMessage).on("clearAllMessage", clearMessage);
  }, []);
  if (showModal) {
    return (
      <div>
        <h2>{content}</h2>
        <button onClick={clearMessage}>모달 닫기</button>
      </div>
    );
  }
  return null;
};

export default Modal;

event.on을 통해 브로커에게 해당 이벤트들을 등록 시킨다.

Topic/Event channel

//event.js
// 중간 브로커 역할
export const event = {
  list: new Map(),
  on(eventType, eventAction) {
    // list에 이벤트를 등록하는 메서드 (구독)
    this.list.has(eventType) || this.list.set(eventType, []); //eventType은 등록할 함수 이름, eventType에 해당하는 이벤트가 존재하는지 확인 후 없다면 빈 배열로 초기화
    if (this.list.get(eventType)) this.list.get(eventType).push(eventAction); //만약 해당 이벤트가 있다면 이벤트 리스트에 eventAction을 추가한다.
    return this;
  },

  emit(eventType, ...args) {
    // on으로 등록한 이벤트를 꺼내서 실행시키는 메서드 (발행)
    this.list.get(eventType) && // list에 eventType이 있다면 해당 eventType에 있는 eventAction들을
      this.list.get(eventType).forEach((cb) => {
        // 다 꺼내서 실행한다.
        cb(...args); //...args는 cb 이벤트에 들어갈 인자
      });
  },
};

여기서 보면 on은 구독, emit은 발행 이라고 생각하면 되겠다.

구현

잘 동작하는 것을 볼 수 있다.

profile
프론트가 하고싶어요

0개의 댓글