본문 바로가기

STUDY/React

React | JWT 안전하게 저장하기 (localStorage 사용 X) (2)

Home 컴포넌트 작성

Home.jsx/경로 (기본 경로)로 접근하면 나타나는 페이지다. 일단 /login/hello링크를 생성했다.

  • /login은 인증되지 않아도 접근이 가능하다
  • /hello는 인증된 상태여야만 접근이 가능하다 -> PrivateRoute처리하여 인증되지 않은 상태라면 /login으로 바로 이동시킴
import React from 'react';
import { Link } from 'react-router-dom';

export default function Home() {
  return (
    <div className="home-wrap">
      <h1>Home Sweet Home 🏠</h1>
      <Link to="/login">로그인</Link>
      <Link to="/hello">여긴 로그인을 해야 갈 수 있어요</Link>
    </div>
  );
}

지금은 로그인하지 않았기 때문에 (authenticated = false) 둘 중 어떤 것을 눌러도 모두 로그인 페이지로 넘어가게 된다.

Home

Login 컴포넌트 작성

이제 로그인 페이지를 만들어 본다.

  • useState를 이용해 input 값 관리
  • App.js에서 전달받은 handleLogin메서드를 로그인 버튼이 클릭되면 실행
import React, { useState } from 'react';

function Login({ handleLogin }) {
  const [input, setInput] = useState({});

  function handleOnChage(e) {
    setInput({ ...input, [e.target.name]: e.target.value });
  }

  function handleSubmit(e) {
    e.preventDefault();
    handleLogin(input);
  }

  return (
      <div>
        <form>
            <input type="text" name="id" onChange={handleOnChange} />
            <input type="password" name="password" onChange={handleOnChange} />
            <button type="submit" onClick={handleSubmit}>LOGIN</button>
        </form>
    </div>
  )
}

아이디와 비밀번호를 알맞게 입력한 뒤 로그인 버튼을 클릭하면, App.js에서 auht.login()메서드와 dispatch가 실행되고,
로그인에 성공하면 /hello로 이동해야한다!

PrivateRoute설정을 해주었기 때문에, /hello경로로 인증되지 않은 회원은 접근할 수 없다.
로그인에 성공해 인증을 완료하면 /hello로 redirect되도록 설정해둔 상태(PublicRoute로 감싸놨음. 사실 이건 안 해도 된다..)

대충 된다는 뜻..^^

문제 발생

일단 localStorage를 사용하지 않기 때문에 발생하는 문제들이 있다.

  • 로그인 후 새로 탭을 열면 또다시 로그인 해야 한다
  • 탭이 여러 개인 상태에서 다른 탭에서 로그아웃해도 다른 탭은 여전히 로그인 상태

일단 다른 탭에서 로그아웃 했을 때 처리부터 해보기로 한다.


가이드에서는 로그아웃을 할 때, localStorage에 로그아웃 기록을 남긴다.
그리고 storage의 이벤트를 감지해서 다른 탭에서도 로그아웃이 되도록 한다. (스토리지에 이벤트 리스너를 추가할 수 있는지도 몰랐는데...!)

auth.js 수정

localStorage에 로그아웃을 했다는 것을 기록하는 logout()메서드를 만든다.

export function logout() {
  console.log('localStorage set logout!');
  window.localStorage.setItem('logout', Date.now());
}

App.js 수정

리듀서에 DELETE_TOKEN을 추가

function reducer(state, action) {
  switch (action.type) {
    case 'SET_TOKEN':
      return { ...state, token: action.token, authenticated: action.result };
    case 'DELETE_TOKEN':
      return { ...state, token: null, authenticated: false };
    default:
      return state;
  }
}

handleLogout메서드를 작성한다. dispatchauth.logout()을 실행
그리고 useEffect에서 이벤트 리스너를 추가해 다른 탭에서 로그아웃 한 것을 감지한 뒤, dispatch를 실행한다.

  const handleLogout = () => {
    dispatch({
      type: 'DELETE_TOKEN',
    });
    auth.logout();
  };

  useEffect(() => {
    window.addEventListener('storage', (e) => {
      if (e.key === 'logout') {
        console.log('로그아웃 감지');
        dispatch({
          type: 'DELETE_TOKEN',
        });
      }
    });
  }, []);

 

그리고 handleLogoutHello컴포넌트로 전달

<PrivateRoute
     path="/hello"
     authenticated={authenticated}
     component={(props) => (
       <Hello {...props} handleLogout={handleLogout} />
     )}
/>

Hello.jsx 수정

이 컴포넌트는 인증된 회원만 접근할 수 있는 곳이므로 이곳에 로그아웃 버튼을 만든다.

import React from 'react';

export default function Hello({ handleLogout }) {
  return (
    <div className="hello-wrap">
      <h1>로그인 성공! 🎉</h1>
      <button type="button" onClick={handleLogout}>
        LOGOUT
      </button>
    </div>
  );
}

 

이제 다른 탭에서 로그아웃을 해도 동시에 모든 탭이 함께 로그아웃 된다!

이해 안 되는 부분은 언제든지 댓글 남겨주세요..