[React]로그인 상태에 따른 Navigation Bar 상태 변화(2)

해결책

1단계: Login.js 파일에서 슈크림 서비스의 토큰을 받아와서 전역변수로 관리를 한다.
2단계: 그것을 Nav.js에서 검사를 한다.
3단계: 이 때 Login에 있는 token과 Nav에 있는 token이 같아야 하기 때문에 Recoil을 써주는 것이다.

결국 이 논리대로 하려면 RecoilRoot부터 다시 감싸줘야 했다.
우선, login 파일도 같이 감싸줘서 recoil 상태를 사용할 수 있게끔 해야한다.

[Routes.js]

const Router = () => {
  return (
    <BrowserRouter>
      <RecoilRoot>
        <Nav />
        <Routes>
          <Route path="/" element={<Main />} />
          <Route path="/products/:id" element={<ProductDetail />} />
          <Route path="/login" element={<Login />} />
        </Routes>
      </RecoilRoot>
      <Footer />
    </BrowserRouter>
  );
};

그리고 Nav에서 감지를 하고 변화를 줘야 하기 때문에 nav도 RecoilRoot로 감싸 준다.

[Nav.js 파일]

  return (
      <NavUl>
        <NavLi>고객센터</NavLi>
        <NavLi>관심상품</NavLi>
        <NavLi>마이페이지</NavLi>
        {token ? (
          <NavLi onClick={logoutToken}>로그아웃</NavLi>
        ) : (
          <NavLi onClick={() => navigate('/login')}>로그인</NavLi>
        )}
      </NavUl>
  );

그리고 이제 토큰을 받아오고 저장하는 구조 자체를 손을 봤어야 했다. 로컬스토리지에 저장된 토큰을 다시 또 변수에 담아서 그것의 변화를 감지시키는 것이 아니라 Login.js에서 useRecoilState를 써서 state에 담아주는 것이다.

[Login.js 파일]

const Login = () => {
  let params = new URLSearchParams(document.location.search);
  let code = params.get('code');

  const navigate = useNavigate();

  const [tokenData, setTokenData] = useState('');

  const [token, setToken] = useRecoilState(tokenState);

  const getToken = async () => {
    try {
      await fetch('https://kauth.kakao.com/oauth/token', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8',
        },
        body: qs.stringify({
          grant_type: 'authorization_code',
          client_id: restApiKey,
          redirect_uri: redirectUrl,
          code: code,
          client_secret: clientSecret,
        }),
      })
        .then(res => res.json())
        .then(data => setTokenData(data.access_token));
    } catch (err) {
      console.error(err);
    }
  };

  const sendToken = async () => {
    try {
      await fetch('http://10.58.6.151:8000/users/login', {
        method: 'GET',
        headers: { Authorization: tokenData },
      })
        .then(res => res.json())
        .then(data => {
          localStorage.setItem('access_token', data.access_token);
          setToken(localStorage.getItem('access_token'));
        })
        .then(navigate('/'));
    } catch (err) {
      console.error(err);
    }
  };

  useEffect(() => {
    getToken();
    if (tokenData) {
      sendToken();
    }
  });

[GlobalState.js 파일]

import { atom } from 'recoil';

export const tokenState = atom({
  key: 'tokenState',
  default: '',
});

//GlobalState는 변수이다.

  • 이렇게 하면 'const [token, setToken] = useRecoilState(tokenState)'에서 tokenState가 default로 담겨있다가 슈크림 서비스 토큰이 들어오면 token이라는 state에 담기게 된다. 이제 Nav.js파일에서 이를 활용해주면 된다.
const Nav = () => {
  const navigate = useNavigate();
  
  //Login 파일에서 온 슈크림 서비스의 토큰이 token이라는 state에 담겾 ㅕ있다.
  const [token, setToken] = useRecoilState(tokenState);

  const logoutToken = () => {
    localStorage.removeItem('access_token');
    setToken('');
  };

  return (
    <RecoilRoot>
      <NavUl>
        <NavLi>고객센터</NavLi>
        <NavLi>관심상품</NavLi>
        <NavLi>마이페이지</NavLi>
        {token ? (
          <NavLi onClick={logoutToken}>로그아웃</NavLi>
        ) : (
          <NavLi onClick={() => navigate('/login')}>로그인</NavLi>
        )}
      </NavUl>
    </RecoilRoot>
  );
};

export default Nav;

여기서 토큰이 담겨져 있으면 navigation bar에 로그아웃이 표시가 되게 된다. 그리고 만일 로그아웃을 클릭하게 되면 logoutToken 함수가 실행이 되고 로컬 스토리지에서 토큰은 제거가 된다. 그리고 setToken('')을 실행함으로서 ''은 자바스크립트에서 false로 간주가 되기 때문에 다시 로그인이 표시가 된다. 그리고 이러한 일련의 과정들은 state 값이 변하는 것이기 때문에 렌더링이 일어남으로써 내가 직접 새로고침을 해줄 필요가 없는 것이다. 즉, 전역변수(recoil 라이브러리)를 활용해서정상적으로 로그인/로그아웃 상태가 nav에도 반영 되게끔 수정한 것이다.

코드 수정 후 결과 화면

  • 처음 로그인 화면
  • 토큰이 들어온 후에 Navigation Bar 상태 변화(로그아웃)
  • 로그아웃 버튼을 누르면 토큰이 지워지고 Navigation Bar 상태 변화(로그인)

좋은 웹페이지 즐겨찾기