배운 코드 정리

발행자/구독자 패턴

fpzmfks 2024. 9. 11. 20:14

오늘 챌린지반 수업에서는 발행자/구독자 패턴을 배우고 이것을 활용하여 Potal로 Toast를 만드는 과제를 받았다. 과제를 실행하기에 앞서 수업에서 사용된 코드를 가져와 분석해보는 시간을 가졌다. 

 

아래가 바로 발행자/구독자 패턴이다. 쉽게 말해 구독을 통해 이곳에 명령어(topic)와 그와 매칭되는 함수들(listeners)

저장 (subscribe) 하고 그것들을 실행 (publish) 시킬 수 있도록 하는 것이다. 

// PUB/SUB 패턴
const EventBus = () => {
  // 명령어를 저장하는 곳
  const topics = new Map();

  const subscribe = (topic, listener) => {
    // 가장 먼저 명령어를 저장함
    // 값을 배열로 줌으로써 명렁어를 입력하면 여러 개의 함수를 실행할 수 있도록 함
    if (!topics.has(topic)) {
      topics.set(topic, []);
    }
    // 구독을 통해 명령어에 함수를 저장함
    topics.get(topic).push(listener);

    // 구독해제 클린업 함수
    return () => {
      // 명령어 topic에 있는 구독되어 있는 함수들
      const listeners = topics.get(topic);
      // 나의 구독listener 취소
      listeners.splice(listeners.indexOf(listener), 1);
    };
  };

  // 명령어를 입력하여 해당 명령어에 구독된 함수들을 실행함
  const publish = (topic, data) => {
    // 구독되어 있지 않으면 실행되지 않음
    if (!topics.has(topic)) return;
    // 구독되어 있는 경우
    // 명령어topic에 저장된 함수들listeners을 실행
    topics.get(topic).forEach((listener) => listener(data));
  };

  return { subscribe, publish };
};

// subscribe함수와 publish함수가 담긴 객체
const bus = EventBus();
export default bus;

 

이러한 구조의 장점은 구독과 실행이 분리되어 있을 수 있다는 것이다. 아래 함수를 보면 <Toast />가 구독한 함수인데, 이와 별도로 showToast를 불러와서 실행시키는 모습을 확인할 수 있다. Toast는 유효한 범위 내에 있다면 위치가 자유로워지는 것이다. 

function App() {
  return (
    <div className="App">
      <Toast />
      <h1>React Event Bus Toast Example</h1>
      <button onClick={() => showToast("This is a toast message!")}>
        Show Toast
      </button>
    </div>
  );
}

 

<Toast/>  구독subscribe

const Toast = () => {
  // 실행시킬 메세지들을 저장하는 상태값
  const [toasts, setToasts] = useState([]);

  useEffect(() => {
    // 구독하면서 이 함수를
    //"SHOW_TOAST" 토픽의                   topic
    // handleToastEvent 리스너로 저장함     listener
    const handleToastEvent = (toast) => {
      // 메세지 저장
      setToasts((prevToasts) => [...prevToasts, { id: Date.now(), ...toast }]);

      // 시간이 지나면 가장 처음 실행된 메세지부터 삭제
      setTimeout(() => {
        setToasts((prevToasts) => prevToasts.slice(1));
      }, 1500);
    };

    // subscribe 실행
    // unsubscribe은 subscribe이 결과값으로 반환하는 구독해제 함수
    const unsubscribe = bus.subscribe("SHOW_TOAST", handleToastEvent);

    // subscribe가 실행 후 반환하는 구독해제 함수
    // 를 실행하여 구독해제
    return () => unsubscribe();
  }, []);

  // 포탈을 열어서 이 함수가 실행되면
  // 2번째 인자의 위치에(이 경우 body)
  // 1번째 인자를 집어넣음
  return createPortal(
    <div className="toast-container">
      {toasts.map((toast, index) => (
        <div key={index} className="toast">
          {toast.message}
        </div>
      ))}
    </div>,
    document.body
  );
};

 

showToast  실행publish

// Toast 파일에서 저장한 SHOW_TOAST에 구독한 함수를 불러와서
// 매개변수message의 값을 넣어 실행시키도록 하는 함수
export const showToast = (message) => {
  // Toast에서 구독으로 저장한 명령어들을
  // 첫번째 인자 "SHOW_TOAST"키를 통해 불러와서
  // 두번째 인자 message를 넣어 실행시킴
  // SHOW_TOAST 명렁어에 구독된 함수가 여러개면 여러 개가 실행됨
  bus.publish("SHOW_TOAST", { message });
};

 

이렇게 Toast의 간단한 로직을 알게 되었는데... 이제부터 여기에 기능을 추가해야 한다. 힘내자...

'배운 코드 정리' 카테고리의 다른 글

min, splice, filter, apply  (0) 2024.07.11
indexOf, sort  (0) 2024.07.09
돌아온 정수 제곱근 판별  (0) 2024.07.02
자연수 뒤집기  (0) 2024.07.01
배열의 평균 값  (0) 2024.06.21