diff --git a/src/App.tsx b/src/App.tsx
index 3913385..930189c 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,6 +1,8 @@
import { RouterProvider } from "react-router-dom";
+import Modal from "./components/Modal/Modal";
import { router } from "./Router";
+
import ToastContainer from "@/components/Toast/ToastContainer";
function App() {
@@ -8,6 +10,7 @@ function App() {
<>
+
>
);
}
diff --git a/src/components/Modal/AlertModalLayout.tsx b/src/components/Modal/AlertModalLayout.tsx
new file mode 100644
index 0000000..f4a3d11
--- /dev/null
+++ b/src/components/Modal/AlertModalLayout.tsx
@@ -0,0 +1,70 @@
+import Button from "../Button";
+
+import { Check, Notice } from "@/assets/icon";
+import { useModalStore } from "@/store/useModalStore";
+
+interface ModalButton {
+ label: string;
+ style: "primary" | "white";
+ onClick: () => void;
+}
+
+interface Props {
+ type: "alert" | "message";
+ iconType?: "check" | "warning" | "none";
+ message: string;
+ button: ModalButton;
+}
+
+const ICONS = {
+ check: Check,
+ warning: Notice,
+};
+
+export default function AlertModalLayout({
+ type,
+ iconType = "none",
+ message,
+ button,
+}: Props) {
+ const Icon = iconType !== "none" ? ICONS[iconType] : null;
+ const { closeModal } = useModalStore();
+ return (
+
e.stopPropagation()}
+ >
+ {Icon &&
}{" "}
+
{message}
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/Modal/ConfirmModalLayout.tsx b/src/components/Modal/ConfirmModalLayout.tsx
new file mode 100644
index 0000000..cbe9ae6
--- /dev/null
+++ b/src/components/Modal/ConfirmModalLayout.tsx
@@ -0,0 +1,49 @@
+import { Check, Notice } from "@/assets/icon";
+
+const ICONS = {
+ check: Check,
+ warning: Notice,
+};
+
+interface ConfirmModalLayoutProps {
+ iconType?: "check" | "warning" | "none";
+ message: string;
+ onClose: () => void;
+ onConfirm: () => void;
+ confirmText?: string;
+ cancelText?: string;
+}
+
+export default function ConfirmModalLayout({
+ iconType = "none",
+ message,
+ onClose,
+ onConfirm,
+ confirmText = "예",
+ cancelText = "아니오",
+}: ConfirmModalLayoutProps) {
+ const Icon = iconType !== "none" ? ICONS[iconType] : null;
+
+ return (
+
+ {Icon &&
}
+
+ {message}
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/Modal/Modal.tsx b/src/components/Modal/Modal.tsx
new file mode 100644
index 0000000..07b3c68
--- /dev/null
+++ b/src/components/Modal/Modal.tsx
@@ -0,0 +1,45 @@
+import AlertModalLayout from "./AlertModalLayout";
+import ConfirmModalLayout from "./ConfirmModalLayout";
+
+import { useModalStore } from "@/store/useModalStore";
+
+export default function Modal() {
+ const { isOpen, options, closeModal } = useModalStore();
+
+ if (!isOpen || !options) return null;
+
+ const handleClose = () => {
+ options.onClose?.();
+ closeModal();
+ };
+
+ return (
+
+ {options.type === "confirm" && (
+
{
+ options.onConfirm?.();
+ closeModal();
+ }}
+ confirmText={options.confirmText ?? "예"}
+ cancelText={options.cancelText ?? "아니오"}
+ />
+ )}
+ {(options.type === "alert" || options.type === "message") && (
+
+ )}
+
+ );
+}
diff --git a/src/components/index.ts b/src/components/index.ts
deleted file mode 100644
index e69de29..0000000
diff --git a/src/store/useModalStore.ts b/src/store/useModalStore.ts
new file mode 100644
index 0000000..b0e6d04
--- /dev/null
+++ b/src/store/useModalStore.ts
@@ -0,0 +1,34 @@
+import { create } from "zustand";
+
+export type ModalType = "alert" | "confirm" | "message";
+
+export interface ModalButton {
+ label: string;
+ style: "primary" | "white";
+ onClick: () => void;
+}
+
+export interface ModalOptions {
+ type: "alert" | "confirm" | "message";
+ message: string;
+ iconType?: "check" | "warning" | "none";
+ buttons?: ModalButton[];
+ onClose?: () => void;
+ onConfirm?: () => void;
+ confirmText?: string;
+ cancelText?: string;
+}
+
+interface ModalState {
+ isOpen: boolean;
+ options: ModalOptions | null;
+ openModal: (options: ModalOptions) => void;
+ closeModal: () => void;
+}
+
+export const useModalStore = create((set) => ({
+ isOpen: false,
+ options: null,
+ openModal: (options) => set({ isOpen: true, options }),
+ closeModal: () => set({ isOpen: false, options: null }),
+}));
diff --git a/src/types/svg.d.ts b/src/types/svg.d.ts
new file mode 100644
index 0000000..ed25de5
--- /dev/null
+++ b/src/types/svg.d.ts
@@ -0,0 +1,10 @@
+declare module "*.svg?react" {
+ import * as React from "react";
+ const ReactComponent: React.FC>;
+ export default ReactComponent;
+}
+
+declare module "*.svg" {
+ const content: string;
+ export default content;
+}
diff --git a/tsconfig.json b/tsconfig.json
index 1ffef60..25ebec3 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -3,5 +3,9 @@
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
- ]
+ ],
+ "compilerOptions": {
+ "typeRoots": ["./src/types", "./node_modules/@types"]
+ },
+ "includes": ["src", "src/types"]
}