Skip to content

토스트를 만들어보자

Geun Seong Lee edited this page Dec 14, 2023 · 1 revision

필요하다. 토스트 기능

웹 게임 진행상 알림을 띄워줘야 할 일이 많은데 alert 함수로 띄우기에는 UI 인터페이스도 별로고 프로그램의 흐름을 멈춰서 영 좋지 않다. 이미 구현된 모달 또한 마찬가지. 그래서 toast 라이브러리를 가져다가 쓸까 했다.

무겁다. React Toastify

https://www.npmjs.com/package/react-toastify

토스트 라이브러리 중 가장 유명한 라이브러리. 근데 알람 하나 띄우자고 400kb나 되는 라이브러리를 까는건 별로라고 생각했다. UI 또한 게임의 흐름과 유사하게 가져가고 싶어서 구현해보고자 했다.

React Toastify는 어떻게 쓰는걸까

  import React from 'react';

  import { ToastContainer, toast } from 'react-toastify';
  import 'react-toastify/dist/ReactToastify.css';

  function App(){
    const notify = () => toast("Wow so easy!");

    return (
      <div>
        <button onClick={notify}>Notify!</button>
        <ToastContainer />
      </div>
    );
  }

대표 토스트 컨테이너를 최상위에 올려두고 toast 함수를 호출해서 계속 부려먹는 방식이다. 비슷한 사용법으로 만들어 보자.

전역상태로 관리

interface ToastState {
  messageList: ReactNode[];
}

interface ToastAction {
  removeToast: (node: ReactNode) => void;
  toast: (node: ReactNode) => void;
}

interface ToastStore extends ToastState, ToastAction {}

const useToastStore = create<ToastStore>((set, get) => ({
  messageList: [],
  removeToast: node => set(state => ({ messageList: state.messageList.filter(msg => msg !== node) })),
  toast: node =>
    set(state => {
      const newMessageList = state.messageList.concat(node);
      setTimeout(() => {
        get().removeToast(node);
      }, 4000);
      return { messageList: newMessageList };
    }),
}));

export const toast = (node: ReactNode) => useToastStore.getState().toast(node);

토스트의 메세지 상태는 전역으로 관리한다. ReactNode타입을 기본으로 지정해 JSX, 문자열 모두 받을 수 있도록 허용한다.

다만 외부에 노출시키지는 않는다. 직접적인 상태 수정은 예상치 못한 결과를 초래할 수있기 때문. 또한 import 시에도 계속 store를 넣어줘야하기 떄문에 따로 함수를 빼서 동작하도록 수정한다.

toast 메소드를 통해 스스로 추가하고 후에 스스로 빠질 수 있도록 구현했다.

const Toast: React.FC = () => {
  const { messageList } = useToastStore();

  return (
    <div className="absolute top-2 right-2 z-50 flex flex-col gap-4">
      <AnimatePresence initial={false}>
        {messageList.map((node, idx) => {
          return <ToastItem key={node?.toString()}>{node}</ToastItem>;
        })}
      </AnimatePresence>
    </div>
  );
};

Toast 컴포넌트를 작성해 messageList를 출력하도록 한다. absolute를 최상위에 두어 항상 같은곳에 위치하게 한다.

만약 React 전용 라이브러리로 만든다면 createPortal을 통해 항상 최상위에 위치하도록 조절해주어야한다.

이제 외부에서 toast 함수를 통해 상태값을 추가해준다면 Toast 컴포넌트에서 출력 할 수 있다.

Clone this wiki locally