예전에 이삼다를 할때 팝업을 열심히 만들었던 기억이 있다. 나는 나름 3중 팝업 만든다고 열심히 손수 만들었는데,
나중에 과장님께서 만든 팝업 컴포넌트를 보고 나니 내 코드는 확장성이나 재활용성을 전혀 고려하지 않은 코드였다는걸 알게 됐다.
팝업이나 토스트 메세지같이 모든 화면에서 사용할 수 있는 컴포넌트는 당연히 전역으로 관리하는게 좋다.
루트 레이아웃에 컴포넌트를 넣어주고
popupStore 와 toastStore 를 각각 만들어준다.
코드를 짤때 주요 포인트들은 다음과 같았다.
1. 새로고침이나 브라우저를 다시 열었을 때 유지 되어야하는 session과 달리
팝업이나 메세지등은 당연히 초기화 되는게 맞기 때문에 그냥 create 해줬다.
2. 인터페이스 가독성이 은근 안좋아서.
팝업 내부에 들어가는 컨텐츠 타입 따로 && extends 팝업 동작 컨트롤하는 애들 따로 만들어줬다
이러면 showPopup 에 들어가는 인자값 타입도 중복을 줄일수 있어서 좋다.
토스트 메세지도 마찬가지로 분리.
// Popup
interface PopupProps {
type?: "info" | "confirm";
title: string;
message: string;
onConfirm?: () => void;
onClose?: () => void;
}
interface PopupState extends PopupProps {
isOpen: boolean;
showPopup: (params: PopupProps) => void;
closePopup: () => void;
}
// Toast
type ToastType = 'success' | 'error' | 'info';
export interface Toast {
id: string;
message: string;
type: ToastType;
}
interface ToastStore {
toasts: Toast[];
addToast: (message: string, type?: ToastType) => void;
removeToast: (id: string) => void;
}
3. toast 상태값을 배열 형태로 만들어주고 클릭을 할때마다 배열에 토스트 객체가 추가되게 만들어줬다.
이 배열 상태값을 가지고 tsx 를 만들어주면 여러개의 토스트 메세지가 동시에 존재할 수 있다.
// Toast 컴포넌트
<div className="fixed top-40 left-1/2 -translate-x-1/2 z-[9999] flex flex-col gap-2">
{toasts.map((toast) => (
<div
key={toast.id}
className={clsx(
'min-w-[200px] text-center px-4 py-2 rounded shadow-md text-white transition-all duration-300 ease-in-out text-sm',
toast.type === 'info' && 'bg-green-500/75',
toast.type === 'error' && 'bg-red-500/75',
toast.type === 'success' && 'bg-blue-500/75'
)}
onClick={() => removeToast(toast.id)}
>
{toast.message}
</div>
))}
</div>
반응형
'프로젝트 > RESUMER' 카테고리의 다른 글
| [resumer] prisma 외래키 설정 (1) | 2025.05.29 |
|---|---|
| [resumer] zustand persist 사용, 커링 currying (0) | 2025.05.20 |