Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
792d1d4
✨feat: 사용자 및 인증 관련 타입 정의
Insung-Jo Jun 7, 2025
09a8bdf
✨feat: 인증 상태 관리용 zustand store 구현
Insung-Jo Jun 7, 2025
1375491
✨feat: axios 인스턴스 및 인증 토큰 인터셉터 적용
Insung-Jo Jun 7, 2025
b730197
📁chore: 폴더 경로 수정
Insung-Jo Jun 7, 2025
d6f14be
🫧modify: axios 인스턴스 네이밍을 api로 변경
Insung-Jo Jun 7, 2025
2e4fcd0
✨feat: 엔드포인트 상수 분리
Insung-Jo Jun 7, 2025
bb45d72
🫧modify: 경로 하드코딩 제거 및 엔드포인트 상수 적용
Insung-Jo Jun 7, 2025
1b60403
✨feat: 로그인 및 회원가입 API 모듈 작성
Insung-Jo Jun 7, 2025
d2da14f
🫧modify: 파일명 컨벤션에 맞게 변경
Insung-Jo Jun 7, 2025
21bc020
✨feat: API 응답 타입 분리 및 타입 정의 개선
Insung-Jo Jun 8, 2025
2d49679
🫧modify: clearAuthState로 이름 변경 및 타입 정리
Insung-Jo Jun 8, 2025
c96b2cd
Merge branch 'develop' into feature/auth
Insung-Jo Jun 8, 2025
c715ea4
✨ Feat: useAuth 훅 구현
Insung-Jo Jun 8, 2025
9b428bc
Merge branch 'develop' into feature/auth
Insung-Jo Jun 9, 2025
e3368f5
🗑️remove: 겹치는 파일 제거
Insung-Jo Jun 9, 2025
3b50240
🫧modify: 커스텀 훅 일부 수정
Insung-Jo Jun 9, 2025
8a9cd00
🫧modify: 파일명 변경 및 적용
Insung-Jo Jun 9, 2025
22b6149
🐛fix: 코드래빗 수정사항 반영
Insung-Jo Jun 9, 2025
51b6910
🫧modify: 누락된 타입 반영
Insung-Jo Jun 9, 2025
105f8ac
🫧modify: 조건문 일부 수정
Insung-Jo Jun 9, 2025
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
14 changes: 14 additions & 0 deletions src/app/features/auth/api/authApi.ts
Original file line number Diff line number Diff line change
@@ -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<LoginResponse> => {
const response = await api.post<LoginResponse>(AUTH_ENDPOINT.LOGIN, data)
return response.data
}

export const signup = async (data: SignupRequest): Promise<SignupResponse> => {
const response = await api.post<SignupResponse>(AUTH_ENDPOINT.SIGNUP, data)
return response.data
}
4 changes: 4 additions & 0 deletions src/app/features/auth/api/authEndpoint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const AUTH_ENDPOINT = {
LOGIN: '/15-2/auth/login',
SIGNUP: '/15-2/users',
}
33 changes: 33 additions & 0 deletions src/app/features/auth/hooks/useAuth.ts
Original file line number Diff line number Diff line change
@@ -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,
}
}
20 changes: 20 additions & 0 deletions src/app/features/auth/store/useAuthStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { create } from 'zustand'
import { persist } from 'zustand/middleware'
import { AuthState } from '../types/auth.type'

export const useAuthStore = create<AuthState>()(
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',
},
),
)
26 changes: 26 additions & 0 deletions src/app/features/auth/types/auth.type.ts
Original file line number Diff line number Diff line change
@@ -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
}
25 changes: 25 additions & 0 deletions src/app/shared/lib/axios.ts
Original file line number Diff line number Diff line change
@@ -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
26 changes: 0 additions & 26 deletions src/app/shared/store/useUserStore.ts

This file was deleted.

9 changes: 4 additions & 5 deletions src/app/shared/types/user.type.ts
Original file line number Diff line number Diff line change
@@ -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
}