diff --git a/src/app/auth/redirect/kakao/page.tsx b/src/app/auth/redirect/kakao/page.tsx new file mode 100644 index 00000000..08b2d4d3 --- /dev/null +++ b/src/app/auth/redirect/kakao/page.tsx @@ -0,0 +1,54 @@ +'use client'; + +import { useSearchParams, useRouter } from 'next/navigation'; +import { useEffect } from 'react'; +import { AxiosError } from 'axios'; + +import axiosInstance from '@/lib/axios/axiosInstance'; +import { useUser } from '@/store/useUser'; +import { UserOnLoginType } from '@/lib/types/user'; + +export default function KakaoRedirectPage() { + const router = useRouter(); + const { updateUser } = useUser(); + const searchParams = useSearchParams(); + const code = searchParams ? searchParams.get('code') : null; + + useEffect(() => { + const controller = new AbortController(); + + if (!code) { + router.push('/login'); + return; + } + + const loginKakao = async () => { + try { + const res = await axiosInstance.get(`/auth/redirect/kakao?code=${code}`, { + signal: controller.signal, + }); + + const { id, accessToken } = res.data; + updateUser({ id, accessToken }); + + router.push('/'); + } catch (error) { + if (error instanceof AxiosError) { + if (!controller.signal.aborted) { + console.error(error.message); + } else { + console.log('Request canceled:', error.message); + } + } + } + }; + + loginKakao(); + + return () => { + controller.abort(); // 마운트 해제 및 axios 요청 취소 + }; + }, [code]); + + return
로그인 중입니다.
; +} diff --git a/src/app/login/_components/init.ts b/src/app/login/_components/init.ts new file mode 100644 index 00000000..e099ee9f --- /dev/null +++ b/src/app/login/_components/init.ts @@ -0,0 +1,2 @@ +// 보일러플레이트용 임시 파일 +// 추후 이 파일은 지워주세요 diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx new file mode 100644 index 00000000..ec76dd49 --- /dev/null +++ b/src/app/login/page.tsx @@ -0,0 +1,24 @@ +/** + TODO + - [ ] 로그인 만료 확인, refreshToken(추후) + - [ ] 로그인 페이지 UI + */ + +import Link from 'next/link'; + +const oauthType = { + kakao: 'kakao', + naver: 'naver', + google: 'google', +}; + +export default function LoginPage() { + return ( +
+ 로그인페이지 +
+ 카카오 로그인 +
+
+ ); +} diff --git a/src/lib/axios/axiosInstance.ts b/src/lib/axios/axiosInstance.ts index e642f4b9..5063aa8c 100644 --- a/src/lib/axios/axiosInstance.ts +++ b/src/lib/axios/axiosInstance.ts @@ -1,8 +1,25 @@ import axios from 'axios'; +import { useUser } from '@/store/useUser'; const axiosInstance = axios.create({ baseURL: 'https://dev.api.listywave.com', - withCredentials: true, + withCredentials: true, // refreshToken을 고려해서 true로 설정 }); +axiosInstance.interceptors.request.use( + (config) => { + const accessToken = useUser.getState().user.accessToken; + + if (accessToken) { + config.headers.Authorization = `${accessToken}`; // Bearer option 추가 예정 + } + + return config; + }, + (error) => { + console.log(error); + return Promise.reject(error); + } +); + export default axiosInstance; diff --git a/src/lib/types/user.ts b/src/lib/types/user.ts new file mode 100644 index 00000000..888605ea --- /dev/null +++ b/src/lib/types/user.ts @@ -0,0 +1,12 @@ +// 로그인한 사용자 리스폰스 타입 +export interface UserOnLoginType { + id: number; + nickname: string; + description?: string; + profileImageUrl?: string; + backgroundImageUrl?: string; + followerCount: number; + followingCount: number; + isFirst: boolean; + accessToken: string; +} diff --git a/src/store/useUser.ts b/src/store/useUser.ts new file mode 100644 index 00000000..a0938995 --- /dev/null +++ b/src/store/useUser.ts @@ -0,0 +1,39 @@ +import { create } from 'zustand'; +import { devtools, persist } from 'zustand/middleware'; +import { UserOnLoginType } from '@/lib/types/user'; + +interface UserStateType { + user: Pick; + updateUser: (user: Pick) => void; +} + +const initialValue = { + id: 0, + accessToken: '', +}; + +// 사용자 정보(id) 및 상태(로그인, 로그아웃)를 저장하는 store +const useUserStore = create()( + devtools( + persist( + (set) => ({ + user: initialValue, + updateUser: (user) => + set((state) => ({ + user: { + ...state.user, + ...user, + }, + })), + }), + { + name: 'user-storage', // localStorage에 저장될 이름 + } + ), + { + name: 'user-store', // Devtools에서 사용될 스토어 이름 + } + ) +); + +export const useUser = useUserStore; diff --git a/tsconfig.json b/tsconfig.json index 97442403..aec7d58b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,13 +18,13 @@ "incremental": true, "plugins": [ { - "name": "next" - } + "name": "next", + }, ], "paths": { - "@/*": ["./src/*"] - } + "@/*": ["./src/*"], + }, }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"] + "exclude": ["node_modules"], }