[노드, 리액트 기초 | React] #26. 인증 체크 기능 구현

ppmyor·2022년 7월 25일
1

노드, 리액트 기초

목록 보기
26/26
post-thumbnail

마지막으로 인증 체크 기능을 구현한다!

🙋‍♀️ 인증 체크 왜 필요한가요!

여러가지 페이지들은 아래 등으로 구성될 수 있다.

  • 아무나 진입이 가능한 페이지
  • 로그인한 회원만 진입이 가능한 페이지
  • 로그인 한 회원은 진입을 못하는 페이지
  • 관리자만 진입 가능한 페이지

이런 페이지 접근에 대한 인증 부분들도 컨트롤 해주어야 한다.
물론 이것보다 user의 role에 따라 이것보다 더 많은 페이지가 있을 수 있고, 페이지 뿐만 아니라 댓글 작성, 파일 전송, 파일 업로드 등등에도 끊임없이 인증 체크 부분이 필요하다.
해당 글에서는 위의 기능들을 인증하기 위해 인증체크 기능을 구현하고, 들어갈 수 있는 페이지들에 대한 통제는 HOC 에서 진행한다.

🙋‍♀️ HOC

HOC의 정의에 대해서는 앞의 글에서 짧게 설명한 적이 있으므로 넘어가고 어떻게 구성되어있는지에 대해 설명하겠다.
HOC는 function으로 다른 컴포넌트를 받은 다음 새로운 컴포넌트를 return한다.

const EnhancedComponent = higherOrderComponent(WrappedComponent);

위의 코드를 보면 hoc에 다른 컴포넌트를 넣어서 새로운 컴포넌트를 return 하는 것을 볼 수 있다.

우리는 인증 체크를 구현하려 하므로 Auth Component(HOC)를 만든다. 해당 Component에는 다른 Component를 집어넣어 새로운 Component를 만들어준다.
우리는 만들어둔 LoginPage, LandingPage, RegisterPage 들을 모두 Auth Component에 넣는다!

🔁 Auth 흐름

이렇게 HOC에 넣으면 어떤 일이 벌어지는지 흐름을 알기 위해 예시를 들어보자.
LandingPage를 Auth Component에 넣었다고 가정한다.
1. Auth 라는 HOC Component에서 백엔드에 request를 날린다.
2. 현재 LandingPage에 들어와있는 사람의 상태 정보(로그인이 되어있는지에 대한 여부, admin인지 등)을 가져온다.
3. 가지고 온 정보를 토대로 페이지에 접근할 수 있는지의 여부를 결정한다.

인증 체크 기능 구현

App.js 수정

import Auth from "./hoc/auth";

function App() {
  const AuthLandingPage = Auth(LandingPage, null);
  const AuthLoginPage = Auth(LoginPage, false);
  const AuthRegisterPage = Auth(RegisterPage, false);

  return (
    <Router>
      <div>
        <Routes>
          <Route path="/" element={<AuthLandingPage />} />
          <Route path="/login" element={<AuthLoginPage />} />
          <Route path="/register" element={<AuthRegisterPage />} />
        </Routes>
      </div>
    </Router>
  );
}
  1. 모든 페이지 컴포넌트를 Auth로 감싸줌.

  2. Auth의 parameter 설명

  • SpecificComponent: 감싸질 컴포넌트
  • option:
    - null: 아무나 출입이 가능한 페이지
    - true: 로그인한 유저만 출입이 가능한 페이지
    - false: 로그인한 유저는 출입 불가능한 페이지
  • adminRoute:
    - true: admin 유저만 들어갈 수 있는 곳이면 true
    - null: 아니면 null
  1. LandingPage는 아무나 들어갈 수 있기 때문에 option에 null 할당

  2. LoginPage와 RegisterPage는 로그인 한 유저는 출입 불가하기 때문에 option에 false 할당

hoc/auth.js

function(SpecificComponent, option, adminRoute = null){

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

function Auth(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) {
            navigate("/login");
          }
        } else {
          // 로그인 한 상태
          if (adminRoute && !response.payload.isAdmin) {
            navigate("/");
          } else {
            if (!option) {
              navigate("/");
            }
          }
        }
      });
    }, []);

    return <SpecificComponent />;
  }
  return AuthenticationCheck;
}

export default Auth;
  1. Auth function 생성 후 parameter로 SpecificComponent, option, adminRoute = null 를 받음

  2. AuthenticationCheck function 생성

  • backend의 request를 날려 유저의 현재 상태를 가져오기 위해 useEffect 사용
    - redux를 사용하기 때문에 dispatch를 통해 action인 user_action의 auth로 보내어줌
  1. response정보를 기반으로 분기 처리

3-1. 로그인 하지 않은 상태인 경우
response.payload.isAuth가 false이면 로그인 하지 않은 상태

  • 로그인한 유저만 접근 가능한 페이지에 접근하려고 하는 경우
    • option이 true인 페이지를 접근 하려고 할 때 못가도록 navigate에서 login으로 이동시켜버림

3-2. 로그인 한 상태인 경우
response.payload.isAuth가 true이면 로그인 한 상태

  • admin이 아닌 유저가 adminPage에 접근하려고 하는 경우
    - adminRoute가 true인 페이지를 접근하려고 하는데 해당 유저는 Admin이 아닌 경우 navigate에서 LandingPage로 이동시켜 버림
  • 로그인한 유저가 출입 불가능한 페이지에 접근하려고 하는 경우
    - option이 false인 페이지를 접근하려고 하는 경우 navigate에서 LandingPage로 이동시켜 버림
  1. 마지막에 SpecificComponent를 return 해줌

_actions/user_action.js 수정

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

  return {
    type: AUTH_USER,
    payload: request,
  };
}
  1. auth function 생성

  2. axios를 통해 request 진행

  • get method이기 때문에 body 부분은 필요 없음
  1. 서버에서 받은 data를 request 변수에 저장
    return을 시켜 reducer로 보냄

  2. type 이름은 AUTH_USER

_actions/types.js 수정

export const AUTH_USER = "auth_user";

AUTH_USER을 추가해준다.

_reducers/user_reducer.js 수정

import { LOGIN_USER, REGISTER_USER } from "../_actions/types";

const userReducer = (state = {}, action) => {
  switch (action.type) {
    case AUTH_USER:
      return { ...state, userData: action.payload };
    default:
      return state;
  }
};

export default userReducer;
  1. AUTH_USER case 추가
  2. 다음 state를 return
    userData에 해당 action의 payload를 넣어줌
  • 서버의 auth 부분에서 모든 것을 처리하고 난 다음 user 정보를 client에 전해주고 있기 때문에 aciont.payload에 모든 user의 정보가 포함되어있음.

🥲 문제

오늘도 어김없이 문제가 발생한다.

Warning: Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it.

react-router-dom v6부터 component가 아닌 element를 사용해야하는데, auth로 감쌀 때 문제가 발생한다.

import Auth from "./hoc/auth";

function App() {
  return (
    <Router>
      <div>
        <Routes>
          <Route path="/" element={ Auth(LandingPage, null) } />
          <Route path="/login" element={ Auth(LoginPage, false) } />
          <Route path="/register" element={ Auth(RegisterPage, false) } />
        </Routes>
      </div>
    </Router>
  );
}

hoc는 function이고 함수는 element에는 컴포넌트만 들어갈 수 있기 때문에 함수는 들어갈 수 없어 발생하는 에러이다.

import Auth from "./hoc/auth";

function App() {
  const AuthLandingPage = Auth(LandingPage, null);
  const AuthLoginPage = Auth(LoginPage, false);
  const AuthRegisterPage = Auth(RegisterPage, false);

  return (
    <Router>
      <div>
        <Routes>
          <Route path="/" element={<AuthLandingPage />} />
          <Route path="/login" element={<AuthLoginPage />} />
          <Route path="/register" element={<AuthRegisterPage />} />
        </Routes>
      </div>
    </Router>
  );
}

그래서 이렇게 변수를 선언한 뒤 컴포넌트와 option들을 auth로 감싸 할당해준다. auth로 감싼 이후 나오는 값은 specificComponent이므로 해당 변수는 Component이고 요 변수를 그대로 element에 넣어주어 해결한다!

대장정이 끝이 났다면 오산이고.. 그냥 기본 기능을 배워보자! 인 기초 강의라 쇼핑몰 사이트 만들려면 다시 바닥부터 시작이다.🥲
그래도 기초 강의 들으면서 노드에 대해 조금 알게되었고, 해본적 없던 기능 구현에 대해 배우면서 아 나 정말 아무것도 모르는 우물 안 개구리였구나를 느꼈다. 배웠던 redux도 기능 구현도 기초적인 부분이라지만 가장 코어한 부분이니 잘 익혀두자.🔥

➕ 참고

따라하며 배우는 노드, 리액트 시리즈 - 기본 강의 를 공부하며 작성한 글입니다.

profile
유영하는 개발자

0개의 댓글