zustand를 활용한 커스텀 Alert 로직 구현하기

허지예·2024년 8월 14일
0

개인 프로젝트 기록

목록 보기
15/17

전역 상태 라이브러리인 zustand를 활용해서 커스텀 Alert을 클라이언트 사이드 어디서든 띄울 수 있도록 구현했다.

1. zustand를 사용해 useAlertStore를 먼저 작성

  • store에 alert 정보를 queue에 담아서 관리하도록 했다. (한번에 여러 alert이 뜨게 될 수도 있으니깐!)
  • 그리고 추가하고(push), 삭제하는(pop) 로직을 넣어줬다.
  • 특정해서 삭제할 수 있게 하기 위해서 각 alert 정보에는 id 값을 넣어줬다.
import { create } from 'zustand';

type AlertType = 'error' | 'success' | 'info';

export interface AlertContent {
  type: AlertType;
  message: string;
  persistent?: boolean;
}

export interface AlertData extends AlertContent {
  id: number;
}

export type AlertStore = {
  nextId: number;
  queue: AlertData[];
  push: (content: AlertContent) => void;
  pop: (id: number) => void;
};

const useAlertStore = create<AlertStore>()((set) => ({
  nextId: 0,
  queue: [],
  push: (content) => {
    set((state) => ({
      nextId: state.nextId + 1,
      queue: [...state.queue, { id: state.nextId, ...content }],
    }));
  },
  pop: (id) => {
    set((state) => ({
      queue: state.queue.filter((alert) => alert.id !== id),
    }));
  },
}));

export default useAlertStore;

2. Alert 컴포넌트 작성

  • daisyuiheroicons을 사용했다.
  • persistent 설정이 있으면, x 버튼을 눌러야 사라지고,
    설정이 없으면 3초 뒤에 자동으로 사라진다.
function Alert() {
  const { queue, pop } = useAlertStore((state) => ({
    queue: state.queue,
    pop: state.pop,
  }));

  return (
    <div className='z-999 toast toast-end items-end'>
      {queue.map(({ id, type, message, persistent }) => (
        <AlertItem key={id} type={type} message={message} persistent={persistent} onClose={() => pop(id)} />
      ))}
    </div>
  );
}

interface AlertItemProps {
  type: 'error' | 'success' | 'info';
  message: string;
  persistent?: boolean;
  onClose: () => void;
}

function AlertItem({ type, message, persistent, onClose }: AlertItemProps) {
  useEffect(() => {
    if (!persistent) {
      setTimeout(() => {
        onClose();
      }, 3000);
    }
  });

  return (
    <div
      className={cn('alert w-fit min-w-400 text-14', {
        'alert-error': type === 'error',
        'alert-success': type === 'success',
        'alert-info': type === 'info',
      })}
    >
      {type === 'error' && <XCircleIcon className='h-24 w-24' />}
      {type === 'success' && <CheckIcon className='h-24 w-24' />}
      {type === 'info' && <InformationCircleIcon className='h-24 w-24' />}
      <span className='ml-2'>{message}</span>
      {persistent && <XMarkIcon className='h-18 w-18 cursor-pointer' onClick={onClose} />}
    </div>
  );
}

export default Alert;

3. app/layout.tsx 수정

  • 루트에 Alert 컴포넌트를 넣어준다.
export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang='ko'>
      <body>
        {children}
        <Alert />
      </body>
    </html>
  );
}

4. 커스텀 alert 로직을 캡슐화하는 함수 하나 구현

function alert({ type, message, persistent }: AlertContent) {
  useAlertStore.getState().push({ type, message, persistent });
}

export default alert;

사용 예시

요렇게 에러 처리를 할 수도 있다.

function someFunction() {
	try {
      ...something
    } catch (error) {
      alert({ type: 'error', message: error.message });
    }
}
profile
대학생에서 취준생으로 진화했다가 지금은 풀스택 개발자로 2차 진화함

0개의 댓글

관련 채용 정보