[๐Ÿ’๐Ÿป‍โ™€๏ธ mbti ํ…Œ์ŠคํŠธ ๋งŒ๋“ค๊ธฐ] ํ† ํฐ ๋งŒ๋ฃŒ ์ด์Šˆ... ๐Ÿ˜ต‍๐Ÿ’ซ

ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋˜ ์ค‘ ํ”„๋กœํ•„ ์กฐํšŒ์—์„œ ๊ฐ‘์ž๊ธฐ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค. 

 

์˜ค๋ฅ˜

๋„คํŠธ์›Œํฌ ํƒญ์—์„œ ํ™•์ธํ•œ ์˜ค๋ฅ˜ ์›์ธ์€ ํ† ํฐ ๋งŒ๋ฃŒ ์ด์Šˆ..! 

 

ํ† ํฐ ๋งŒ๋ฃŒ ์‹œ๊ฐ„์„ ํ™•์ธํ•ด ๋ณด๋‹ˆ.. 1์‹œ๊ฐ„์ธ ๊ฒƒ์„ ํ™•์ธํ–ˆ๋‹ค... 

๋งŒ๋ฃŒ ์‹œ๊ฐ„์ด 1์‹œ๊ฐ„์ธ ๊ฒƒ์„ ํ™•์ธ

 

ํ•ด๊ฒฐ ๊ณผ์ •...

์šฐ์„  ๋น ๋ฅธ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด ํ† ํฐ์„ 1๋ถ„์งœ๋ฆฌ ๋ฐœ๊ธ‰ํ•˜๋„๋ก ํ–ˆ๋‹ค.

export const login = async (userData) => {
  return await authInstance.post("/login?expiresIn=1m", userData);
};

 

์ฒ˜์Œ ์ƒ๊ฐํ•œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์€ ์‘๋‹ต์ด status ๊ฐ€ 401 ์ธ ๊ฒฝ์šฐ ๋กœ๊ทธ์•„์›ƒ ์ฒ˜๋ฆฌํ•˜๊ณ  ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ ๋Œ๋ฆฌ๋ฉด ๋˜๊ฒ ์ง€ ํ–ˆ๋Š”๋ฐ, ๋‹ค๋ฅธ ์˜ค๋ฅ˜๋„ ๋‹ค 401์„ ์ฃผ๊ณ  ์žˆ์—ˆ๋‹ค.

๊ทธ๋Ÿฌ๋ฉด ์‘๋‹ต์œผ๋กœ "ํ† ํฐ์ด ๋งŒ๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์‹œ ๋กœ๊ทธ์ธ ํ•ด์ฃผ์„ธ์š”." ๋ผ๋Š” ๋ฉ”์„ธ์ง€๋ฅผ ์ฃผ๊ณ  ์žˆ์œผ๋‹ˆ ์˜ค๋ฅ˜ ๋ฉ”์„ธ์ง€๊ฐ€ ์ด์™€ ๊ฐ™์œผ๋ฉด ์ฒ˜๋ฆฌํ•˜๋„๋ก ํ•˜๋ ค๊ณ  ํ–ˆ๋Š”๋ฐ tanstack query ๋กœ ํ˜ธ์ถœ ์‹œ ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ .. error ์— ์—๋Ÿฌ๋ฉ”์„ธ์ง€๊ฐ€ ์žˆ์–ด์•ผ๋  ๊ฒƒ ๊ฐ™์€๋ฐ null ์ด๋‹ค..(?) ์ด๊ฑฐ๋กœ๋Š” ํ•ด๊ฒฐ์„ ๋ชปํ•˜๊ฒ ๊ตฐ.. ํŒจ์Šค..

 

๋‹ค์Œ ๋ฐฉ๋ฒ•์€ ํ•ด๋‹น ๋ฌธ์„œ์— ๋‚˜์™€ ์žˆ๋Š” ๋Œ€๋กœ axios ๋กœ ํ˜ธ์ถœ ํ›„ catch ๋กœ ์žก์•„์„œ ์ฒ˜๋ฆฌํ•ด ๋ณผ ์ง€ ์ƒ๊ฐํ–ˆ๋Š”๋ฐ, ์ €๋ ‡๊ฒŒ ํ•  ๊ฒฝ์šฐ ๋ชจ๋“  api ์š”์ฒญํ•˜๋Š” ๊ณณ๋งˆ๋‹ค ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ฃผ์–ด์•ผ ํ•ด์„œ ์ค‘๋ณต์ฝ”๋“œ๊ฐ€(ํšŒ์› ์ •๋ณด ์กฐํšŒ, ํ”„๋กœํ•„ ์ˆ˜์ • ๋“ฑ) ์ƒ๊ธด๋‹ค. ๊ทธ๋ž˜์„œ ์ด ๋ฐฉ๋ฒ•๋„ ์‹œ๋„ํ•ด๋ณด๋‹ค๊ฐ€ ํŒจ์Šค..

/** ํšŒ์›์ •๋ณด ์กฐํšŒ */
export const getUserProfile = async (token) => {
  return await authInstance.get("/user", {
    headers: {
      Authorization: `Bearer ${token}`,
    },
  });
  // ์—ฌ๊ธฐ์„œ ์ฒ˜๋ฆฌํ• ๊นŒ ์ƒ๊ฐํ–ˆ์Œ
  // .catch((error) => {
  //   console.log(error.response.data.message);
  // });
};

/** ํ”„๋กœํ•„ ์ˆ˜์ • */
export const updateProfile = async ({ nickname, token }) => {
	... ์ƒ๋žต
};

 

๋‹ค์Œ ์ƒ๊ฐํ•œ ๋ฐฉ์‹์€ axios interceptor ์ด๋‹ค. 

const authInstance = axios.create({
  baseURL: "https://moneyfulpublicpolicy.co.kr",
});

authInstance.interceptors.response.use(
  (response) => {
    return response;
  },
  (error) => {
    if (
      error.response.data.message ===
      "ํ† ํฐ์ด ๋งŒ๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์‹œ ๋กœ๊ทธ์ธ ํ•ด์ฃผ์„ธ์š”."
    ) {
      const { persist } = useUserStore();
      const navigate = useNavigate();
      persist.clearStorage();
      
      navigate(HOME);
    }
    return Promise.reject(error);
  }
);

์œ„์™€ ๊ฐ™์ด ์ธํ„ฐ์…‰ํ„ฐ์—์„œ ํ•˜๋ฉด ํšŒ์› ์ •๋ณด ์กฐํšŒ, ํ”„๋กœํ•„ ์กฐํšŒํ•  ๋•Œ ๊ณตํ†ต์œผ๋กœ ์ฒ˜๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ฒผ๋Š”๋ฐ.. useNavigate ๊ฐ™์€ ๋ฆฌ์•กํŠธ ํ›…์€ ๋ฐ˜๋“œ์‹œ React ์ปดํฌ๋„ŒํŠธ ํ•จ์ˆ˜ ๋‚ด์—์„œ ํ˜ธ์ถœ๋˜์–ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ...  ๋น„๋™๊ธฐ ํ•จ์ˆ˜ ํ˜น์€ React ์ปดํฌ๋„ŒํŠธ ์™ธ๋ถ€์—์„œ๋Š” ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค๊ณ ํ•œ๋‹ค. 

 

๊ทธ๋Ÿฌ๋ฉด.. ํ›…์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด๋‹ค๊ฐ€..


์•„๋ž˜์™€ ๊ฐ™์€ ๋ฐฉ๋ฒ•์œผ๋กœ ์‹œ๋„ํ–ˆ๋‹ค.

const authInstance = axios.create({
  baseURL: "https://moneyfulpublicpolicy.co.kr",
});

authInstance.interceptors.response.use(
  (response) => {
    return response;
  },
  (error) => {
    if (
      error.response.data.message ===
      "ํ† ํฐ์ด ๋งŒ๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์‹œ ๋กœ๊ทธ์ธ ํ•ด์ฃผ์„ธ์š”."
    ) {
      localStorage.removeItem("userInfo"); // ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€์—์„œ ๋ฐ์ดํ„ฐ ์‚ญ์ œ
      alert("๋กœ๊ทธ์ธ ๊ธฐ๊ฐ„์ด ๋งŒ๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์‹œ ๋กœ๊ทธ์ธํ•ด์ฃผ์„ธ์š”.");
      window.location.href = HOME;
    }

    return Promise.reject(error);
  }
);

์ด๊ฒŒ ์ตœ์„ ... ์ธ์ค„ ์•Œ์•˜๋Š”๋ฐ ์ด ๋ฐฉ์‹์—๋„ ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค. ์‚ฌ์‹ค navigate ๋ƒ…๋‘๊ณ    window.location.href = HOME; ๋ฅผ ์‚ฌ์šฉํ•œ ๊ฒƒ๋„ ์ฐ์ฐํ–ˆ์ง€๋งŒ ๋” ํฐ ๋ฌธ์ œ๋Š”, ์‚ฌ์šฉํ•˜๋Š” API ์ค‘ json server ์„ ์‚ฌ์šฉํ•˜๋Š” test ๊ด€๋ จ API ๋Š” ์š”์ฒญ ์‹œ ํ† ํฐ์„ ์ฃผ์ง€ ์•Š๋Š”๋‹ค. ํ•˜์ง€๋งŒ ํšŒ์› ๊ด€๋ จ API ๋Š” ์š”์ฒญ ์‹œ ํ† ํฐ์„ ํ—ค๋”์— ๋„ฃ์–ด์„œ ์š”์ฒญํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ํ™”๋ฉด์—์„œ ํƒญ ์ด๋™ ์‹œ ํ”„๋กœํ•„ ํ™”๋ฉด์—์„œ๋Š” ํ† ํฐ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ  ํ…Œ์ŠคํŠธ ๊ด€๋ จ ํŽ˜์ด์ง€์—์„œ๋Š” ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š” ์ƒํ™ฉ. 

 

๊ทธ๋ž˜์„œ ๊ณ ๋ฏผ๋์— ํ”„๋กœํ•„ ๊ด€๋ จ API ์—์„œ ํ† ํฐ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ํ…Œ์ŠคํŠธ API ์—๋„ ์˜ํ–ฅ์ด ๊ฐ€๋„๋ก ๋ณ€๊ฒฝ์„ ํ•˜์˜€๋‹ค.

 

๊ทธ ์œ„์น˜๋Š” ProtectedRoute ์ด๋‹ค. ํ”„๋กœํ•„๊ณผ ํ…Œ์ŠคํŠธ ํŽ˜์ด์ง€๋Š” ๋กœ๊ทธ์ธํ•œ ์œ ์ €๋งŒ ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•˜๊ธฐ๋•Œ๋ฌธ์— ์ด ๊ณณ์—์„œ ๋กœ๊ทธ์ธ ์•ˆํ–ˆ์œผ๋ฉด ๋กœ๊ทธ์ธ ํ™”๋ฉด์œผ๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธํ•ด์ค€๋‹ค. ํ”„๋กœํ•„ ํŽ˜์ด์ง€์™€ ํ…Œ์ŠคํŠธํŽ˜์ด์ง€๋Š” ์ด๊ณณ์„ ๋ฌด์กฐ๊ฑด ๊ฑฐ์น˜๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ณณ์—์„œ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋ฉด ๋œ๋‹ค.

์–ด๋–ป๊ฒŒ? ๋กœ๊ทธ์ธ ๋˜์–ด์žˆ์œผ๋ฉด ์œ ์ € ์ •๋ณด ์•ˆ์— ์žˆ๋Š” ํ† ํฐ์œผ๋กœ ํšŒ์› ์กฐํšŒ๋ฅผ ํ•˜๊ณ  ํ† ํฐ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๋กœ๊ทธ์•„์›ƒ & ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ ์ด๋™์‹œํ‚ค๊ธฐ

const ProtectedRouts = () => {
  const { user, isLogin, setIsLogin, setUser, pageType } = useUserStore();
  const navigate = useNavigate();
  const clearUserIdStorage = useUserStore.persist.clearStorage;

  /**
   * ๋กœ๊ทธ์ธ ํ–ˆ์„ ๊ฒฝ์šฐ, ์ƒˆ๋กœ๊ณ ์นจํ•˜๋ฉด ์ด ๋กœ์ง์ด ์ž‘๋™
   * -> ํšŒ์› ์ •๋ณด ์กฐํšŒํ•ด์„œ ํ† ํฐ ์œ ํšจํ•œ์ง€ ํŒ๋‹จ
   * -> ์˜ค๋ฅ˜ ๋ฐœ์ƒํ•˜๋ฉด ๋กœ๊ทธ์•„์›ƒ ์ฒ˜๋ฆฌ ํ›„, ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ ์ด๋™
   */
  useEffect(() => {
    const getUser = async () => {
      if (isLogin) {
        try {
          await getUserProfile(user.accessToken);
        } catch (error) {
          setUser(null);
          setIsLogin(false);
          clearUserIdStorage();
          navigate(SING_IN);
        }
      }
    };
    getUser();
  }, []);

  /** ๋กœ๊ทธ์ธ์ด ์•ˆ๋œ ์ƒํƒœ์ด๋ฉด -> ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ ๋ณด๋‚ด๊ธฐ */
  if (!isLogin) {
    alert("๋กœ๊ทธ์ธ ํ›„ ์ด์šฉํ•ด์ฃผ์„ธ์š”.");
    return <Navigate to={SING_IN} replace />;
  }

  return <Outlet />;
};

๊ทผ๋ฐ.... ์ด๊ฒƒ ์—ญ์‹œ ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค. useEffect  ๋กœ ์ธํ•ด์„œ ํŽ˜์ด์ง€ ํ•œ๋ฒˆ ์ด๋™ํ•˜๊ณ  ๋‚˜์„œ ๋‹ค๋ฅธ ํŽ˜์ด์ง€ ์ด๋™์‹œ ๋™์ž‘์„ ์•ˆ ํ•œ๋‹ค!! ใ…œใ…œ ๊ฐ ํŽ˜์ด์ง€๋งˆ๋‹ค ๋ฆฌ๋žœ๋”๋ง์„ ํ•ด์ค„ ํŠธ๋ฆฌ๊ฑฐ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

 

๊ทธ๋ž˜์„œ header ์—์„œ ํŽ˜์ด์ง€ ์ด๋™ํ•  ๋•Œ๋งˆ๋‹ค pageType ์ƒํƒœ๋ฅผ ์ฃผ์–ด์„œ ProtectedRouts ๊ฐ€ ๋ฆฌ๋žœ๋”๋ง์ด ๋˜์–ด์„œ useEffect ๋ฅผ ๋‹ค์‹œ ์‹คํ–‰ํ•˜๋„๋ก ํ–ˆ๋‹ค.

const Header = () => {
	... ์ƒ๋žต

  return (
    <header>
    
    ... ์ƒ๋žต
    
        {user ? (
          <div className="flex gap-10">
            <span className="text-black">{user.nickname}</span>
            <button
              onClick={() => {
                setPageType("Profile");
                navigate(PROFILE);
              }}
            >
              ํ”„๋กœํ•„
            </button>
            
            ... ์ƒ๋žต

  
          </div>
        ) : (
          <Link to={SING_IN}>๋กœ๊ทธ์ธ</Link>
        )}
      </div>
    </header>
  );
};

export default Header;

ํŽ˜์ด์ง€ ์ด๋™ ํ• ๋•Œ๋งˆ๋‹ค setPageType("Profile") ์™€ ๊ฐ™์ด pageType ๋ฅผ ์„ค์ •ํ•ด์„œ ๋ฆฌ๋žœ๋”๋ง์ด ์ผ์–ด๋‚˜๋„๋ก ํ–ˆ๋‹ค...

 

const ProtectedRouts = () => {
  const { user, isLogin, setIsLogin, setUser, pageType } = useUserStore();
  const navigate = useNavigate();
  const clearUserIdStorage = useUserStore.persist.clearStorage;

  useEffect(() => {
    const getUser = async () => {
      if (isLogin) {
        try {
          await getUserProfile(user.accessToken);
        } catch (error) {
	...์ƒ๋žต
        }
      }
    };
    getUser();
  }, [pageType]);


	... ์ƒ๋žต

  return <Outlet />;
};

useEffect ์—๋Š” pageType ์˜์กดํ•˜๋„๋ก ํ–ˆ๋‹ค. 

 

์ผ๋‹จ.. ์›ํ•˜๋Š” ๋™์ž‘๋Œ€๋กœ ํ•ด๊ฒฐ์€ํ–ˆ๋‹ค.. ํ•˜์ง€๋งŒ ์ด๊ฒƒ์ €๊ฒƒ ์‹œ๋„ํ•˜๊ณ  ์ฝ”๋“œ ์ˆ˜์ •ํ•˜๊ณ  ํ•˜๋‹ˆ๊นŒ ๊ดœํžˆ ๊ผฌ์ธ ๋Š๋‚Œ์ด๊ณ  ์ฐ์ฐํ•œ ๊ธฐ๋ถ„์ด ๋“ ๋‹ค. ์ฒ˜์Œ๋ถ€ํ„ฐ ์„ค๊ณ„๋ฅผ ์ž˜ํ–ˆ๋‹ค๋ฉด.. ์ข‹์•˜์„ ํ…๋ฐ ์•„์‰ฌ์›€์ด ์žˆ๋‹ค.

 

์ด ์ด์Šˆ๋ฅผ ์™„๋ฒฝํ•˜๊ฒŒ ํ•ด๊ฒฐํ–ˆ๋‹ค..? โŒ ์•„๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋‚ด๊ฐ€ ์ฒ˜์Œ ์„ค๊ณ„ํ•œ ๊ตฌ์กฐ์—์„œ ์ตœ๋Œ€ํ•œ ํ•ด๊ฒฐํ•ด ๋ณผ ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ์–ด๋Š ์ •๋„ ํ•ด๊ฒฐํ–ˆ๋‹ค๊ณ  ๋ณธ๋‹ค. 

์ฒ˜์Œ์œผ๋กœ ๋กœ๊ทธ์ธ, ํšŒ์›๊ฐ€์ž…, ๊ถŒํ•œ ์ฒ˜๋ฆฌ, ํ† ํฐ ์ฒ˜๋ฆฌ, ์ƒˆ๋กœ๊ณ ์นจ ์ „๋ถ€ ๋‹ค ํ•˜๋ฉด์„œ ๋Š๋‚€ ๊ฒƒ์€.. ํ”„๋ก ํŠธ๋‹จ์—์„œ ๊ณ ๋ คํ•ด์•ผ ํ•  ์ƒํ™ฉ์ด ๊ต‰์žฅ์ด ๋งŽ๋‹ค๋Š” ๊ฒƒ์„ ๋Š๊ผˆ๋‹ค.. 

์–ด๋ ต๊ณ  ๋จธ๋ฆฌ ํ„ฐ์ง€๋Š” ํ”„๋กœ์ ํŠธ์˜€๋‹ค.. ๋‚˜์ค‘์— ๋‹ค์‹œ ์ด ํ”„๋กœ์ ํŠธ๋ฅผ ๋ดค์„ ๋•Œ๋Š” ์–ด๋””์„œ๋ถ€ํ„ฐ ์ž˜๋ชป๋๋Š”์ง€ ๊นจ๋‹ซ๋Š” ์ˆœ๊ฐ„์ด ์˜ค๋ฉด ์ข‹๊ฒ ๋‹ค.