Back

react 로그인 상태관리

리액트 프로젝트 내에서 로그인 상태를 redux로 관리하고, 로그인이 된 상태에서만 볼 수 있는 route, 로그인이 되지 않은 상태에서만 볼 수 있는 route를 구현했다. react router의 props 중 보여줄 element를 한단계 더 감싸서 감싼 컴포넌트에서 redux store의 값을 통해 인증이 되어있는지 아닌지 여부를 판단한다. 이는 react router 공식문서에서 auth 관련한 예제를 따라했었다. 여기에서도 인증이 필요한 router는 컴포넌트로 감싸며, context api를 사용하여 해당 값의 인증 여부에 따라 라우팅을 해줬다. 나는 context api대신 redux를 사용한 것이다.

인증이 되어있으면, children component를 그대로 return 해주도록 하고, 그렇지 않으면 react-router-dom의 Navigate를 통해 내가 원하는 곳으로 이동하게 return 해줬다.

기존에는 store 속 로그인과 관련한 initialState는 false 였다. (초기 값은 로그인이 되어있지 않은 상태로 되어야하기 때문에)

그리고 app.js 내부 어떤 컴포넌트가 실행될 경우 useEffect를 통해 마운트 시점에 로그인 인증 상태 검사를 하여 로그인 상태를 store에 dispatch 해줬다.

잘 동작했다. 하지만, 처음 렌더링이 된 이후 모든 검사가 완료된 store 안에서만 잘 동작했다.

브라우저의 주소창에 바로 인증이 필요한 주소로 접속하였을 때 로그인 인증이 되어있는 상황에서도 항상 로그인이 되어있지 않은 상태로 판단하여 보이지 않았다.

이당시 로그인을 했을 때 보이고싶지 않은 별도의 컴포넌트도 만들었었는데, 이 경우에는 더 심각했다. 로그인이 되어있는 상태로만 볼 수 있는 화면같은 경우에는 어떤 경우에도 인증되지 않은 사용자라면 무조건 보이지 않기라도 했으나, 로그인이 되어있지 않을 때만 보이고 싶은 화면은 브라우저 주소로 바로 접근하면 보인 뒤 예외처리로 진행했던 alert 창이 떴다.

처음에 상태검사를 하게될 때 필요한 store의 초기값으로부터 렌더링이 시작이 된다는 생각을 했다. 그리고, 실행 순서를 다시 생각도 안 해보고 dispatch가 setState처럼 비동기로 작동을 하는 것인가라는 의문을 품었다.

→ dispatch 쪽에서 디버깅을 해보니 의외로 그렇지 않았다. 그냥 바로 store의 값이 바뀌는 것을 확인했다.

store의 initial state를 통해 전체 렌더링을 하고, 렌더링 된 이후 최상위 app.js 컴포넌트의 useEffect를 통해 로그인 인증 상태 검사를 한 뒤 다시 그려주느라 그렇구나 추측이 되었다.

직접 debugger를 도배하여 확인을 해보니, 확실하게 어떤 화면이 마운트 되기도 전에 즉, 유저인증 상태를 검사하기도 전에 새로감싼 컴포넌트가 로그인 상태를 확인하여 어떤 컴포넌트를 리턴할지가 먼저 실행이 되었다.

파블로프의 개마냥 바로 구글에 나처럼 redux store에 로그인 상태 관련한 것을 사용한 사람들이 있는지 찾아보았으나, 의외로 원하는 답을 찾지 못했다.

갑자기 드는 생각인데 다른 사람들은 어떻게 하는 것일까?

비동기처리가 필요한 별도의 요청 가능 여부 상태를 확인하는 작업 없이 바로 확인하는 어떤 방법이 있는가?

혹시 브라우저 스토리지만으로만 로그인 상태를 관리하며, 어떤 이유로든 세션이 만료되면 요청 중간에 오류 응답을 받는 경우의 예외처리를 통해 빼는가?

백엔드 세션 만료시간도 존재하는데 이런 것을 생각해보면 쿠키에서 시간을 동일하게 설정하고 관리하는가?

잘 모르겠다.

순간 애초에 redux toolkit createSlice에서의 initialState부터 검사를 하면 되지 않을까 생각했다. 백엔드에 실제로 유저인증이 이루어진 뒤 보낼 수 있는 요청을 보내보고 이 응답값을 통해 판단을 해야했기 때문에 비동기로 처리해야했다. 이런식으로 했다.

export const userStateSlice = createSlice({
  name: 'isAuthenticated',
  initialState: {
    value: false,
  },
  reducers: {
    login: (state) => {
      state.value = true;
    },
    logout: (state) => {
      state.value = false;
    },
  },
});
export const userStateSlice = createSlice({
  name: 'isAuthenticated',
  initialState: {
    value: (async () => {
      let result;
      try {
        result = await req(); //인증된 사용자만 정상적인 응답을 받을 수 있는 요청
      } catch (e) {}
      return result !== undefined;
    })(),
  },
  reducers: {
    login: (state) => {
      state.value = true;
    },
    logout: (state) => {
      state.value = false;
    },
  },
});

렌더링이 이루어지기 전에 먼저 진행되는 initialState가 비동기처리가 된 return을 주면 삐까번쩍한 최신 redux toolkit 형님이 알아서 센스있게 값을 반환받기 전까지 기다렸다가 렌더링하지 않을까 희망을 품었다.

하지만 이렇게하니, 해당 store값을 useSelector를 통해 받아오는 컴포넌트에서 요청을 통해 받아온 결과값을 가져오는 것이 아니라, 그냥 promise 값을 받아버린다.


원점으로 돌아가 간단하게 생각을 하게 되었다.

렌더링이 되기 전에 무조건 처리해야 하는 예제는 많지 않을까?

useEffect만으로는 컴포넌트가 렌더링 되기 전에 (componentWillMount) 어떤 것을 할 수 없다.

하지만 나처럼 이런 시점에 원하는 코드를 먼저 실행하고싶었던 사람들이 수도없이 많을 것이라는게 직감되었다. 이런 해결 방법도 없이 hooks가 계속 사용된다는 것은 말이 안된다고 생각했다. 결국 여러 검색을 통해 위에처럼 조건부 렌더링을 useState를 통해 만들고 useEffect의 어떤 처리가 완료된 이후에 렌더링을 하도록 하는 예제를 확인했다.

Run Code in React Before Render

위의 예제처럼 간단하게 조건부 렌더링을 활용하여 최상단 App에서 특정 하위 컴포넌트는 로그인 상태를 처리하는 행동이 완료된 이후 렌더링하도록 변경했다.

아주 잘 동작한다. 그리고 또한 로그인 인증 상태에서 보이고싶지 않은 화면을 감싸는 컴포넌트 역시 잘 동작한다.

앞으로 componentWillMount와 동일한 진행을 꼭 해야 하는 경우가 생기면 위의 예제를 빠르게 떠올려 전처럼 빙빙 돌아가는 일이 없도록 해야겠다.