debounce

지헌·2024년 6월 20일
0

debounce

// debounce 사용

import { useEffect, useCallback } from "react";
import { useRecoilState } from "recoil";
import { userState, isLoggedInState } from "@/recoil/userState";
import { useLogout } from "@/hooks/useLogout";
import { basicAxios } from "@/api/axios";
import axios, { AxiosResponse, AxiosError } from "axios";

// API 응답 데이터 타입 정의
interface RefreshResponse {
  accessToken: string;
}

// LocalStorage에서 가져오는 데이터의 타입 정의
type LocalStorageItem = string | null;

// debounce 함수 정의
const debounce = <F extends (...args: any[]) => void>(
  func: F,
  delay: number
): ((...args: Parameters<F>) => void) => {
  let timer: ReturnType<typeof setTimeout>;

  return (...args: Parameters<F>) => {
    clearTimeout(timer);
    timer = setTimeout(() => {
      func(...args);
    }, delay);
  };
};

const useAuth = () => {
  const [user, setUser] = useRecoilState(userState);
  const [isLoggedIn, setIsLoggedIn] = useRecoilState(isLoggedInState);
  const { logout } = useLogout();

  const refreshAccessToken = useCallback(async (): Promise<string | null> => {
    try {
      console.log("Starting token refresh...");
      const response: AxiosResponse<RefreshResponse> = await basicAxios.post(
        "/users/refresh"
      );
      console.log("Refresh response:", response);

      const { accessToken } = response.data;
      localStorage.setItem("AccessToken", accessToken);

      // 액세스 토큰 만료 시간 갱신 (예시: 5분 후 만료)
      const accessTokenExpiration = new Date().getTime() + 5 * 60 * 1000;
      localStorage.setItem(
        "AccessTokenExpiration",
        accessTokenExpiration.toString()
      );

      return accessToken;
    } catch (error) {
      console.error("액세스 토큰 갱신 실패:", error);

      if (axios.isAxiosError(error)) {
        const response = (error as AxiosError).response;
        console.log("Axios 오류 응답:", response);

        if (response?.status === 401) {
          console.log("Unauthorized 오류 응답:", response.data);
        }
      }

      return null;
    }
  }, []);

  const debounceCheckAndRefreshToken = useCallback(
    debounce(async () => {
      await checkAndRefreshToken();
    }, 1000),
    []
  );

  const checkAndRefreshToken = useCallback(async (): Promise<void> => {
    const accessToken: LocalStorageItem = localStorage.getItem("AccessToken");
    const accessTokenExpiration: LocalStorageItem = localStorage.getItem(
      "AccessTokenExpiration"
    );

    console.log("Checking and refreshing token...");

    if (accessToken && accessTokenExpiration) {
      const now = new Date().getTime();
      const expirationTime = Number(accessTokenExpiration);

      if (now >= expirationTime) {
        console.log("Token expired. Refreshing...");
        const newAccessToken = await refreshAccessToken();
        if (newAccessToken) {
          setIsLoggedIn(true);
        } else {
          logout();
        }
      } else {
        setIsLoggedIn(true); // AccessToken이 유효하면 로그인 상태로 설정
        console.log("Token is valid. Setting isLoggedIn to true.");
      }
    } else {
      console.log("No access token or expiration time found. Logging out...");
      logout(); // AccessToken이 없으면 로그아웃 처리
    }
  }, [refreshAccessToken, setIsLoggedIn, logout]);

  useEffect(() => {
    const checkTokenAndRefresh = async (): Promise<void> => {
      const accessToken: LocalStorageItem = localStorage.getItem("AccessToken");
      const accessTokenExpiration: LocalStorageItem = localStorage.getItem(
        "AccessTokenExpiration"
      );

      console.log(
        "Executing useEffect to check token and refresh if necessary..."
      );

      if (accessToken && accessTokenExpiration) {
        const now = new Date().getTime();
        const expirationTime = Number(accessTokenExpiration);

        if (now < expirationTime) {
          setIsLoggedIn(true); // AccessToken이 유효하면 로그인 상태로 설정
          console.log("Token is valid. Setting isLoggedIn to true.");
          const storedUser: LocalStorageItem = localStorage.getItem("user");
          if (storedUser) {
            const parsedUser = JSON.parse(storedUser);
            setUser(parsedUser);
          }
        } else {
          console.log("Token expired. Calling debounceCheckAndRefreshToken...");
          debounceCheckAndRefreshToken(); // debounce를 통해 호출
        }
      } else {
        console.log("No access token or expiration time found. Logging out...");
        logout(); // AccessToken이 없으면 로그아웃 처리
      }
    };

    checkTokenAndRefresh();
  }, [setIsLoggedIn, setUser, debounceCheckAndRefreshToken, logout]);

  return { user, isLoggedIn, checkAndRefreshToken };
};

export default useAuth;
profile
차곡차곡 그만 쌓아올리고 취업해서 부딪쳐보고 싶은

0개의 댓글