Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: useAuth 중복수정 #90

Merged
merged 5 commits into from
Jan 1, 2025
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
284 changes: 171 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,112 @@ 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,

/**
* 로그인 응답을 받아 인증 상태를 설정하는 함수
*
* @param response - 로그인 API 응답 데이터
* @returns Promise<void>
*
* @description
* - 토큰과 사용자 정보를 쿠키에 저장
* - 스토어의 상태를 업데이트
* - 인증 상태를 true로 설정
*/
setAuth: async (response: LoginResponse) => {
const {
token,
memberId,
memberName,
memberNickName,
annualIncome,
deposit,
} = response;

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,
Expand All @@ -167,12 +136,101 @@ export const useAuth = create<AuthStore>((set) => ({
memberNickName,
annualIncome,
deposit,
isAuthenticated: !!token,
isAuthenticated: true,
isInitialized: true,
});
},

/**
* 인증 상태를 초기화하고 로그아웃하는 함수
*
* @returns Promise<void>
*
* @description
* - 모든 인증 관련 쿠키를 삭제
* - 스토어의 상태를 초기화
* - 인증 상태를 false로 설정
*/
clearAuth: async () => {
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,
});
} catch (error) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

로그아웃 처리의 안정성 개선 필요

setAuth와 동일한 패턴의 문제가 있습니다. 쿠키 삭제 작업의 최적화와 에러 처리가 필요합니다.

다음과 같이 개선해주세요:

clearAuth: async () => {
+ try {
-   await deleteCookie("token");
-   await deleteCookie("memberId");
-   await deleteCookie("memberName");
-   await deleteCookie("memberNickName");
-   await deleteCookie("annualIncome");
-   await deleteCookie("deposit");
+   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) {
+   console.error('로그아웃 처리 중 오류 발생:', error);
+   throw error;
+ }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
clearAuth: async () => {
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,
});
clearAuth: async () => {
try {
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) {
console.error('로그아웃 처리 중 오류 발생:', error);
throw error;
}

set({ isInitialized: true });
throw error;
}
},
}));
},

/**
* 페이지 로드시 쿠키에서 인증 상태를 복원하는 함수
*
* @returns Promise<void>
*
* @description
* - 중복 초기화 방지를 위한 Promise 캐싱 포함
* - 쿠키에서 인증 정보를 읽어와 스토어에 설정
* - 초기화 상태를 추적하기 위한 플래그 관리
* - 에러 발생시 초기화 상태 정리
*/
initAuth: async () => {
if (get().isInitialized) {
return;
}

if (initPromise) {
return initPromise;
}

initPromise = (async () => {
try {
set({ isInitializing: true });

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");

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;
},
};
});
Loading