본문 바로가기

React를 시작해보자

7) React - JWT를 이용한 로그인 인증 - 3부

반응형

앞에서 간단하게 로그인해서 access token을 받아오고 받아온 access token을 request header에 저장하는것 까지 

공부를 했다.

 

이제 access token의 갱신을 어떻게 하는지에 대해 공부하자.

 

먼저 App.jsx 파일을 수정하자. App.jsx 가  최초 render 될 떄 refresh token을 체크하고 access token을 갱신하도록했는데. 이부분을  utils.js 파일로 옴기도록 하자 이유는 이 refresh token 하는 부분을 로그인 할떄도 사용하기 위해서이다.

로그인을 한 후에도 주기적으로 access token이 갱신되어야 하므로... 

 

// App.jsx

import './App.css';
import { BrowserRouter, Route } from 'react-router-dom';
import LoginPage from './pages/LoginPage';
import HomePage from './pages/HomePage';
import AuthRoute from './component/AuthRoute';
import Counnter from './component/Counter';
import { useEffect, useState } from 'react';
import { refreshToken } from './utils';

function App() {
  const [isLogin , setIsLogin] = useState(false);
  const [loading , setLoading] = useState(false);

  useEffect(()=>{
    try{
        refreshToken(loginCallBack);
    }catch(e){
        console.log(e);
    }
  },[]);

  function loginCallBack(login){
    setIsLogin(login);
    setLoading(true);
  }

  if(loading){
    return (
      <div className="App">      
          <BrowserRouter>
            <AuthRoute exact isLogin={isLogin} path="/" component={HomePage} />
            <Route path="/login"  render={(props)=> <LoginPage {...props} loginCallBack={loginCallBack}/>} />
            <AuthRoute path="/counter" isLogin={isLogin} component={Counnter} />
          </BrowserRouter>
  
      </div>
    );
  }else{
    return (
      <div>
        Loading ....
      </div>
    ) 
  }
}

export default App;
//utils.js

import axios from 'axios';

export const refreshToken = function(callback){
    axios.post("/auth/refreshToken" , {
        headers: {
          "Content-Type": 'application/json',
        }})
    .then(res =>{
        console.log("res.data.accessToken : " + res.data);
        axios.defaults.headers.common['Authorization'] = 'Bearer ' + res.data;
        if(callback){
            callback(true);
        }

        setTimeout(function(){
            refreshToken(null) ;
        }, (60 * 1000));
        
    })
    .catch(ex=>{
        console.log("app silent requset fail : " + ex);
        if(callback){
            callback(false);
        }
    })
    .finally(()=>{
      console.log("refresh token request end");
    //   setLoading(true);

    });
};

이부분을 추가 하면 이제 silent 로그인시에 자동으로 access token을 갱신하게 된다. 이유는 setTimeout 부분을 추가했기 때문이다.

 

이제 로그인부분도 수정해보자.

import { useEffect } from "react";
import axios from "axios";
import { refreshToken } from './../utils';

function LoginPage(props){
    
    function joinHandler(){
        try{
            let data = {email: "devracoon@naver.com"};
            axios.post("/auth/login" ,JSON.stringify(data), {
                headers: {
                  "Content-Type": 'application/json',
                }})
            .then(res =>{
                console.log("res.data.accessToken : " + res.data);
                axios.defaults.headers.common['Authorization'] = 'Bearer ' + res.data;
                props.loginCallBack(true);
                props.history.push("/");
                //이부분 추가함 60초 뒤에 refresh token이 실행되도록 수정.
                //이후 refresh token에서 자동으로 setTimeout이 발생해 주기적으로 access Token이 갱신됨
                setTimeout(function(){
                    refreshToken(null);
                } , 60 * 1000);
            })
            .catch(ex=>{
                console.log("login requset fail : " + ex);
            })
            .finally(()=>{console.log("login request end")});
        }catch(e){
            console.log(e);
        }  
    }

    useEffect(()=>{
        console.log("LoginPage render ... ");
    })
    return(
        <div>
            <span>Login Page</span>
            
            <button type="button" onClick={joinHandler}>Join</button>
        </div>
    )
}

export default LoginPage;

이렇게 처리를 하고 나면 access token의 갱신은 자동으로 이뤄지게 된다. 

그러면 refresh token이 만료되었을때는 다시 로그인을 해야 하는데 그럴때 마다 axios request 보낼때 체크를 하나하나 다 해야 하나? 아니다. axios에서도 interceptor가 존재한다. 

axios interceptor를 index.js 파일에 추가하자.

// index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import axios from 'axios';

axios.interceptors.response.use(
  function (response) {
  /*
      http status가 200인 경우
      응답 성공 직전 호출됩니다. 
      .then() 으로 이어집니다.
  */
    return response;
  },
  function (error) {
  /*
      http status가 200이 아닌 경우
      응답 에러 직전 호출됩니다.
      .catch() 으로 이어집니다.    
  */
    //401은 Access Token or Refresh Token 이 invalid 될때
    //response data의 code값이 
    // 4401 : access Token error , 4402: refresh Token error
    if(error.response.status === 401){
      if(error.response.data.code === '4401'){
        window.location.href= '/'; 
      }
    }
    return Promise.reject(error);
  }
);

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

 

이렇게 하고나면 token 처리에 대한 부분은 끝난다. 이제 본격적으로 UI 개발을 시작해보자 .

UI는 material ui template를 이용해서 좀 더 쉽게 개발하려고 한다.