성장과정(dev)/Frontend(feat. Vue, Next.js)

[react] HOC를 사용한 페이지별 접근인증

lowellSunny 2020. 11. 16. 11:42

* HOC(higher-order component)

> 특징 : 다른 컴포넌트를 받아 새로운 컴포넌트를 return하는 function

> Auth라는 컴포넌트(HOC) 내에 다른 컴포넌트들을 삽입 가능

const EnhancedComponent = higherOrderComponent ( WrapperdComponent );

 

* HOC 적용방법

1. front 폴더 아래 hoc 폴더 auth.js 생성

import react, {useEffect} from 'react';
import Axios from "axios";
import {useDispatch} from "react-redux";
import { auth } from '../_actions/user_action';

/**
 *
 * @param specificComponent - 감쌀 컴포넌트
 * @param option            - null: 아무나 출인이 가능한 페이지, true: 로그인한 유저만 출입가능, false: 로그인한 유저는 접속불가능항 페이지
 * @param adminRoute        - 관리자만 접속할 수 있도록 하는 옵션
 * @returns {*}
 */
export default function( SpecificComponent, option, adminRoute = null ) {

    function AuthenticationCheck( props ) {

        const dispatch = useDispatch();

        useEffect( () => {


            dispatch( auth() ).then( response => {
               if( !response.payload.isAuth ) {
                   if( option ) {
                       props.history.push( '/login' )
                   }
               }
               else {
                    if( adminRoute && !response.payload.isAdmin ) {
                        props.history.push( '/' )
                    }
                    else {
                        props.history.push( '/' )
                    }
               }
            } )

            //Axios.get( "/api/users/auth")
        }, [] )

        return (
            <SpecificComponent/>

        )
    }

    return AuthenticationCheck
}

2. action에 auth 추가 (파일명 user_action.js)

//get 메소드는 body 부분이 필요하지 않음
export function auth() {

    const request = axios.get('/api/user/auth' )
        .then( response => response.data )

    //  return을 시켜 Reducer로 보내줌.
    return {
        type: AUTH_USER,
        payload: request
    }
}

3. action에서 호출할 user_reducer 추가

export default function( state = {}, action ) {

    //  정의 된 타입이 많아질 것이기 때문에 switch문을 사용
    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;
    }
}

 

4. App.js 에 생성한 auth import 한 후 컴포넌트들을 Auth 로 감싸준다.

import React from "react";
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link
} from "react-router-dom";

import LandingPage from "./components/views/LandingPage/LandingPage";
import LoginPage from "./components/views/LoginPage/LoginPage";
import RegisterPage from "./components/views/RegisterPage/RegisterPage";
import Auth from './hoc/auth';

function App() {
  return (
      <Router>
        <div>

          <hr />

          {/*
          A <Switch> looks through all its children <Route>
          elements and renders the first one whose path
          matches the current URL. Use a <Switch> any time
          you have multiple routes, but you want only one
          of them to render at a time
        */}
          <Switch>
            <Route exact path="/" component={Auth( LandingPage, null, true)}/>
            <Route exact path="/login" component={Auth(LoginPage, false)}/>
            <Route exact path="/register" component={Auth(RegisterPage, false)}/>
          </Switch>
        </div>
      </Router>
  );
}

export default App;


 

* props.history.push( "페이지url" )

가리키는 페이지로 넘어가게 하기 위해서 props.history.push 해당 이벤트를 사용하는데

이것을 사용하려면 필요한 라이브러리가 있다.

import {withRouter} from 'react-router-dom';

여기서 withRouter는 주로 history에 접근하여 컴포넌트에서 라우터를 조작하는 데 사용

해당 라이브러리를 사용하지 않으면 아래와 같은 에러가 발생한다.

LoginPage.js:36 Uncaught (in promise) TypeError: Cannot read property 'push' of undefined

 

 

 

 

 

==========================================

※추가정보

해당 코드에서 세미콜론을 붙이고 안붙이고의 차이는 딱히 없다. 웬만하면 끝에 세미콜론을 붙여주는 것 추천