diff --git a/src/app/error.tsx b/src/app/error.tsx
new file mode 100644
index 0000000..ddd9385
--- /dev/null
+++ b/src/app/error.tsx
@@ -0,0 +1,22 @@
+"use client";
+
+import Image from "next/image";
+
+export default function NotFound() {
+ return (
+
+
+
+
+
+
GrowFolio
+
서비스 업데이트 준비중입니다
+
+ 빠른 시일 내에 찾아뵙겠습니다
+
+
+
+
+
+ );
+}
diff --git a/src/hooks/use-auth.ts b/src/hooks/use-auth.ts
index 496ed83..03d4ba6 100644
--- a/src/hooks/use-auth.ts
+++ b/src/hooks/use-auth.ts
@@ -1,9 +1,7 @@
-/* eslint-disable*/
-
+/*eslint-disable*/
"use client";
import { create } from "zustand";
-
import { deleteCookie, getCookie, setCookie } from "@/utils/next-cookies";
interface LoginResponse {
@@ -24,51 +22,59 @@ interface AuthStore {
deposit: number | null;
isAuthenticated: boolean;
isInitialized: boolean;
+ isInitializing: boolean;
setAuth: (response: LoginResponse) => Promise;
clearAuth: () => Promise;
initAuth: () => Promise;
}
/**
- * 인증 토큰을 관리하는 Zustand 스토어 훅
+ * 인증 상태를 관리하는 Zustand 스토어 훅
*
- * @example
- * // 1. 컴포넌트에서 토큰 관리
- * import { useAuth } from '@/hooks/useAuth';
+ * @description
+ * - 로그인/로그아웃 상태 관리
+ * - 토큰 및 사용자 정보 관리
+ * - 쿠키 기반의 인증 상태 유지
+ * - 중복 초기화 방지 로직 포함
*
- * function LoginComponent() {
+ * @example
+ * // 1. 로그인 처리
+ * const LoginComponent = () => {
* const { setAuth } = useAuth();
*
* const handleLogin = async () => {
- * const response = await fetch('/api/login');
- * const data = await response.json();
- * await setAuth(data); // 로그인 후 인증 정보 저장
+ * const response = await loginAPI();
+ * await setAuth(response);
* };
- * }
+ * };
*
* @example
- * // 2. API 요청시 토큰 사용
- * function ProtectedComponent() {
- * const { token } = useAuth();
+ * // 2. 인증이 필요한 API 요청
+ * const ProtectedComponent = () => {
+ * const { token, isInitialized } = useAuth();
*
- * const fetchData = async () => {
- * const response = await fetch('/api/protected', {
- * headers: {
- * 'Authorization': `Bearer ${token}`
- * }
- * });
- * };
- * }
+ * useEffect(() => {
+ * if (!isInitialized) return;
+ *
+ * const fetchData = async () => {
+ * const response = await fetch('/api/protected', {
+ * headers: { Authorization: `Bearer ${token}` }
+ * });
+ * };
+ *
+ * fetchData();
+ * }, [isInitialized, token]);
+ * };
*
* @example
- * // 3. 컴포넌트 마운트시 인증 상태 초기화
- * function App() {
+ * // 3. 앱 초기화시 인증 상태 복원
+ * const App = () => {
* const { initAuth } = useAuth();
*
* useEffect(() => {
- * initAuth(); // 페이지 로드시 쿠키에서 인증 정보 복원
+ * initAuth();
* }, []);
- * }
+ * };
*
* @example
* // 4. 로그아웃시 인증 정보 제거
@@ -76,89 +82,44 @@ interface AuthStore {
* const { clearAuth } = useAuth();
*
* const handleLogout = async () => {
- * await clearAuth(); // 로그아웃시 인증 정보 삭제
+ * await clearAuth();
* };
* }
*/
-export const useAuth = create((set) => ({
- token: null,
- memberId: null,
- memberName: null,
- memberNickName: null,
- annualIncome: null,
- deposit: null,
- isAuthenticated: false,
- isInitialized: false,
-
- setAuth: async (response: LoginResponse) => {
- const {
- token,
- memberId,
- memberName,
- memberNickName,
- annualIncome,
- deposit,
- } = response;
-
- // Store all relevant data in cookies
- await setCookie("token", token);
- await setCookie("memberId", memberId.toString());
- await setCookie("memberName", memberName);
- await setCookie("memberNickName", memberNickName);
- await setCookie("annualIncome", annualIncome.toString());
- await setCookie("deposit", deposit.toString());
-
- set({
- token,
- memberId,
- memberName,
- memberNickName,
- annualIncome,
- deposit,
- isAuthenticated: true,
- isInitialized: true,
- });
- },
-
- clearAuth: async () => {
- // Clear all cookies
- await deleteCookie("token");
- await deleteCookie("memberId");
- await deleteCookie("memberName");
- await deleteCookie("memberNickName");
- await deleteCookie("annualIncome");
- await deleteCookie("deposit");
-
- set({
- token: null,
- memberId: null,
- memberName: null,
- memberNickName: null,
- annualIncome: null,
- deposit: null,
- isAuthenticated: false,
- isInitialized: true,
- });
- },
-
- initAuth: async () => {
- try {
- set({ isInitialized: false });
-
- const token = await getCookie("token");
- const memberIdStr = await getCookie("memberId");
- const memberName = await getCookie("memberName");
- const memberNickName = await getCookie("memberNickName");
- const annualIncomeStr = await getCookie("annualIncome");
- const depositStr = await getCookie("deposit");
-
- // Convert string values back to numbers
- const memberId = memberIdStr ? parseInt(memberIdStr, 10) : null;
- const annualIncome = annualIncomeStr
- ? parseInt(annualIncomeStr, 10)
- : null;
- const deposit = depositStr ? parseInt(depositStr, 10) : null;
+export const useAuth = create((set, get) => {
+ let initPromise: Promise | null = null;
+
+ return {
+ token: null,
+ memberId: null,
+ memberName: null,
+ memberNickName: null,
+ annualIncome: null,
+ deposit: null,
+ isAuthenticated: false,
+ isInitialized: false,
+ isInitializing: false,
+
+ setAuth: async (response: LoginResponse) => {
+ const {
+ token,
+ memberId,
+ memberName,
+ memberNickName,
+ annualIncome,
+ deposit,
+ } = response;
+
+ // 모든 쿠키 설정을 병렬로 처리
+ await Promise.all([
+ setCookie("token", token),
+ setCookie("memberId", memberId.toString()),
+ setCookie("memberName", memberName),
+ setCookie("memberNickName", memberNickName),
+ setCookie("annualIncome", annualIncome.toString()),
+ setCookie("deposit", deposit.toString()),
+ ]);
set({
token,
@@ -167,12 +128,93 @@ export const useAuth = create((set) => ({
memberNickName,
annualIncome,
deposit,
- isAuthenticated: !!token,
+ isAuthenticated: true,
+ isInitialized: true,
+ });
+ },
+
+ clearAuth: async () => {
+ // 모든 쿠키 삭제를 병렬로 처리
+ await Promise.all([
+ deleteCookie("token"),
+ deleteCookie("memberId"),
+ deleteCookie("memberName"),
+ deleteCookie("memberNickName"),
+ deleteCookie("annualIncome"),
+ deleteCookie("deposit"),
+ ]);
+
+ set({
+ token: null,
+ memberId: null,
+ memberName: null,
+ memberNickName: null,
+ annualIncome: null,
+ deposit: null,
+ isAuthenticated: false,
isInitialized: true,
});
- } catch (error) {
- set({ isInitialized: true });
- throw error;
- }
- },
-}));
+ },
+
+ initAuth: async () => {
+ if (get().isInitialized) {
+ return;
+ }
+
+ if (initPromise) {
+ return initPromise;
+ }
+
+ initPromise = (async () => {
+ try {
+ set({ isInitializing: true });
+
+ // 모든 쿠키 조회를 병렬로 처리
+ const [
+ token,
+ memberIdStr,
+ memberName,
+ memberNickName,
+ annualIncomeStr,
+ depositStr,
+ ] = await Promise.all([
+ getCookie("token"),
+ getCookie("memberId"),
+ getCookie("memberName"),
+ getCookie("memberNickName"),
+ getCookie("annualIncome"),
+ getCookie("deposit"),
+ ]);
+
+ const memberId = memberIdStr ? parseInt(memberIdStr, 10) : null;
+ const annualIncome = annualIncomeStr
+ ? parseInt(annualIncomeStr, 10)
+ : null;
+ const deposit = depositStr ? parseInt(depositStr, 10) : null;
+
+ set({
+ token,
+ memberId,
+ memberName,
+ memberNickName,
+ annualIncome,
+ deposit,
+ isAuthenticated: !!token,
+ isInitialized: true,
+ isInitializing: false,
+ });
+ } catch (error) {
+ set({
+ isInitialized: true,
+ isInitializing: false,
+ });
+ throw error;
+ } finally {
+ initPromise = null;
+ }
+ })();
+
+ return initPromise;
+ },
+ };
+});