2024.03.08 TIL - JWT 로그인 로직, 트러블슈팅(JWT 로그인시 session storage 확인 안됨)

Innes·2024년 3월 8일
0

TIL(Today I Learned)

목록 보기
85/147
post-thumbnail

JWT 로그인 로직

  • 로그인 비활성화시 로그인, 회원가입 페이지에만 접근 가능
  • 로그인 활성화시 홈, 디테일 페이지에 접근 가능
  • 로그인 상태는 session storage내의 token 유무로 판별
// (todoList typescript 리팩토링 과제 중 일부)
// ✅ Routes.tsx 

import { createBrowserRouter } from "react-router-dom";
import Home from "../pages/Home";
import Detail from "../pages/Detail";
import Header from "../layout/Header";
import { Login } from "../pages/Login";
import { SignUp } from "../pages/SignUp";
import { ProtectedSite } from "../components/ProtectedSite";

export const router = createBrowserRouter([
  {
    path: "/",
    element: (
      // ⭐️ ProtectedSite에서 로그인 상태를 확인하여 페이지를 이동시킨다.
      <ProtectedSite>
        <Header />
      </ProtectedSite>
    ),
    children: [
      {
        path: "",
        element: <Home />,
      },
      {
        path: "detail/:id",
        element: <Detail />,
      },
    ],
  },
  {
    path: "/login",
    element: <Login />,
  },
  {
    path: "/signUp",
    element: <SignUp />,
  },
]);
// ✅ ProtectedSite.tsx

import { Navigate } from "react-router-dom";

export const ProtectedSite = ({ children }: { children: React.ReactNode }) => {
  // ⭐️ session storage 토큰 유무로 로그인 상태를 확인
  const token = sessionStorage.getItem("token");

  if (!token) {
    return <Navigate to="/login" />;
  }
  return children;
};
// ✅ Login.tsx

import { Link, useNavigate } from "react-router-dom";
import styled from "styled-components";
import { UserLogin } from "../api/auth-api";

export const Login = () => {
  const navigate = useNavigate();
  const onLoginHandler = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();

    const formData = new FormData(e.currentTarget);
    const id = formData.get("id") as string;
    const password = formData.get("password") as string;

    try {
      // ⭐️ 로그인시 - sessionStorage에 토큰 저장, 홈으로 이동
      // try catch문으로 작성해야 오류처리 가능!
      const data = await UserLogin({ id, password });
      sessionStorage.setItem("token", data.accessToken);
      navigate("/");
    } catch (error) {
      console.log("error", error);
    }
  };
  return (
    <Container>
      <Form onSubmit={onLoginHandler}>
        <p>☁️ 로그인 ⛅️</p>
        <input type="text" name="id" placeholder="email" />
        <input type="password" name="password" placeholder="비밀번호" />
        <input type="submit" value="로그인" />
        <div>
          <span>회원이 아니신가요? &rarr; </span>
          <LinkToSignUp style={{ cursor: "pointer" }} to="/signUp">
            회원가입하기
          </LinkToSignUp>
        </div>
      </Form>
    </Container>
  );
};
// ✅ auth-api.ts - 로그인 request보내는 api

import axios from "axios";
import { User } from "../types/Auth";

const userClient = axios.create({
  baseURL: "https://moneyfulpublicpolicy.co.kr",
  headers: {
    "Content-Type": "application/json",
  },
});

interface signUpProps {
  id: string;
  password: string;
  nickname: string;
}

type LoginProps = Omit<signUpProps, "nickname">;
export const UserLogin = async ({
  id,
  password,
}: LoginProps): Promise<User> => {
  const response = await userClient.post("/login", {
    id,
    password,
  });
  return response.data;
};

🏹 트러블슈팅

session storage 토큰 확인 안됨

  • 문제 : 로그인하면 바로 session storage를 확인해서 토큰이 있으면 홈화면으로 넘겨줘야하는데, 토큰 확인을 안해서 홈화면으로 넘어가질 못하고 로그인화면에서 제자리

  • 기존 코드

// 🧡 시도 1. useEffect 활용하여 화면 렌더링시 token 판별하게 만들기

import { useEffect, useState } from "react";
import { Navigate } from "react-router-dom";

export const ProtectedSite = ({ children }: { children: React.ReactNode }) => {
  const [token, setToken] = useState(sessionStorage.getItem("token"));

  // 의존성 배열에도 token, sessionStorage.getItem("token") 등 다 넣어봄
  useEffect(() => {
    const getToken = sessionStorage.getItem("token");
    getToken && setToken(getToken);
  }, []);
  
  // 여기서 token이 빈값으로 뜸 (undefined, null 아닌 그냥 아예 빈칸)
  console.log("token", token);

  if (!token) {
    return <Navigate to="/login" />;
  }
  return children;
};


// 🧡 시도 2. useEffect 제거 후 재시도 - Too many rerender 오류 발생

import { useEffect, useState } from "react";
import { Navigate } from "react-router-dom";

export const ProtectedSite = ({ children }: { children: React.ReactNode }) => {
  const [token, setToken] = useState(sessionStorage.getItem("token"));

    const getToken = sessionStorage.getItem("token");
    getToken && setToken(getToken);

  if (!token) {
    return <Navigate to="/login" />;
  }
  return children;
};
  • 원인

    • 시도 1 : useEffect는 마운트 된 다음 실행되는 hook이다. 따라서 아래의 console.log와 if문이 실행된 다음에야 useEffect가 실행되는 흐름이었던 것! 로그인페이지로 Navigate 한 다음 토큰을 판별하니까 로그인페이지에 계속 머물러있었다...!
      -> 마운트와 hook의 실행에 대한 흐름을 알아야 했다.

    • 시도 2 : useState로 set하면 화면이 리렌더링된다. 해당 로직에서는 토큰이 있으면 setToken하고, set했으니 리렌더링 발생하고, 리렌더링해보니 토큰이 있으니까 또 set하고, set했으니까 리렌더링되고... 무한 반복이었던 것이다.
      -> useState set하는 경우 화면이 리렌더링되는 흐름을 이해해야 했다.

  • 해결 코드 : 그냥 session storage에서 토큰 가져오는 변수 지정하고 토큰 있으면 페이지 이동... 단순하게 하면 되는 거였다...!

import { Navigate } from "react-router-dom";

export const ProtectedSite = ({ children }: { children: React.ReactNode }) => {
  const token = sessionStorage.getItem("token");

  if (!token) {
    return <Navigate to="/login" />;
  }
  return children;
};
profile
무서운 속도로 흡수하는 스폰지 개발자 🧽

0개의 댓글