Register Page
RegisterPage는 앞서 작성한 LoginPage와 유사하기 때문에 복사해서 사용하고 필요한 코드를 추가해주면 된다.
// RegisterPage.js
import React, { useState } from 'react'
import { useDispatch } from 'react-redux';
import { registerUser } from '../../../_actions/user_action';
import { withRouter } from 'react-router-dom';
function RegisterPage() {
const [Email, setEmail] = useState("")
const [Name, setName] = useState("")
const [Password, setPassword] = useState("")
const [ConfirmPassword, setConfirmPassword] = useState("")
const onEmailHandler = (event) => {
setEmail(event.currentTarget.value)
}
const onNameHandler = (event) => {
setName(event.currentTarget.value)
}
const onPasswordHandler = (event) => {
setPassword(event.currentTarget.value)
}
const onConfirmPasswordHandler = (event) => {
setConfirmPassword(event.currentTarget.value)
}
const onSubmitHandler = (event) => {
event.preventDefault(); //페이지 refresh를 막아줌
if(Password !== ConfirmPassword) {
return alert('비밀번호와 비밀번호 확인은 같아야 합니다.')
}
let body = {
email: Email,
name: Name,
password: Password
}
dispatch(registerUser(body))
.then(response => {
if(response.payload.success) {
props.history.push('/login') //회원가입 성공 시 LoginPage로 이동
}
else {
alert('Failed to sign up')
}
})
}
return (
<div style = {{
display: 'flex', justifyContent: 'center', alignItems: 'center', width: '100%', height: '100vh'
}}>
<h2>RegisterPage</h2>
<form style = {{ display: 'flex', flexDirection: 'column' }}
onSubmit={onSubmitHandler}
>
<label>Email</label>
<input type="email" value={Email} onChange={onEmailHandler} />
<label>Name</label>
<input type="text" value={Name} onChange={onNameHandler} />
<label>Password</label>
<input type="password" value={Password} onChange={onPasswordHandler} />
<label>Confirm Password</label>
<input type="password" value={ConfirmPassword} onChange={onConfirmPasswordHandler} />
<br />
<button>
Sign Up
</button>
</form>
</div>
)
}
export default withRouter(RegisterPage)
loginUser Action과 마찬가지로 registerUser Action을 만들기 위해 user_action.js 파일에 다음 코드를 추가하여 작성한다.
//user_action.js
import axios from 'axios';
import { LOGIN_USER, REGISTER_USER } from './types';
export function loginUser(dataToSubmit) {
const request = axios.post('/api/users/login'), dataToSubmit)
.then(response => reponse.data)
return { //reducer로 넘겨준다.
type: LOGIN_USER,
payload: request
}
}
export function registerUser(dataToSubmit) {
const request = axios.post('/api/users/register'), dataToSubmit)
.then(response => reponse.data)
return { //reducer로 넘겨준다.
type: REGISTER_USER,
payload: request
}
}
그리고 types.js에도 REGISTER_USER를 추가해준다.
//types.js
export const LOGIN_USER = "login_user";
export const REGISTER_USER = "register_user";
Reducer 부분은 _reducers 폴더 안에 user_reducer에 작성하면 된다.
//user_reducer.js
import { LOGIN_USER, REGISTER_USER } from '../_actions/types';
export default function (state = {}, action) {
switch (action.type) {
case LOGIN_USER:
return { ...state, loginSuccess: action.payload }
break;
case REGISTER_USER:
return { ...state, register: action.payload }
break;
default:
return state;
}
}
이제 실행시켜서 localhost:3000/register로 이동하고 회원가입을 해보면 회원가입이 성공해 로그인페이지로 이동하는 것을 확인할 수 있다.
로그아웃
// LandingPage.js
import React, { useEffect } from 'react';
import axios from 'axios';
import { withRouter } from 'react-router-dom';
function LandingPage(props) {
useEffect(() => {
axios.get('/api/hello')
.then(response => { console.log(response.data) })
}, [])
const onClickHandler = () => {
axios.get('/api/users/logout')
.then(response => {
if(response.data.success) {
props.history.push('/login') //로그아웃 성공 시 LoginPage로 이동
}
else {
alert('Failed to logout')
}
})
}
return (
<div style={{
display: 'flex', justifyContent: 'center', alignItems: 'center', width: '100%', height: '100vh'
}}>
<h2>LandingPage</h2>
<button onClick={onClickHandler}>
Logout
</button>
</div>
)
}
export default withRouter(LandingPage)
실행시켜보면 다음과 같이 로그아웃 버튼이 생기고, Logout 버튼을 클릭하면 LoginPage로 이동하는 것을 확인할 수 있다.
Authentication Check
여러가지 페이지들을 다음 이미지와 같이 분류해 볼 수 있다.
사용자가 진입할 수 있는 페이지에 대한 통제는 HOC(Higher Order Component)를 사용해서 해결한다.
노드 리액트 기초 - 4th Day 게시글에서 잠시 나왔었던 이미지를 보면 이해가 쉬울 것이다.
https://sungeun97.tistory.com/21?category=1127827
Auth(HOC)에서 백엔드로 request를 보내 사용자의 상태를 받아온다.
받아온 사용자의 상태가 해당 페이지의 권한에 부합하면 진입을 허용하고, 그렇지 않다면 다른 페이지로 이동시킨다.
Authentication Check를 하기 위해 먼저 client>src>hoc 안에 auth.js라는 파일을 생성하여 다음 코드를 작성한다.
// auth.js
import React, { useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { auth } from '../_actions/user_action';
export default function (SpecificComponent, option, adminRoute = null) {
function AuthenticationCheck(props) {
const dispatch = useDispatch();
useEffect(() => {
dispatch(auth()).then(response => {
console.log(response)
})
}, [])
return (
<SpecificComponent />
)
}
return AuthenticationCheck
}
그리고 user_action.js, types.js, user_reducer.js에 다음 코드를 추가한다.
//user_action.js
import axios from 'axios';
import { LOGIN_USER, REGISTER_USER, AUTH_USER } from './types';
export function auth() {
const request = axios.get('/api/users/auth')
.then(response => reponse.data)
return { //reducer로 넘겨준다.
type: AUTH_USER,
payload: request
}
}
//types.js
export const AUTH_USER = "auth_user";
//user_reducer.js
import { LOGIN_USER, REGISTER_USER, AUTH_USER } from '../_actions/types';
export default function (state = {}, action) {
switch (action.type) {
case LOGIN_USER:
return { ...state, loginSuccess: action.payload }
break;
case REGISTER_USER:
return { ...state, register: action.payload }
break;
case AUTH_USER:
return { ...state, userData: action.payload }
break;
default:
return state;
}
}
각 페이지에 대한 Authentication Check를 해주기 위해 App.js에 아까 작성한 Auth를 import 하고, Router 안에 각 Route component를 Auth로 감싸준다.
그럼 각각 Auth(LandingPage, null), Auth(LoginPage, false), Auth(RegisterPage, false)가 되는데 이 의미는 아까 auth.js에서 export default function(SpecificComponent, option, adminRoute = null) 이라고 작성한 부분을 보면 각 페이지들이 SpecificComponent에 해당하고 null, false가 option에 해당한다.
이때 option은 3가지 값을 가질 수 있다.
1. null → 아무나 진입 가능
2. true → 로그인한 사용자만 진입 가능
3. false → 로그인한 사용자는 진입 불가
그렇기 때문에 LandingPage는 아무나 진입이 가능하기에 option에 null이 들어간 것이고, RegisterPage와 LoginPage는 로그인한 사용자는 진입할 수 없어야 해서 false가 들어간 것이다.
만약 로그인한 사용자만 들어가게 하고 싶은 페이지를 생성한다면 option으로 true를 넣어주면 된다.
그리고 adminRoute는 default값으로 null을 가져서 생략이 가능한데, 만약 관리자만 진입하게 하고 싶다면 3번째 매개변수 값으로 true를 넣어주면 된다.
ex) Auth(AdminPage, true, true) → AdminPage에 로그인한 관리자만 진입 가능
실행시킨 후 콘솔 창을 보면 다음과 같다.
먼저 로그인 하기 전에는 isAuth가 false로 나오는 것을 확인할 수 있다.
로그인을 하면 LandingPage로 이동하고 isAuth가 true로 바뀐 것을 확인할 수 있다.
그리고 다시 로그아웃을 하면 LoginPage로 이동하고 isAuth가 false로 바뀐 것을 확인할 수 있다.
이제 마지막으로 권한이 없는 사람이 해당 페이지에 접근했을 시에 막아주는 처리를 해야 한다.
다시 auth.js로 돌아와 다음 코드를 추가한다.
// auth.js
export default function (SpecificComponent, option, adminRoute = null) {
function AuthenticationCheck(props) {
const dispatch = useDispatch();
useEffect(() => {
dispatch(auth()).then(response => {
console.log(response)
//로그인 하지 않은 상태
if(!response.payload.isAuth) {
//option이 true인 페이지 접근 시
if(option) {
props.history.push('/login') //LoginPage로 이동
}
}
//로그인 한 상태
else {
//관리자가 아닌 사람이 AdminPage로 접근 시
if(adminRoute && !response.payload.isAdmin) {
props.history.push('/') //LandingPage로 이동
}
else {
//option이 false인 페이지 접근 시
if(option === false) {
props.history.push('/') //LandingPage로 이동
}
}
}
})
}, [])
return (
<SpecificComponent />
)
}
return AuthenticationCheck
}
다시 실행시켜서 로그인하여 LoginPage나 RegisterPage로 접근을 시도하면 LandingPage로 이동하는 것을 확인할 수 있다.
이로써 John Ahn님의 노드 리액트 기초 강의가 모두 끝났고, 이제 본격적으로 게시글 작성/삭제/수정, 파일 업로드/다운로드, 게시글 정렬, 회원정보 수정 등과 같은 기본적이며 필수적인 기능들을 개발하며 프로젝트를 진행한다.
처음 웹 개발 프로젝트를 진행하여 어려움이 컸는데 John Ahn님의 강의 덕분에 스타트를 잘 할 수 있었던 것 같다.
(뒤에 나올 게시글 작성, 게시글 정렬, 파일 업로드 부분도 John Ahn님의 Online Shop Clone 강의를 참고하여 개발한다. ㅋㅋ)
'Study > React & Node.js' 카테고리의 다른 글
[React] Could not resolve dependency error peer react@"^16.8.0 || ^17.0.0" from @material-ui/core@4.12.4 (0) | 2023.03.06 |
---|---|
노드 리액트 기초 - 7th Day (1) (0) | 2021.07.30 |
노드 리액트 기초 - 6th Day (0) | 2021.07.20 |
노드 리액트 기초 - 5th Day (0) | 2021.04.02 |
노드 리액트 기초 - 4th Day (2) | 2021.04.02 |