diff --git a/src/app/features/auth/api/authApi.ts b/src/app/features/auth/api/authApi.ts new file mode 100644 index 0000000..4902afa --- /dev/null +++ b/src/app/features/auth/api/authApi.ts @@ -0,0 +1,14 @@ +import api from '@/app/shared/lib/axios' +import { AUTH_ENDPOINT } from './authEndpoint' +import { LoginRequest, SignupRequest, LoginResponse } from '../types/auth.type' +import { User as SignupResponse } from '@/app/shared/types/user.type' + +export const login = async (data: LoginRequest): Promise => { + const response = await api.post(AUTH_ENDPOINT.LOGIN, data) + return response.data +} + +export const signup = async (data: SignupRequest): Promise => { + const response = await api.post(AUTH_ENDPOINT.SIGNUP, data) + return response.data +} diff --git a/src/app/features/auth/api/authEndpoint.ts b/src/app/features/auth/api/authEndpoint.ts new file mode 100644 index 0000000..ed55a9c --- /dev/null +++ b/src/app/features/auth/api/authEndpoint.ts @@ -0,0 +1,4 @@ +export const AUTH_ENDPOINT = { + LOGIN: '/15-2/auth/login', + SIGNUP: '/15-2/users', +} diff --git a/src/app/features/auth/hooks/useAuth.ts b/src/app/features/auth/hooks/useAuth.ts new file mode 100644 index 0000000..6b577e0 --- /dev/null +++ b/src/app/features/auth/hooks/useAuth.ts @@ -0,0 +1,33 @@ +import { useAuthStore } from '../store/useAuthStore' +import { login as loginApi, signup as signupApi } from '../api/authApi' +import { LoginRequest, SignupRequest } from '../types/auth.type' + +export function useAuth() { + const { setAccessToken, setUser, clearAuthState } = useAuthStore() + + async function login(data: LoginRequest) { + const response = await loginApi(data) + const { accessToken, user } = response + + if (!accessToken || !user) { + throw new Error('유효하지 않은 응답입니다.') + } + + setAccessToken(accessToken) + setUser(user) + } + + async function signup(data: SignupRequest) { + await signupApi(data) + } + + function logout() { + clearAuthState() + } + + return { + login, + signup, + logout, + } +} diff --git a/src/app/features/auth/store/useAuthStore.ts b/src/app/features/auth/store/useAuthStore.ts new file mode 100644 index 0000000..29b4470 --- /dev/null +++ b/src/app/features/auth/store/useAuthStore.ts @@ -0,0 +1,20 @@ +import { create } from 'zustand' +import { persist } from 'zustand/middleware' +import { AuthState } from '../types/auth.type' + +export const useAuthStore = create()( + persist( + (set) => ({ + accessToken: null, + user: null, + isLoggedIn: false, + setAccessToken: (token) => set({ accessToken: token, isLoggedIn: true }), + setUser: (user) => set({ user: user }), + clearAuthState: () => + set({ accessToken: null, user: null, isLoggedIn: false }), + }), + { + name: 'auth-storage', + }, + ), +) diff --git a/src/app/features/auth/types/auth.type.ts b/src/app/features/auth/types/auth.type.ts new file mode 100644 index 0000000..d5029af --- /dev/null +++ b/src/app/features/auth/types/auth.type.ts @@ -0,0 +1,26 @@ +import { User } from '@/app/shared/types/user.type' + +export interface LoginRequest { + email: string + password: string +} + +export interface LoginResponse { + accessToken: string + user: User +} + +export interface SignupRequest { + nickname: string + email: string + password: string +} + +export interface AuthState { + accessToken: string | null + user: User | null + isLoggedIn: boolean + setAccessToken: (token: string) => void + setUser: (user: User) => void + clearAuthState: () => void +} diff --git a/src/app/shared/lib/axios.ts b/src/app/shared/lib/axios.ts new file mode 100644 index 0000000..97868fa --- /dev/null +++ b/src/app/shared/lib/axios.ts @@ -0,0 +1,25 @@ +import axios from 'axios' +import { useAuthStore } from '@/app/features/auth/store/useAuthStore' +import { AUTH_ENDPOINT } from '@/app/features/auth/api/authEndpoint' + +const api = axios.create({ + baseURL: process.env.NEXT_PUBLIC_API_URL, +}) + +api.interceptors.request.use( + (config) => { + const token = useAuthStore.getState().accessToken + const publicPaths = [AUTH_ENDPOINT.LOGIN, AUTH_ENDPOINT.SIGNUP] + const isPulicPath = publicPaths.some((path) => config.url?.includes(path)) + + if (!isPulicPath && token) { + config.headers.Authorization = `Bearer ${token}` + } + return config + }, + (error) => { + return Promise.reject(error) + }, +) + +export default api diff --git a/src/app/shared/store/useUserStore.ts b/src/app/shared/store/useUserStore.ts deleted file mode 100644 index 30bdda9..0000000 --- a/src/app/shared/store/useUserStore.ts +++ /dev/null @@ -1,26 +0,0 @@ -// store/useUserStore.ts -import { create } from 'zustand' -import { persist } from 'zustand/middleware' - -import { User } from '../types/User.interface' - -interface UserState { - user: User | null - accessToken: string | null - setUser: (user: User, accessToken: string) => void - logout: () => void // clearUser와 동일 -} - -export const useUserStore = create()( - persist( - (set) => ({ - user: null, - accessToken: null, - setUser: (user, accessToken) => set({ user, accessToken }), - logout: () => set({ user: null, accessToken: null }), - }), - { - name: 'user-storage', - }, - ), -) diff --git a/src/app/shared/types/user.type.ts b/src/app/shared/types/user.type.ts index 5455413..75d83ed 100644 --- a/src/app/shared/types/user.type.ts +++ b/src/app/shared/types/user.type.ts @@ -1,9 +1,8 @@ export interface User { id: number - name: string email: string - avatarUrl?: string // optional - role?: 'user' | 'admin' // optional 예시 - createdAt?: string // optional ISO date string - updatedAt?: string // optional ISO date string + nickname: string + profileImageUrl: string | null + createdAt?: string + updatedAt?: string }