개발자 박가나
[241125 TIL] 본캠프 39일차 ('MBTI' 프로젝트 기능 구현) 본문
인증/인가 기능 구현을 위해서 테스트 전용 API(https://moneyfulpublicpolicy.co.kr)를 사용하였다.
회원가입
https://moneyfulpublicpolicy.co.kr/register로 post 요청을 보내는 방법으로 회원가입 기능을 구현하였다.
/* src/api/Auth.js */
import axios from 'axios';
const BASE_URL = 'https://moneyfulpublicpolicy.co.kr';
export const runSignUp = async (data) => {
try {
const response = await axios.post(`${BASE_URL}/register`, data);
return { data: response.data };
} catch (e) {
return { error: e };
}
};
/* src/pages/SignUp.jsx */
import { useNavigate } from 'react-router-dom';
import { useForm } from '../hooks/useForm';
import { runSignUp } from '../api/MoneyfulPublicPolicy';
export default function SignUp() {
const navigate = useNavigate();
const { values, handleChange } = useForm({
id: '',
password: '',
nickname: ''
});
const handleSignUp = async (e) => {
e.preventDefault();
const { data, error } = await runSignUp(values);
if (error) {
window.alert(`${error.status} 오류가 발생했습니다.`);
}
else if (data.success) {
window.alert('회원가입에 성공했습니다.');
navigate('/signin');
}
else {
window.alert('회원가입에 실패했습니다.');
}
};
}
로그인
https://moneyfulpublicpolicy.co.kr/login으로 post 요청을 보내는 방법으로 로그인 기능을 구현하였다.
- 로그인 성공 시 localStorage에 accessToken 값 저장
- localStorage에 accessToken 값이 있으면 로그인한 상태로 판단
/* src/api/Auth.js */
import axios from 'axios';
const BASE_URL = 'https://moneyfulpublicpolicy.co.kr';
export const runSignIn = async (data) => {
try {
const response = await axios.post(`${BASE_URL}/login`, data);
return { data: response.data };
} catch (e) {
return { error: e };
}
};
/* src/pages/SignIn.jsx */
import { useNavigate } from 'react-router-dom';
import { useForm } from '../hooks/useForm';
import { runSignIn } from '../api/MoneyfulPublicPolicy';
import { useAuth } from '../contexts/AuthContext';
export default function SignIn() {
const navigate = useNavigate();
const { login } = useAuth();
const { values, handleChange } = useForm({
id: '',
password: ''
});
const handleSignIn = async (e) => {
e.preventDefault();
const { data, error } = await runSignIn(values);
if (error) {
window.alert(`${error.status} 오류가 발생했습니다.`);
}
else if (data.success) {
window.alert('로그인에 성공했습니다.');
login(data.accessToken);
navigate('/');
}
else {
window.alert('로그인에 실패했습니다.');
}
};
}
/* src/contexts/AuthContext.jsx */
import { useState } from 'react';
const token = localStorage.getItem('accessToken');
export const AuthProvider = ({ children }) => {
const [isAuthenticated, setIsAuthenticated] = useState(!!token);
const login = (token) => {
localStorage.setItem('accessToken', token);
setIsAuthenticated(true);
};
};
로그아웃
localStorage에 저장된 값을 제거하는 방법으로 로그아웃 기능을 구현하였다.
- 로그아웃 성공 시 localStorage에서 accessToken 값 제거
- localStorage에 accessToken 값이 없으면 로그인하지 않은 상태(= 로그아웃한 상태)로 판단
/* src/components/Header.jsx */
import { useNavigate } from 'react-router-dom';
import { useAuth } from '../contexts/AuthContext';
export default function Header() {
const navigate = useNavigate();
const { logout } = useAuth();
const handleSignOut = () => {
logout();
navigate('/signin');
};
}
/* src/contexts/AuthContext.jsx */
import { useState } from 'react';
const token = localStorage.getItem('accessToken');
export const AuthProvider = ({ children }) => {
const [isAuthenticated, setIsAuthenticated] = useState(!!token);
const logout = () => {
localStorage.removeItem('accessToken');
setIsAuthenticated(false);
};
};
프로필 수정
https://moneyfulpublicpolicy.co.kr/profile로 patch 요청을 보내는 방법으로 프로필 수정 기능을 구현하였다.
/* src/api/Auth.js */
import axios from 'axios';
const BASE_URL = 'https://moneyfulpublicpolicy.co.kr';
export const fetchUser = async (token) => {
try {
const response = await axios.get(`${BASE_URL}/user`, {
headers: {
Authorization: `Bearer ${token}`
}
});
return { data: response.data };
} catch (e) {
return { error: e };
}
};
export const updateUser = async (token, data) => {
try {
const response = await axios.patch(`${BASE_URL}/profile`, data, {
headers: {
'Content-Type': 'multipart/form-data',
Authorization: `Bearer ${token}`
}
});
return { data: response.data };
} catch (e) {
return { error: e };
}
};
/* src/pages/Profile.jsx */
import { useEffect, useState } from 'react';
import { fetchUser, updateUser } from '../api/Auth';
const token = localStorage.getItem('accessToken');
export default function Profile() {
const [nickname, setNickname] = useState('');
useEffect(() => {
const fetchUserData = async () => {
const { data, error } = await fetchUser(token);
// 오류 발생
if (error) {
window.alert(`${error.status} 오류가 발생했습니다.`);
}
// 성공
else if (data.success) {
setNickname(data.nickname);
}
// 실패
else {
window.alert('유저 정보를 불러오지 못했습니다.');
}
};
fetchUserData();
}, []);
const handleUpdateUser = async () => {
const { data, error } = await updateUser(token, { nickname });
// 오류 발생
if (error) {
window.alert(`${error.status} 오류가 발생했습니다.`);
}
// 성공
else if (data.success) {
window.alert('프로필이 수정되었습니다.');
window.location.reload();
}
// 실패
else {
window.alert('유저 정보를 불러오지 못했습니다.');
}
};
}
로그인 여부에 따라 접속 가능한 페이지 제한 (Protected Route)
로그인 여부에 따라 접속 가능한 페이지에 제한을 두었다.
- 로그인 여부에 상관없이 Home 페이지 접속 가능
- 로그인을 하지 않은 상태로 Profile / Test / Result 페이지 접속 시 SignIn 페이지로 이동
- 로그인을 한 상태로 SignUp / SignIn 페이지 접속 시 Home 페이지로 이동
/* src/routes/AuthenticatedRoute.jsx */
import { Navigate, Outlet } from 'react-router-dom';
import { useAuth } from '../contexts/AuthContext';
export default function AuthenticatedRoute() {
const { isAuthenticated } = useAuth();
if (!isAuthenticated) return <Navigate to="/signin" replace />;
return <Outlet />;
}
/* src/routes/NonAuthenticatedRoute.jsx */
import { Navigate, Outlet } from 'react-router-dom';
import { useAuth } from '../contexts/AuthContext';
export default function NonAuthenticatedRoute() {
const { isAuthenticated } = useAuth();
if (isAuthenticated) return <Navigate to="/" replace />;
return <Outlet />;
}
/* src/routes/Router.jsx */
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import AuthenticatedRoute from './AuthenticatedRoute';
import NonAuthenticatedRoute from './NonAuthenticatedRoute';
function Router() {
return (
<BrowserRouter>
<Header />
<Routes>
<Route path="/" element={<Home />} />
<Route element={<NonAuthenticatedRoute />}>
<Route path="/signin" element={<SignIn />} />
<Route path="/signup" element={<SignUp />} />
</Route>
<Route element={<AuthenticatedRoute />}>
<Route path="/profile" element={<Profile />} />
<Route path="/test" element={<Test />} />
<Route path="/result" element={<Result />} />
</Route>
</Routes>
</BrowserRouter>
);
}
export default Router;
'내일배움캠프' 카테고리의 다른 글
[241127 TIL] 본캠프 41일차 (TanStack Query 실습) (0) | 2024.11.27 |
---|---|
[241126 TIL] 본캠프 40일차 ('MBTI' 프로젝트 기능 구현) (0) | 2024.11.26 |
[241122 TIL] 본캠프 38일차 (1) | 2024.11.22 |
[241121 TIL] 본캠프 37일차 (0) | 2024.11.21 |
[241120 TIL] 본캠프 36일차 ('뉴스피드' 프로젝트 4일차) (1) | 2024.11.20 |