145 lines
4.5 KiB
TypeScript
145 lines
4.5 KiB
TypeScript
import { create } from 'zustand';
|
|
import { persist } from 'zustand/middleware';
|
|
import { authApi } from '@/lib/api/auth.api';
|
|
import { UserDto, LoginDto, SignupDto } from '@/types/auth.types';
|
|
|
|
/**
|
|
* 인증 Store 상태 - 인증 관련 값 안올때 확인
|
|
* 상태(State)를 중앙에서 관리하는 저장소
|
|
* login.page 프론트에서 요청을 받으면 백엔드로 요청을 전송하고 받은 데이터를 저장하고 관리
|
|
*/
|
|
interface AuthState {
|
|
// 상태
|
|
user: UserDto | null; // 현재 로그인한 사용자 정보
|
|
accessToken: string | null; // JWT 액세스 토큰 (24시간 유효)
|
|
isAuthenticated: boolean; // 로그인 여부
|
|
|
|
|
|
// 각 페이지의 호출된 함수 실행
|
|
login: (dto: LoginDto) => Promise<void>; // 로그인
|
|
signup: (dto: SignupDto) => Promise<void>; // 회원가입
|
|
logout: () => Promise<void>; // 로그아웃
|
|
loadProfile: () => Promise<void>; // 프로필 불러오기
|
|
clearAuth: () => void; // 인증 정보 초기화 (클라이언트에서)
|
|
}
|
|
|
|
/**
|
|
* 인증 Store
|
|
*
|
|
* @description
|
|
* JWT 토큰 기반 인증 상태 관리
|
|
* - localStorage에 토큰 자동 저장 (persist)
|
|
* - 로그인/로그아웃/회원가입/토큰 갱신 기능
|
|
*/
|
|
|
|
// useAuthStore는 Zustand의 create 함수로 만든 커스텀 스토어 훅
|
|
// Zustand import 후 그 반환값을 useAuthStore에 할당
|
|
// import { useAuthStore } from "@/store/auth-store"; // 스토어에서 토큰/초기화 사용
|
|
export const useAuthStore = create<AuthState>()(
|
|
persist( // persist 미들웨어로 전체 상태를 자동 저장,복원/ partialize(state)쓰게되면 저장할 필드만 선별하는거라함
|
|
// persist미들웨어를 붙이면 set으로 상태를 바꿀때 선택된 필드가 자동으로 localStorage에 저장
|
|
(set, get) => ({
|
|
// 초기 상태
|
|
user: null,
|
|
accessToken: null,
|
|
isAuthenticated: false,
|
|
|
|
/**
|
|
* @description 로그인 페이지에서 백엔드 API로 요청 전송 authApi.login(dto)
|
|
* 백엔드에서 토큰을 반환하면 프론트에서 로컬 스토리지에 저장
|
|
*/
|
|
login: async (dto: LoginDto) => {
|
|
try {
|
|
const response = await authApi.login(dto);
|
|
|
|
// 받아온 결과를 상태에 저장
|
|
// Zustand 상태 업데이트 (persist 미들웨어가 자동으로 localStorage에 저장)
|
|
set({
|
|
user: response.user,
|
|
accessToken: response.accessToken,
|
|
isAuthenticated: true,
|
|
});
|
|
} catch (error) {
|
|
console.error('로그인 실패:', error);
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 회원가입
|
|
*/
|
|
signup: async (dto: SignupDto) => {
|
|
try {
|
|
const response = await authApi.signup(dto);
|
|
|
|
// Zustand 상태 업데이트 (persist 미들웨어가 자동으로 localStorage에 저장)
|
|
set({
|
|
user: response.user,
|
|
accessToken: response.accessToken,
|
|
isAuthenticated: true,
|
|
});
|
|
} catch (error) {
|
|
console.error('회원가입 실패:', error);
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 로그아웃
|
|
*/
|
|
logout: async () => {
|
|
try {
|
|
await authApi.logout();
|
|
} catch (error) {
|
|
console.error('로그아웃 요청 실패:', error);
|
|
} finally {
|
|
// Zustand 상태 초기화 (persist 미들웨어가 자동으로 localStorage에서 삭제)
|
|
set({
|
|
user: null,
|
|
accessToken: null,
|
|
isAuthenticated: false,
|
|
});
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 프로필 불러오기
|
|
*/
|
|
loadProfile: async () => {
|
|
try {
|
|
const profile = await authApi.getProfile();
|
|
|
|
set({
|
|
user: profile,
|
|
isAuthenticated: true,
|
|
});
|
|
} catch (error) {
|
|
console.error('프로필 불러오기 실패:', error);
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 인증 정보 초기화 (클라이언트 측)
|
|
*/
|
|
clearAuth: () => {
|
|
// Zustand 상태 초기화 (persist 미들웨어가 자동으로 localStorage에서 삭제)
|
|
set({
|
|
user: null,
|
|
accessToken: null,
|
|
isAuthenticated: false,
|
|
});
|
|
},
|
|
}),
|
|
{
|
|
name: 'auth-storage', // localStorage 키 이름
|
|
partialize: (state) => ({
|
|
// localStorage에 저장할 필드만 선택
|
|
user: state.user,
|
|
accessToken: state.accessToken,
|
|
isAuthenticated: state.isAuthenticated,
|
|
}),
|
|
}
|
|
)
|
|
);
|