로그인 구현 (redux-persist)React+REST API 게시판 구현/FE - React2021. 10. 2. 15:17
Table of Contents
What to do?
- 이메일과 패스워드를 입력받아서 로그인을 하는 화면을 만든다.
- 로그인을 성공하면 서버에서 jwt 토큰을 받아서 store에 저장하고 redux-persist 모듈을 사용해서 local storage에도 저장한다.
- 회원가입과 마찬가지로 formik과 yup 라이브러리를 사용해서 validation을 처리한다.
redux-pesist
- redux에 저장하는것은 영구적인 저장소가 아니다. 만일 새로고침하게 되면 store에 저장된 정보는 메모리에 저장된것이기 때문에 모두 없어지게 된다.
- 따라서 영구적으로 저장하기 위해서는 local storage나 session storage에 저장해야 하는데 redux-persist 모듈이 그것을 도와준다.
- session storage는 페이지를 새로고침해도 데이터가 남아있고 local storage는 브라우저를 껏다 켜도 데이터가 남아있다!
- reduce에 persist store를 정의할 때 local storage에 저장하고 싶으면 아래처럼 storage를 불러와서 사용하면 되고
import storage from 'redux-persist/lib/storage
- session storage에 저장하고 싶으면 아래처럼 sessionStorage을 불러와서 사용하자
import storageSession from 'redux-persist/lib/storage/session
jwt 토큰 정보 저장
- 로그인을 성공하면 서버에서 jwt 토큰을 받는다. 토큰을 redux store에 저장하고 redux-persist 모듈을 사용해서 local storage나 session storage에도 저장한다.
- redux 폴더 아래 reducers 폴더를 만들고 token을 리턴하는 reducer를 만들기 위해 AuthReducer.js 파일을 생성하자.
redux/reducers/AuthReducer.js
const SET_TOKEN = 'set_token';
const AuthInitialState = {
token: null
}
export const setToken = (token) => ({
type: SET_TOKEN,
token
})
export const AuthReducer = (state = AuthInitialState, action) => {
switch(action.type) {
case SET_TOKEN:
return {
...state,
token: action.token
}
default:
return state;
}
}
- redux 폴더 아래 redux의 reducer를 redux-persist와 combine해서 store를 생성하는 configStore.js 파일을 생성하자.
redux/configStore.js
import {persistReducer} from "redux-persist";
import {AuthReducer} from "./reducers/AuthReducer";
import {createStore, combineReducers} from "redux";
// local storage 사용
import storage from "redux-persist/lib/storage";
const persistConfig = {
key: "root",
//local storage에 저장
storage: storage };
const allReducers = combineReducers({
Auth: AuthReducer
});
const store = createStore(persistReducer(persistConfig, allReducers),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
export default store;
- 마지막으로 index.tsx 파일에 redux와 redux-persist의 store를 적용시키자!
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {BrowserRouter} from "react-router-dom";
import {Provider} from "react-redux";
import store from "./redux/configStore";
import {PersistGate} from "redux-persist/integration/react";
import persistStore from "redux-persist/es/persistStore";
const persistor = persistStore(store)
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<PersistGate persistor={persistor}>
<BrowserRouter>
<App/>
</BrowserRouter>
</PersistGate>
</Provider>
);
reportWebVitals();
로그인 페이지 구현
- DB에 저장된 회원의 Email과 Password가 일치하지 않으면 toast 알람으로 에러 메시지를 띄워준다!
- 로그인을 성공하면 서버에서 jwt 토큰을 받아서 store에 저장하고 redux-persist 모듈을 사용해서 local storage에도 저장하고 홈화면으로 돌아간다!
- 사용할 url: /login
- 우선 pages 디렉토리 아래 login 디렉토리를 만들고 Login 컴포넌트를 만들자.
- 디자인은 SignUp 컴포넌트와 같기 때문에 SignUp 컴포넌트의 scss 파일을 사용하자!
pages/login/Login.js
- formik으로 form의 state 관리 및 유효성 검증을 하고 로그인을 하는 API를 호출해서 로그인에 성공시 jwt-token을 redux-persist에 저장하고 홈 화면으로 가게한다.
- 이메일이 존재하지 않거나 패스워드가 틀리면 toast 알림을 띄워준다.
import axios from "axios";
import {toast, ToastContainer} from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import {Formik, ErrorMessage} from "formik";
import * as Yup from "yup";
import {Button, TextField} from "@mui/material";
import {useNavigate} from "react-router-dom";
// SignUp 컴포넌트 scss 이용
import "../sign-up/signUp.scss";
import {useDispatch} from "react-redux";
import {setToken} from "../../redux/reducers/AuthReducer";
const Login = () => {
const navigate = useNavigate();
const dispatch = useDispatch();
const validationSchema = Yup.object().shape({
email: Yup.string()
.email("올바른 이메일 형식이 아닙니다!")
.required("이메일을 입력하세요!"),
password: Yup.string()
.required("패스워드를 입력하세요!")
});
const submit = async (values) => {
const {email, password} = values;
try {
const {data} = await axios.post("/api/auth/signin", {
email,
password,
});
dispatch(setToken(data.jwt));
toast.success(<h3>로그인 성공😎</h3>, {
position: "top-center",
autoClose: 2000,
});
setTimeout(() => {
navigate("/");
}, 2000);
} catch (e) {
// 서버에서 받은 에러 메시지 출력
toast.error(e.response.data.message + "😭", {
position: "top-center",
});
}
};
return (
<Formik
initialValues={{
email: "",
password: "",
}}
validationSchema={validationSchema}
onSubmit={submit}
>
{({values, handleSubmit, handleChange}) => (
<div className="signup-wrapper">
<ToastContainer/>
<form onSubmit={handleSubmit} autoComplete="off">
<div className="input-forms">
<div className="input-forms-item">
<div className="input-label">이메일</div>
<TextField
value={values.email}
name="email"
variant="outlined"
onChange={handleChange}
/>
<div className="error-message">
<ErrorMessage name="email"/>
</div>
</div>
<div className="input-forms-item">
<div className="input-label">비밀번호</div>
<TextField
value={values.password}
name="password"
variant="outlined"
type="password"
onChange={handleChange}
/>
<div className="error-message">
<ErrorMessage name="password"/>
</div>
</div>
<Button
color="primary"
variant="contained"
fullWidth
type="submit"
>
로그인
</Button>
</div>
</form>
</div>
)}
</Formik>
);
};
export default Login;
- App.tsx에 로그인화면 SignIn에 대한라우팅과 redux의 state(jwt-token)를 가져와서 콘솔에 출력하는 부분을 추가하자
라우팅 추가하기
App.js
- /login url로 진입했을 때 App.js에서 Login 컴포넌트를 보여주자
- App.js에서 redux의 state를 가져와서 콘솔에 찍어보면 새로고침을 해도 토큰의 정보가 남아있다!
import React from "react";
import {useSelector} from "react-redux";
import {Routes, Route} from "react-router-dom";
import Header from "./components/Header";
import Home from "./pages/home/Home";
import SignUp from "./pages/sign-up/SignUp";
import Login from "./pages/login/Login";
const App = () => {
const token = useSelector((state) => state.Auth.token);
console.log(token);
return (
<React.Fragment>
<Header/>
<Routes>
<Route path="/" element={<Home/>}/>
<Route path="/sign-up" element={<SignUp/>}/>
<Route path="/login" element={<Login/>}/>
</Routes>
</React.Fragment>
)
}
export default App
- App.js에서 콘솔에 출력해보면 redux state에도 토큰이 저장되어 있고 새로고침해도 화면에 출력되는 이유는 redux-persist 라이브러리로 redux의 상태를 로컬 스토리지에도 저장하고 있기 때문이다!
'React+REST API 게시판 구현 > FE - React' 카테고리의 다른 글
게시물 등록하기 페이지 구현 (0) | 2021.10.11 |
---|---|
axios interceptor 구현하기 (0) | 2021.10.04 |
인증 처리하기+PrivateRoute+로그아웃 (0) | 2021.10.04 |
회원가입 구현하기 (0) | 2021.09.29 |
초반 프로젝트 설정하기 (0) | 2021.09.26 |
@덕구공 :: Duck9s'
주니어 개발자에욤
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!