프로젝트 1 - 회원가입, 로그아웃, 인증체크

⭐프로젝트 1 - 회원가입, 로그아웃, 인증체크

📕회원가입

이번에는 회원가입 페이지를 만들어보자

앞에서 만든 로그인 페이지와 많은 부분이 동일하니 코드를 전부 복사해오자

이름, 이메일, 비밀번호, 비밀번호 확인 칸이 있어야 하니 입력칸을 두개 더 만들자

똑같이 state도 2개가 추가돼야한다

그리고 핸들러도 2개 더 만들어야 한다

  const  [Email, setEmail] = useState("")
  const  [Name, setName] = useState("")
  const  [Password, setPassword] = useState("")
  const  [ConfirmPassword, ConfirmsetPassword] = 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)
  }

        <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} />

나는 이렇게 추가하였다

전체 코드를 보자면 아래와 같다

import React, { useState } from 'react'
import { useDispatch } from 'react-redux'
import { useNavigate } from 'react-router-dom'
import { loginUser } from '../../../_actions/user_action'


function RegisterPage(props) {
  const navigate = useNavigate()
  const dispatch = useDispatch()
  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();
    
    let body = {
      email: Email,
      password: Password
    }

    dispatch(loginUser(body))
      .then(response => {
        if (response.payload.loginSuccess) {
          navigate('/')
        } else {
          alert('Error')
        }
    })
  }

  return (
    <div style={{
      display: 'flex', justifyContent: 'center', alignItems: 'center',
      width: '100%', height: '100vh'
    }}>
      <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 type="submit">
          Register
        </button>
      </form>
    </div>
  )
}

export default RegisterPage

이렇게 회원가입 페이지를 만들고 페이지로 들어가보면 아래처럼 나오는 것을 볼 수 있다

입력칸을 만드는 것 까지는 끝났고 이제 데이터가 서버로 넘어가도록 만들자

로그인 만들때 디스패치를 이용해서 한 것과 똑같이 하면된다

다른 점이라고 하면 body에는 이름까지 함께 넣어주어야 하기 때문에

name: Name을 추가하였다

그리고 비밀번호가 서로 같아야 버튼이 눌리게 변경해주어야 한다

    if(Password !== ConfirmPassword){
      return alert('비밀번호가 같지않습니다.')
    }

    let body = {
      email: Email,
      name: Name,
      password: Password
    }

SubminHandler 안에 위의 코드를 추가시켜 주었다

그리고 디스패치할때 loginUser의 액션을 받아 왔었다

회원가입 액션도 만들어야 하는데 액션의 이름은 signupUser로 만들었다

user_actions.js에서 추가로 만들면 된다

로그인액션과 비슷하기 때문에 카피한 다음 주소를 서버index.js에서

만든 라우터 주소로 바꿔주자

그리고 서버에서 리턴을 받을 때 타입을 SIGNUP_USER로 바꿔주고

type.js에도 추가시켜 주자

user_action.js

import axios from 'axios'
import { LOGIN_USER } from './types'
import { SIGNUP_USER } from './types'

export function loginUser(dataToSubmit) {
    const request = axios.post('/api/users/login', dataToSubmit)
    .then(response => response.data )

    return {
        type: LOGIN_USER,
        payload: request
    }
}

export function signupUser(dataToSubmit) {
    const request = axios.post('/api/users/signup', dataToSubmit)
    .then(response => response.data )

    return {
        type: SIGNUP_USER,
        payload: request
    }
}

types.js

export const LOGIN_USER = "login_user"
export const SIGNUP_USER = "signup_user"

그리고 리듀서로 가서 사인업 유저의 케이스일 때 리턴하는 경우를 작성하면 된다

user_reducer.js

import { LOGIN_USER, SIGNUP_USER } from '../_actions/types'

export default function (state = {}, action) {
    switch(action.type) {
        case LOGIN_USER:
            return { ...state, loginSuccess: action.payload }
            break;
        case SIGNUP_USER:
            return { ...state, signupSuccess: action.payload}
            break;
        default:
            return state;
    }
}

이렇게 하면 회원가입을 하는 액션까지 만들었다

이제 만든 액션을 이용해서 디스패치를 하면 결과가 나올 것이다

onSubminHandler 부분의 코드는 아래와 같다

  const onSubmitHandler = (event) => {
    event.preventDefault();

    if(Password !== ConfirmPassword){
      return alert('비밀번호가 같지않습니다.')
    }

    let body = {
      email: Email,
      name: Name,
      password: Password
    }

    dispatch(signupUser(body))
      .then(response => {
        if (response.payload.success) {
          navigate('/LoginPage')
        } else {
          alert('Error')
        }
    })
  }

서버를 열고 회원가입 라우트로 가서 작성을 하고 버튼을 눌러보자


이렇게 로그인 페이지로 이동되고 툴을 확인해서 보면 true로 리턴된 것을 알 수 있다

📘로그아웃

로그아웃 기능은 상당히 간단하다

다른 페이지를 만드는 것이 아니라 메인페이지에 버튼 하나만 만들면 된다

랜딩페이지에 버튼을 하나 추가시키고 버튼에다가 onclick이벤트를 주고 핸들러를 만들자

핸들러에 들어갈 내용은 위와 똑같이 디스패치이고 굉장히 간단하다

user_action.js에 logoutUser 함수를 추가하자

import { LOGOUT_USER } from './types'

export function logoutUser() {
    const request = axios.get('/api/users/logout')
    .then(response => response.data )

    return {
        type: LOGOUT_USER,
        payload: request
    }
}

types.js에 타입을 추가하자

export const LOGIN_USER = "login_user"
export const SIGNUP_USER = "signup_user"
export const LOGOUT_USER = "logout_user"

user_reducer.js에 케이스를 추가하자

import { LOGIN_USER, LOGOUT_USER, SIGNUP_USER } from '../_actions/types'

export default function (state = {}, action) {
    switch(action.type) {
        case LOGIN_USER:
            return { ...state, loginSuccess: action.payload }
            break;
        case SIGNUP_USER:
            return { ...state, signup: action.payload}
            break;
        case LOGOUT_USER:
            return { ...state, logoutSuccess: action.payload}
            break;
        default:
            return state;
    }
}

자 이렇게 하면 끝났다

이제 디스패치만 하면 끝난다

import React, { useEffect } from 'react'
import axios from 'axios'
import { useDispatch } from 'react-redux'
import { useNavigate } from 'react-router-dom'
import { logoutUser } from '../../../_actions/user_action'

function LandingPage() {
  const navigate = useNavigate()
  const dispatch = useDispatch()

  useEffect(() => {
    axios.get('/api/hello')
    .then(response => {console.log(response.data)})
  }, [])

  const onClickHandler = () => {
    dispatch(logoutUser())
      .then(response => {
        if (response.payload.LogoutSuccess) {
          navigate('/LoginPage')
        } else {
          alert('Error')
        }
    })
  }

  return (
    <div style={{display: 'flex', justifyContent: 'center', alignItems: 'center',
    width: '100%', height: '100vh'}}>
      <h2>
        시작 페이지
      </h2>

      <button  onClick={onClickHandler}>
        Logout
      </button>
    </div>
  )
}

export default LandingPage

이렇게 핸들러를 만들고 웹을 키고 로그인을 한뒤 버튼을 눌러보자!

이렇게 사진처럼 로그아웃이 되었고 로그인 라우트로 와졌다!

📗인증체크

예전에 말한것 처럼 auth기능이 필요한 이유는 인증이 된 유저와 되지 않은

유저가 사용할 수 있는 페이지에 차이가 있기 때문이다

예를 들어 이미 로그인을 했는데 또 로그인 페이지에 들어간다거나,

관리자만 들어갈 수 있는 곳같은 것이 있다

또한 파일전송, 업로드, 댓글 등 로그인 한 유저들만 사용가능한

기능들에도 제한을 두어야 한다

들어갈 수 있는 페이지들에 대한 통제는 HOC로 할 것이다

HOC ( Higher-Order Component )

hoc는 함수인데 다른 컴포넌트를 받고 새로운 컴포넌트를 리턴한다

hoc에 모든 컴포넌트들을 다 넣을 것이다

그리고 어떤 사용자가 페이지에 들어갈 때 마다 hoc에서 서버쪽으로

request를 보내서 상태를 받아온다

상태는 사용자가 로그인 되어있는지, 아닌지, 관리자인지 등을 의미한다

그리고 관리자가 아닌데 관리자 페이지로 들어가려고 한다면

hoc에서 못들어오게 막는것이다

hoc를 사용하기 위해 전에 만들어 놓은 hoc 폴더에 auth.js 파일을 만들자

그리고 서버쪽에 사용자의 상태를 물어보고 받아오는 함수를 먼저 만들자

useEffect를 이용하고 이미 서버에 auth api를 만들어 놓았기때문에

axios로 요청만 하면 된다

여기서 또 바로 axios 하지않고 리덕스를 사용해서 하도록 하자

이제는 간단하게 할 수 있다

user_action.js

import { AUTH } from './types'

export function auth() {
    const request = axios.get('/api/users/auth')
    .then(response => response.data )

    return {
        type: AUTH,
        payload: request
    }
}

types.js

export const LOGIN_USER = "login_user"
export const SIGNUP_USER = "signup_user"
export const LOGOUT_USER = "logout_user"
export const AUTH = "auth"

user_reducer.js

import { LOGIN_USER, LOGOUT_USER, SIGNUP_USER, AUTH } from '../_actions/types'

export default function (state = {}, action) {
    switch(action.type) {
        case LOGIN_USER:
            return { ...state, loginSuccess: action.payload }
            break;
        case SIGNUP_USER:
            return { ...state, signup: action.payload}
            break;
        case LOGOUT_USER:
            return { ...state, logoutSuccess: action.payload}
            break;
        case AUTH:
            return { ...state, userData: action.payload}
            break;
        default:
            return state;
    }
}

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 />
}

우리가 만든 함수를 보면 매개변수로 컴포넌트, 옵션, 어드민 3가지가 있다

컴포넌트는 hoc에 들어갈 컴포넌트이고 옵션은 3가지이다

null : 아무나 출입이 가능한 페이지

true : 로그인한 유저만 출입이 가능한 페이지

false : 로그인한 유저는 출입이 불가한 페이지

마지막 어드민은 관리자만 들어가게 하고싶다면 true를 붙여주면 된다

아래에서 hoc에 다른 컴포넌트를 넣으면서 이해해보자

app.js에 보면 Routes 부분에 모든 컴포넌트들이 있다

그걸 auth함수로 감싸면 hoc에 추가하는 것이다

import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom";
import Footer from './components/views/Footer/Footer'
import LandingPage from './components/views/LandingPage/LandingPage'
import LoginPage from './components/views/LoginPage/LoginPage'
import NavBar from './components/views/NavBar/NavBar'
import RegisterPage from './components/views/RegisterPage/RegisterPage'
import Auth from './hoc/auth'

function App() {
  return (
    <Router>
      <div>
        {/* A <Switch> looks through its children <Route>s and
            renders the first one that matches the current URL. */}
        <Routes>
          <Route path="/Footer" element={<Footer />} />
          <Route path="/login" element={Auth(LoginPage, false)} />
          <Route path="/NavBar" element={<NavBar />} />
          <Route path="/signup" element={Auth(RegisterPage, false)} />
          <Route path="/" element={Auth(LandingPage, null)} />
        </Routes>
      </div>
    </Router>
  );
}

export default App;

위와 같은 방식으로 하는 것이다

이렇게 해놓고 페이지를 켜보면 페이지를 이동할 때 마다 request를 주는 것이다

이렇게 처음 랜딩페이지에 들어왔을 때는 isAuth가 false로 나오는데

로그인을 하고 난뒤에는 isAuth가 true로 리턴되었다

이제 이 정보들과 옵션을 가지고 분기를 할 것이다

auth.js

import React, { useEffect } from "react";
import { useDispatch } from 'react-redux'
import { auth } from '../_actions/user_action'
import { useNavigate } from 'react-router-dom'

export default function (SpecificComponent, option, adminRoute = null) {
    function AuthenticationCheck(props) {
        const dispatch = useDispatch()
        const navigate = useNavigate()

        useEffect(() => {
            dispatch(auth())
                .then(response => {
                    console.log(response)
                    if (!response.payload.isAuth) {
                        //로그인 하지 않은 상태 
                        if (option === true) {
                            navigate('/login')
                            alert("로그인이 필요합니다.")
                        }
                    } else {
                        //로그인 한 상태
                        if (adminRoute && !response.payload.isAdmin) {
                            navigate('/')
                            alert("권한이 없습니다.")
                        } else {
                            if(option === false){
                                navigate('/')
                                alert("이미 로그인 하였습니다.")
                            }
                        }

                    }


                })
        }, [])

        return (
            <SpecificComponent />
        )
    }

    return <AuthenticationCheck />
}

이렇게 작성을 하고 사이트를 열고 테스트를 해보자

로그인을 한 뒤에 다시 로그인으로 들어가면 아래와 같이 뜨고 랜딩페이지로 넘어간다

좋은 웹페이지 즐겨찾기