STUDY/React

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

개미606 2021. 5. 27. 16:50

JWT는 어디에 저장해야 할까

이전 글에서 알아 보았듯, WebStorage(LocalStorage 혹은 SessionStorage)에 저장하면 안 된다!

 

그러다가 이 가이드를 발견했고, 이대로 따라해보기로 했다.

 

가이드는 아래와 같은 방식으로 구현된다.

  • 서버는 GraphQL이다. http통신으로 JWT를 받는다.
  • LocalStorageJWT를 저장하지 않는다. (중요)
  • JWTIn Memory에 저장한다. -> 이 가이드는 in memory방식의 문제점을 해결해준다..! (매우 중요)
  • 서버에서 refresh_token을 제공해야 한다. (제일 중요)
  • 탭을 새로 생성해도, 다른 탭에서 로그아웃을 해도 정상적으로 처리되도록 만든다!

그리고 이 가이드와 내가 한 방식의 다른점..!

  • 서버가 없음 -> 서버까지 구현하기 귀찮았다..😥 -> http 통신은 생략한다!
  • React를 사용할 것임 -> 그리고 useReducer hook도 사용한다 - 이건 자유.. 쓰고싶은대로 쓰면 된다 사실..

React 프로젝트 생성

create-react-app을 이용한다.

$ create-react-app jwt-inmemory-test

그리고 필요한.. 모듈을 install한다. 일단 리액트 라우터를 사용하기 위해..

$ npm install react-router-dom

컴포넌트들 생성

만들고 싶은 대로 만든다..

  • Login.jsx : 로그인 하는 페이지
  • Home.jsx : 로그인을 하지 않아도 접근할 수 있는 페이지
  • Hello.jsx : 로그인을 해야만 접근할 수 있는 페이지

auth.js 작성

만들고 싶은 위치에 auth.js파일을 생성한다.
이 파일 내에 인증관련된 메서드들을 작성할 것이다!
일단 login메서드를 만든다. id와 password를 받아 일치하면 멋진.. 토큰을 반환한다.

export function login({ id, password }) {
  if (id === 'russ' && password === 'whynot0') {
    return {
      access_token: 'jx84e3kjew1njej3al2q9w',
      refresh_token: 'g2rjfd7452bjfgn;a&*(jkehj',
    };
  } else {
    return undefined;
  }
}

App.js에서 useReducer로 인증 접근 처리

나는 PrivateRoute를 만들어서 처리했는데, 그냥 state값으로 바로 처리해도 될 것 같다.

* Private Route관련 글


initialState는 말그대로 기본 state다.
authenticated값은 인증 여부를 boolean값으로 담고, token에는 인증 후 받은 토큰을 저장할 곳이다.

const initialState = {
    authenticated: false,
    token: null
}

reducer도 작성해준다. SET_TOKEN이라는 type을 가진 dispatch가 실행되면, state를 변경한다.

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

App.js를 마저 작성하면.. 이렇다.
handleLogin메서드를 Login컴포넌트에 props로 전달한다.

// 이 부분에 initailState와 reducer를 작성

function App() {
  const [state, dispatch] = useReducer(reducer, initialState);
  const { authenticated } = state;

  function handleLogin(id, password) {
    let token = auth.login(id, password);

    if (token) {
      console.log('로그인 성공!');
      dispatch({
        type: 'SET_TOKEN',
        token: token,
        result: true,
      });
    } else {
      console.log('로그인 실패');
      dispatch({
        type: 'SET_TOKEN',
        token: null,
        result: false,
      });
    }
  }

  return (
    <Router>
      <Switch>
        <Route exact path="/" render={Home} />
        <PublicRoute
          path="/login"
          authenticated={authenticated}
          compoenent={(props) => <Login {...props} handleLogin={handleLogin} />}
        />
        <PrivateRoute
          path="/hello"
          authenticated={authenticated}
          component={(props) => (
            <Hello {...props} />
          )}
        />
      </Switch>
    </Router>
  );
}

export default App;

 

갑자기 힘들어져서 다음에 이어서...