Login & Register with the MERN Stack (part5)
PART5. Frontend 구현 (redux setting)
In this part
- create component for Login, Register
- set up redux
Create Component
i. auth component
auth를 위한 Login.js
와 Register.js
파일을 auth
폴더 내에 생성한다.
mkdir auth && cd auth && touch Login.js Register.js
1. Register.js
email, name, password, confirm password를 입력받고 제출하는 폼을 만든다. 간단한 스타일도 미리 적용했다.
import React, { useState } from "react";
import { Link } from "react-router-dom";
import styled from "styled-components";
const StyledOuterContainer = styled.div`
box-sizing: border-box;
display: flex;
width: 100%;
height: 500px;
`;
const StyledInnerContainer = styled.div`
box-sizing: border-box;
display: block;
width: 50%;
height: 100%;
`;
const StyledForm = styled.form`
padding: 50px 0px 0px 50px;
`;
const StyledImg = styled.img`
max-width: 100%;
height: 100%;
overflow: hidden;
box-shadow: 1px 2px 10px black;
clip-path: polygon(-20% 0%, 100% 0%, 100% 100%, -20% 100%);
`;
const Styledh3 = styled.h3`
font: bold 40px/1 sans-serif;
text-shadow: 1px 1px 1px #999da0;
margin: 20px 0px;
padding: 50px 0px 0px 50px;
`;
const Styledp = styled.p`
font: 15px sans-serif;
color: #999da0;
padding: 0px 0px 0px 50px;
`;
const StyledLink = styled(Link)`
font: 15px/1 sans-serif;
color: #63c5da;
text-decoration-line: none;
`;
const StyledDiv = styled.div`
height: 50px;
`;
const StyledInput = styled.input`
border: none;
height: 30px;
width: 400px;
border-bottom: 1px #ebecf0 solid;
font: 13px sans-serif;
&:focus {
outline: none;
box-shadow: 0px 1px 2px #999da0;
}
`;
const StyledButton = styled.button`
all: unset;
box-sizing: border-box;
display: block;
width: 150px;
margin: 10px auto;
text-align: center;
padding: 10px;
color: white;
background-color: #3e4248;
text-decoration-line: none;
border-radius: 3px;
box-shadow: 1px 1px 2px black;
cursor: pointer;
`;
const Register = () => {
const [email, setEmail] = useState("");
const [name, setName] = useState("");
const [password, setPassword] = useState("");
const [password2, setPassword2] = useState("");
const onChangeEmail = (e) => {
setEmail(e.target.value);
};
const onChangeName = (e) => {
setName(e.target.value);
};
const onChangePassword = (e) => {
setPassword(e.target.value);
};
const onChangePassword2 = (e) => {
setPassword2(e.target.value);
};
const onSubmit = (e) => {
e.preventDefault();
const newUser = {
name: name,
email: email,
password: password,
password2: password2,
};
console.log(newUser);
};
return (
<StyledOuterContainer>
<StyledInnerContainer>
<Styledh3>Register</Styledh3>
<Styledp>
Already have an account? <StyledLink to="/login">Log in</StyledLink>
</Styledp>
<StyledForm onSubmit={onSubmit}>
<StyledDiv>
<StyledInput
type="email"
placeholder="Email"
value={email}
onChange={onChangeEmail}
/>
</StyledDiv>
<StyledDiv>
<StyledInput
type="text"
placeholder="Name"
value={name}
onChange={onChangeName}
/>
</StyledDiv>
<StyledDiv>
<StyledInput
type="password"
placeholder="Password"
value={password}
onChange={onChangePassword}
/>
</StyledDiv>
<StyledDiv>
<StyledInput
type="password"
placeholder="Confirm Password"
value={password2}
onChange={onChangePassword2}
/>
</StyledDiv>
<StyledButton type="submit">Sign up</StyledButton>
</StyledForm>
</StyledInnerContainer>
<StyledInnerContainer>
<StyledImg src="https://cdn.pixabay.com/photo/2016/03/27/07/32/clouds-1282314_960_720.jpg"></StyledImg>
</StyledInnerContainer>
</StyledOuterContainer>
);
};
export default Register;
register
화면은 다음과 같다.
입력한 뒤 SIGN UP 버튼을 누르면 콘솔창에 잘 나오는 것을 확인할 수 있다.
2. Login.js
Login.js
도 Register.js
와 비슷하다. email, password를 입력받는 폼을 만든다.
import React, { useState } from "react";
import { Link } from "react-router-dom";
import styled from "styled-components";
const StyledOuterContainer = styled.div`
box-sizing: border-box;
display: flex;
width: 100%;
height: 500px;
`;
const StyledInnerContainer = styled.div`
box-sizing: border-box;
display: block;
width: 50%;
height: 100%;
`;
const StyledForm = styled.form`
padding: 50px 0px 0px 50px;
`;
const StyledImg = styled.img`
max-width: 100%;
height: 100%;
overflow: hidden;
box-shadow: 1px 2px 10px black;
clip-path: polygon(-20% 0%, 100% 0%, 100% 100%, -20% 100%);
`;
const Styledh3 = styled.h3`
font: bold 40px/1 sans-serif;
text-shadow: 1px 1px 1px #999da0;
margin: 20px 0px;
padding: 50px 0px 0px 50px;
`;
const Styledp = styled.p`
font: 15px sans-serif;
color: #999da0;
padding: 0px 0px 0px 50px;
`;
const StyledLink = styled(Link)`
font: 15px/1 sans-serif;
color: #63c5da;
text-decoration-line: none;
`;
const StyledDiv = styled.div`
height: 50px;
`;
const StyledInput = styled.input`
border: none;
height: 30px;
width: 400px;
border-bottom: 1px #ebecf0 solid;
font: 13px sans-serif;
&:focus {
outline: none;
box-shadow: 0px 1px 2px #999da0;
}
`;
const StyledButton = styled.button`
all: unset;
box-sizing: border-box;
display: block;
width: 150px;
margin: 10px auto;
text-align: center;
padding: 10px;
color: white;
background-color: #3e4248;
text-decoration-line: none;
border-radius: 3px;
box-shadow: 1px 1px 2px black;
cursor: pointer;
`;
const Login = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const onChangeEmail = (e) => {
setEmail(e.target.value);
};
const onChangePassword = (e) => {
setPassword(e.target.value);
};
const onSubmit = (e) => {
e.preventDefault();
const userData = {
email: email,
password: password,
};
console.log(userData);
};
return (
<StyledOuterContainer>
<StyledInnerContainer>
<Styledh3>Login</Styledh3>
<Styledp>
Dont't have an account?{" "}
<StyledLink to="/register">Register</StyledLink>
</Styledp>
<StyledForm onSubmit={onSubmit}>
<StyledDiv>
<StyledInput
type="email"
placeholder="Email"
value={email}
onChange={onChangeEmail}
/>
</StyledDiv>
<StyledDiv>
<StyledInput
type="password"
placeholder="Password"
value={password}
onChange={onChangePassword}
/>
</StyledDiv>
<StyledButton type="submit">LOGIN</StyledButton>
</StyledForm>
</StyledInnerContainer>
<StyledInnerContainer>
<StyledImg src="https://cdn.pixabay.com/photo/2016/03/27/07/32/clouds-1282314_960_720.jpg"></StyledImg>
</StyledInnerContainer>
</StyledOuterContainer>
);
};
export default Login;
login
화면은 다음과 같다.
입력한 뒤 LOGIN 버튼을 누르면 콘솔창에 잘 나오는 것을 확인할 수 있다.
3. App.js
먼저 Routes
를 이용하기 위해 Route
,와 Routes
를 가져온다.
import { Route, Routes } from "react-router-dom";
그리고 앞에서 만든 Register
, Login
컴포넌트를 가져온다.
import Register from "./components/auth/Register";
import Login from "./components/auth/Login";
그다음 화면에서 Navbar 아래에서 각 url에 맞는 화면이 나타나야 한다. 따라서 Route를 이용하여 각 url에 component를 설정해준다. 완성된 코드는 아래와 같다.
import React, { Component } from "react";
import { Route, Routes } from "react-router-dom";
import Navbar from "./components/layout/Navbar";
import Landing from "./components/layout/Landing";
import Register from "./components/auth/Register";
import Login from "./components/auth/Login";
class App extends Component {
render() {
return (
<div className="App">
<Navbar />
<Routes>
<Route path="/" element={<Landing />} />
<Route path="/register" element={<Register />} />
<Route path="/login" element={<Login />} />
</Routes>
</div>
);
}
}
export default App;
각 버튼을 누르거나 url을 입력했을때 화면이 잘 나타나는 것을 확인 할 수 있다.
Setting up Redux
i. edit App.js
App.js
에 redux를 위해 Provider와 store를 가져온다.
import React, { Component } from "react";
import { Route, Routes } from "react-router-dom";
import { Provider } from "react-redux";
import store from "./store";
import Navbar from "./components/layout/Navbar";
import Landing from "./components/layout/Landing";
import Register from "./components/auth/Register";
import Login from "./components/auth/Login";
class App extends Component {
render() {
return (
<Provider store={store}>
<div className="App">
<Navbar />
<Routes>
<Route path="/" element={<Landing />} />
<Route path="/register" element={<Register />} />
<Route path="/login" element={<Login />} />
</Routes>
</div>
</Provider>
);
}
}
export default App;
ii. setting up redux file structure
먼저 store.js
파일을 만든다.
mkdir store.js
다음 actions
폴더에 authActions.js
와 types.js
파일을 만든다.
mkdir actions && cd actions && touch authActions.js && touch types.js
reducers
폴더에 index.js
, authReducer.js
, errorReducer.js
파일을 만든다.
mkdir reducers && cd reducers && touch authReducer.js && touch errorReducer.js && touch index.js
iii. setting up store
store는 redux에서 가장 핵심적인 인스턴스이다. 현재 상태를 내장하고있고, subscribe 중인 함수들이 상태가 업데이트 될 때 마다 다시 실행되게 한다.
store.js
에서 sotre를 create 한다.
import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
const initialState = {};
const middleware = [thunk];
const store = createStore(
() => [],
initialState,
compose(applyMiddleware(...middleware))
);
export default store;
iiii. define actions
action은 store라는 저장소에 정보를 전달하기 위한 데이터의 묶음이다.
따라서 types.js
에 다음을 export한다.
export const GET_ERRORS = "GET_ERRORS";
export const USER_LOADING = "USER_LOADING";
export const SET_CURRENT_USER = "SET_CURRENT_USER";
v. create reducers
Reducer들은 상태변화들을 어떻게 할지 관리하는 정보가 담겨있다. 따라서 Store와 정보를 주고받는 역할을 한다.
1. authReducer.js
먼저 types.js
에서 actions를 import 한다. 다음 initialState를 설정한다. 마지막으로 action type에 따라 state의 변화를 정의한다.
import { SET_CURRENT_USER, USER_LOADING } from "../actions/types";
const isEmpty = require("is-empty");
const initialState = {
isAuthenticated: false,
user: {},
loading: false,
};
export default function (state = initialState, action) {
switch (action.type) {
case SET_CURRENT_USER:
return {
...state,
isAuthenticated: !isEmpty(action.payload),
user: action.payload,
};
case USER_LOADING:
return {
...state,
loading: true,
};
default:
return state;
}
}
2. errorReducer.js
actions가 GET_ERRORS일 때 처리를 해준다.
import { GET_ERRORS } from "../actions/types";
const initialState = {};
export default function (state = initialState, action) {
switch (action.type) {
case GET_ERRORS:
return action.payload;
default:
return state;
}
}
3. index.js
combineReducers를 사용하여 authReducer와 errorReducer를 하나의 Reducer로 관리할 수 있게 만든다.
import { combineReducers } from "redux";
import authReducer from "./authReducer";
import errorReducer from "./errorReducer";
export default combineReducers({
auth: authReducer,
errors: errorReducer,
});
vi. set auth token
Authorization을 관리하기 위한 auth token을 setAuthToken.js
에 정의한다.
먼저 util
폴더를 만들고 setAuthToken.js
파일을 생성한다.
mkdir utils && cd utils && touch setAuthToken.js
다음 코드를 작성한다.
import axios from "axios";
const setAuthToken = (token) => {
if (token) {
// 만약 로그인되어 있다면 authorization token을 모든 request에 apply한다.
axios.defaults.headers.common["Authorization"] = token;
} else {
// 로그인 안되어있으면 삭제
delete axios.defaults.headers.common["Authorization"];
}
};
export default setAuthToken;
vii. create actions
먼저 types.js
에서 actions와 dependencies를 import한다. 다음 axios를 이용하여 각 action에 HTTPRequest를 만든다. 마지막으로 dispatch를 이용하여 actions를 reducers에 전달한다.
import axios from "axios";
import setAuthToken from "../utils/setAuthToken";
import jwt_decode from "jwt-decode";
import { GET_ERRORS, SET_CURRENT_USER, USER_LOADING } from "./types";
// Register User
export const registerUser = (userData, history) => (dispatch) => {
axios
.post("/api/users/register", userData)
.then((res) => history.push("/login")) // register가 성공하면 login창으로 redirect
.catch((err) =>
dispatch({
type: GET_ERRORS,
payload: err.response.data,
})
);
};
// Login User
export const loginUser = (userData) => (dispatch) => {
axios
.post("/api/users/login", userData)
.then((res) => {
//localStorage에 token 저장
const { token } = res.data;
localStorage.setItem("jwtToken", token);
// set authToken
setAuthToken(token);
// decode token
const decoded = jwt_decode(token);
// Set current user
dispatch(setCurrentUser(decoded));
})
.catch((err) =>
dispatch({
type: GET_ERRORS,
payload: err.response.data,
})
);
};
// set current user
export const setCurrentUser = (decoded) => {
return {
type: SET_CURRENT_USER,
payload: decoded,
};
};
// set user loading
export const setUserLoading = () => {
return {
type: USER_LOADING,
};
};
// logout
export const logoutUser = () => (dispatch) => {
// local storage에서 token 제거
localStorage.removeItem("jwtToken");
// auth token 제거
setAuthToken(false);
// current user 비우기
dispatch(setCurrentUser({}));
};
viii. set rootReducer
store.js
에 rootReducer를 추가한다.
import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
import rootReducer from "./reducers";
const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
compose(applyMiddleware(...middleware))
);
export default store;
redux 세팅 끝~~
Author And Source
이 문제에 관하여(Login & Register with the MERN Stack (part5)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@yeonoey/Login-Register-with-the-MERN-Stack-part5저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)