Skip to content

Commit

Permalink
feat: useAuth 중복수정 (#90)
Browse files Browse the repository at this point in the history
  • Loading branch information
Han-wo authored Jan 1, 2025
1 parent 004a54c commit de9b924
Show file tree
Hide file tree
Showing 2 changed files with 177 additions and 113 deletions.
22 changes: 22 additions & 0 deletions src/app/error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
"use client";

import Image from "next/image";

export default function NotFound() {
return (
<div className="lg:max-w-2000 flex h-screen w-full min-w-400 shrink-0 flex-col items-center justify-center bg-gradient-to-b from-green-100 via-green-50 to-white px-4 py-8 md:px-12 lg:px-16">
<div className="bg-gray w-full min-w-400 shrink-0 rounded-20">
<div className="flex w-full flex-col items-center justify-center p-4">
<div className="mb-15 flex flex-col items-center justify-center gap-13">
<Image src="/icons/Logo.svg" alt="Logo" width={44} height={44} />
<span className="text-40-600">GrowFolio</span>
<h1 className="mt-20 text-24-700">서비스 업데이트 준비중입니다</h1>
<p className="text-18-600 text-gray-600">
빠른 시일 내에 찾아뵙겠습니다
</p>
</div>
</div>
</div>
</div>
);
}
268 changes: 155 additions & 113 deletions src/hooks/use-auth.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
/* eslint-disable*/

/*eslint-disable*/
"use client";

import { create } from "zustand";

import { deleteCookie, getCookie, setCookie } from "@/utils/next-cookies";

interface LoginResponse {
Expand All @@ -24,141 +22,104 @@ interface AuthStore {
deposit: number | null;
isAuthenticated: boolean;
isInitialized: boolean;
isInitializing: boolean;
setAuth: (response: LoginResponse) => Promise<void>;
clearAuth: () => Promise<void>;
initAuth: () => Promise<void>;
}

/**
* 인증 토큰을 관리하는 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. 로그아웃시 인증 정보 제거
* function LogoutButton() {
* const { clearAuth } = useAuth();
*
* const handleLogout = async () => {
* await clearAuth(); // 로그아웃시 인증 정보 삭제
* await clearAuth();
* };
* }
*/

export const useAuth = create<AuthStore>((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<AuthStore>((set, get) => {
let initPromise: Promise<void> | 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,
Expand All @@ -167,12 +128,93 @@ export const useAuth = create<AuthStore>((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;
},
};
});

0 comments on commit de9b924

Please sign in to comment.