- 로그인 요청 후 응답을 AuthData 로 저장 로직 구현 - Access/Refresh 토큰 구현 중
This commit is contained in:
@@ -3,7 +3,7 @@ import SignUpPage from './ui/page/signup/SignUpPage';
|
||||
import Layout from './layouts/Layout';
|
||||
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
|
||||
import { useAuthStore } from './store/authStore';
|
||||
import { PageRouting } from './data/RoutingData';
|
||||
import { PageRouting } from './const/PageRouting';
|
||||
import LoginPage from './ui/page/login/LoginPage';
|
||||
import ResetPasswordPage from './ui/page/resetPassword/ResetPasswordPage';
|
||||
|
||||
@@ -17,7 +17,7 @@ function App() {
|
||||
<Route element={<LoginPage />} path={PageRouting["LOGIN"].path} />
|
||||
<Route element={<SignUpPage />} path={PageRouting["SIGN_UP"].path} />
|
||||
<Route element={<ResetPasswordPage />} path={PageRouting["RESET_PASSWORD"].path} />
|
||||
{!(authData?.isLogedIn) ? <Route element={<Navigate to={PageRouting["LOGIN"].path} />} path="*" /> : null}
|
||||
{!authData ? <Route element={<Navigate to={PageRouting["LOGIN"].path} />} path="*" /> : null}
|
||||
</Route>
|
||||
</Routes>
|
||||
</Router>
|
||||
|
||||
9
src/const/HttpResponse.ts
Normal file
9
src/const/HttpResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export const HttpResponse = {
|
||||
"ACCESS_TOKEN_EXPIRED": "ACCESS TOKEN EXPIRED",
|
||||
"REFRESH_TOKEN_EXPIRED": "REFRESH TOKEN EXPIRED",
|
||||
"UNAUTHORIZED": "UNAUTHORIZED",
|
||||
"OK": "OK",
|
||||
"CREATED": "CREATED",
|
||||
"BAD_REQUEST": "BAD REQUEST",
|
||||
"INTERNAL_SERVER_ERROR": "INTERNAL SERVER ERROR"
|
||||
} as const;
|
||||
@@ -16,5 +16,5 @@ export const PageRouting: Record<string, PageRoutingInfo> = {
|
||||
USER_FOLLOWING: { path: "/info/following", title: "팔로잉 목록" },
|
||||
USER_FOLLOWER: { path: "/info/follower", title: "팔로워 목록" },
|
||||
SETTINGS: { path: "/settings", title: "설정" },
|
||||
NOT_FOUD: { path: "/not-found", title: "존재하지 않는 페이지" },
|
||||
NOT_FOUND: { path: "/not-found", title: "존재하지 않는 페이지" },
|
||||
} as const;
|
||||
@@ -1,5 +1,4 @@
|
||||
export type AuthData = {
|
||||
accessToken: string;
|
||||
refreshToken: string;
|
||||
isLogedIn: boolean;
|
||||
}
|
||||
@@ -20,22 +20,51 @@ export class AccountNetwork extends BaseNetwork {
|
||||
async checkDuplication(data: CheckDuplicationRequest) {
|
||||
const { type, value } = data;
|
||||
|
||||
return await this.instance.get<CheckDuplicationResponse>(`${this.baseUrl}/check-duplication?type=${type}&value=${value}`);
|
||||
return await this.get<CheckDuplicationResponse>(
|
||||
`${this.baseUrl}/check-duplication?type=${type}&value=${value}`
|
||||
, {
|
||||
authPass: true
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async sendVerificationCode(data: SendVerificationCodeRequest) {
|
||||
return await this.instance.post<SendVerificationCodeResponse>(this.baseUrl + "/send-verification-code", data);
|
||||
return await this.post<SendVerificationCodeResponse>(
|
||||
this.baseUrl + "/send-verification-code"
|
||||
, data
|
||||
, {
|
||||
authPass: true
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async verifyCode(data: VerifyCodeRequest) {
|
||||
return await this.instance.post<VerifyCodeResponse>(this.baseUrl + "/verify-code", data);
|
||||
return await this.post<VerifyCodeResponse>(
|
||||
this.baseUrl + "/verify-code"
|
||||
, data
|
||||
, {
|
||||
authPass: true
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async signup(data: SignupRequest) {
|
||||
return await this.instance.post<SignupResponse>(this.baseUrl + "/signup", data);
|
||||
return await this.post<SignupResponse>(
|
||||
this.baseUrl + "/signup"
|
||||
, data
|
||||
, {
|
||||
authPass: true
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async login(data: LoginRequest) {
|
||||
return await this.instance.post<LoginResponse>(this.baseUrl + "/login", data);
|
||||
return await this.post<LoginResponse>(
|
||||
this.baseUrl + "/login"
|
||||
, data
|
||||
, {
|
||||
authPass: true
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import type {
|
||||
AxiosRequestConfig,
|
||||
AxiosError,
|
||||
AxiosResponse,
|
||||
InternalAxiosRequestConfig,
|
||||
} from "axios";
|
||||
|
||||
export class BaseNetwork {
|
||||
@@ -29,12 +30,15 @@ export class BaseNetwork {
|
||||
// ★ 요청 인터셉터
|
||||
this.instance.interceptors.request.use(
|
||||
(config) => {
|
||||
// 예: 자동 토큰 추가
|
||||
// const token = localStorage.getItem("token");
|
||||
// if (token) {
|
||||
// config.headers.Authorization = `Bearer ${token}`;
|
||||
// }
|
||||
|
||||
const reqConfig = config as InternalAxiosRequestConfig & { authPass?: boolean };
|
||||
if (reqConfig.authPass) {
|
||||
return config;
|
||||
}
|
||||
const accessToken = localStorage.getItem("accessToken");
|
||||
if (accessToken) {
|
||||
config.headers.Authorization = `Bearer ${accessToken}`;
|
||||
}
|
||||
|
||||
return config;
|
||||
},
|
||||
(error: AxiosError) => Promise.reject(error)
|
||||
@@ -61,11 +65,11 @@ export class BaseNetwork {
|
||||
/**
|
||||
* 기본 CRUD 메서드
|
||||
*/
|
||||
protected async get<T = any>(url: string, config?: AxiosRequestConfig) {
|
||||
protected async get<T = any>(url: string, config?: AxiosRequestConfig & { authPass?: boolean }) {
|
||||
return await this.instance.get<T>(url, config);
|
||||
}
|
||||
|
||||
protected async post<T = any>(url: string, data?: any, config?: AxiosRequestConfig) {
|
||||
protected async post<T = any>(url: string, data?: any, config?: AxiosRequestConfig & { authPass?: boolean }) {
|
||||
return await this.instance.post<T>(url, data, config);
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,15 @@ interface AuthStoreProps {
|
||||
|
||||
export const useAuthStore = create<AuthStoreProps>((set) => ({
|
||||
authData: undefined,
|
||||
login: (data: AuthData) => set({ authData: data }),
|
||||
logout: () => set({ authData: undefined })
|
||||
login: (data: AuthData) => {
|
||||
set({ authData: data });
|
||||
Object.entries(data)
|
||||
.forEach((entry) => {
|
||||
localStorage.setItem(entry[0], entry[1]);
|
||||
})
|
||||
},
|
||||
logout: () => {
|
||||
set({ authData: undefined });
|
||||
localStorage.clear();
|
||||
}
|
||||
}));
|
||||
@@ -7,17 +7,18 @@ import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { PageRouting } from '@/data/RoutingData';
|
||||
import { PageRouting } from '@/const/PageRouting';
|
||||
import * as z from 'zod';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { Validator } from '@/util/Validator';
|
||||
import { LoginRequest } from '@/data/request/account/LoginRequest';
|
||||
import { AccountNetwork } from '@/network/AccountNetwork';
|
||||
import { toast } from 'sonner';
|
||||
import { LoginResponse } from '@/data/response';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
|
||||
export default function LoginPage() {
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
const { login } = useAuthStore();
|
||||
const navigate = useNavigate();
|
||||
const accountNetwork = new AccountNetwork();
|
||||
const loginForm = useForm<z.infer<typeof LoginSchema>>({
|
||||
@@ -37,17 +38,19 @@ export default function LoginPage() {
|
||||
navigate(PageRouting["RESET_PASSWORD"].path);
|
||||
}, []);
|
||||
|
||||
const moveToMainPage = useCallback(() => {
|
||||
|
||||
const moveToHomePage = useCallback(() => {
|
||||
navigate(PageRouting["HOME"].path);
|
||||
}, []);
|
||||
|
||||
// TODO 33 로그인 기능 구현
|
||||
const login = async () => {
|
||||
const reqLogin = async () => {
|
||||
if (isLoading) return;
|
||||
const type = Validator.isEmail(id) ? 'email' : 'accountId';
|
||||
|
||||
const data: LoginRequest = new LoginRequest(type, id, password);
|
||||
|
||||
setIsLoading(true);
|
||||
|
||||
const loginPromise = accountNetwork.login(data);
|
||||
|
||||
toast.promise<{ message?: string }>(
|
||||
@@ -55,9 +58,9 @@ export default function LoginPage() {
|
||||
try {
|
||||
loginPromise.then((res) => {
|
||||
if (res.data.success) {
|
||||
resolve({message: ''})
|
||||
resolve({message: ''});
|
||||
} else {
|
||||
reject(res.data.message)
|
||||
reject(res.data.message);
|
||||
}
|
||||
})
|
||||
} catch (err) {
|
||||
@@ -71,11 +74,18 @@ export default function LoginPage() {
|
||||
}
|
||||
);
|
||||
|
||||
// loginPromise.then((res) => {
|
||||
// if (res.data.success) {
|
||||
// moveToMainPage();
|
||||
// }
|
||||
// });
|
||||
loginPromise
|
||||
.then((res) => {
|
||||
if (res.data.success) {
|
||||
const data = {
|
||||
accessToken: res.data.accessToken!,
|
||||
refreshToken: res.data.refreshToken!
|
||||
}
|
||||
login({ ...data });
|
||||
moveToHomePage();
|
||||
}
|
||||
})
|
||||
.finally(() => setIsLoading(false));
|
||||
}
|
||||
|
||||
const TextSeparator = ({ text }: { text: string }) => {
|
||||
@@ -148,7 +158,7 @@ export default function LoginPage() {
|
||||
className="w-full bg-indigo-500 hover:bg-indigo-400"
|
||||
type="button"
|
||||
disabled={id.trim().length < 1 || password.trim().length < 1}
|
||||
onClick={login}
|
||||
onClick={reqLogin}
|
||||
>
|
||||
로그인
|
||||
</Button>
|
||||
|
||||
@@ -7,7 +7,7 @@ import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useState, useCallback } from 'react';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { PageRouting } from '@/data/RoutingData';
|
||||
import { PageRouting } from '@/const/PageRouting';
|
||||
import * as z from 'zod';
|
||||
|
||||
export default function ResetPasswordPage() {
|
||||
|
||||
@@ -17,7 +17,7 @@ import { CheckDuplicationRequest, SignupRequest } from '@/data/request';
|
||||
import { AccountNetwork } from '@/network/AccountNetwork';
|
||||
import { toast } from 'sonner';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { PageRouting } from '@/data/RoutingData';
|
||||
import { PageRouting } from '@/const/PageRouting';
|
||||
|
||||
export default function SignUpPage() {
|
||||
const [emailVerificationModalOpen, setEmailVerificationModalOpen] = useState<boolean>(false);
|
||||
|
||||
@@ -4,6 +4,9 @@ import tailwindcss from '@tailwindcss/vite'
|
||||
import path from 'path'
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
server: {
|
||||
port: 5185
|
||||
},
|
||||
plugins: [
|
||||
react(),
|
||||
tailwindcss()
|
||||
|
||||
Reference in New Issue
Block a user