본문 바로가기

React

React: navigation을 Promise의 resolve로 넘겨서 사용하기

728x90
반응형

token을 새로 받는 경우에

refreshToken이 만료되어 error가 발생 -> 특정 페이지로 redirect

 

axios interceptor를 사용중에 있고, error 발생 후 window.location.href="/"을 통해 redirect를 하면,

access token, refresh token 등을 redux store에서 지우는

로그아웃 처리와 header에서 Authorization을 지웠음에도 남아 있는 현상 발생

 + 페이지 전체 리로드시 리액트가 가지는 상태를 잃어버리는 단점

-> navigation을 활용해서 페이지 리다이렉트

 

navigation을 사용하는데 추가적인 문제상황

  * 훅 문법으로 axios request 함수 내에서 사용이 제약적

    - navigate를 변수 생성하여, 이 변수를 넘겨받아 처리

  * navigate가 사용하려는 곳에서 준비되지 않아서 전달받지 못하는 문제 발생

    - Promise (async, await) 사용으로 처리

 

예제 코드 커밋:

https://github.com/Junanjunan/g6_react/commit/afbe07ed5aed1685a289dad1819946cfa8a80f43

 

[fix] Handling navigation on error of refresh_token (with navigation … · Junanjunan/g6_react@afbe07e

…hook)

github.com

 

 

Flow

1. navigate 값을 얻을 수 있는 Promise를 활용

  - Promise의 resolve를 통해서 얻는 값이 navigate (naviationFunction)이 될 수 있도록 처리

    - new Promise((resolve) => {resolveNavigation = resolve});

      -> resolveNavigation을 resolve의 refrerence로 만든다. (이 refrerence를 통해 외부에서 resolve 메소드를 커스텀)

    - resolve의 reference인 resolveNavigation을 통해 resolveNavigation(navigate) 실행

      -> navigationPromise는 navigate를 값으로 얻을 수 있음

         - 즉 Promise의 resolve가 반환하는 값이 navigate가 되도록 함

// src/lib/useNavigationSetup.ts

import { useEffect } from 'react';
import { useNavigate, NavigateFunction } from 'react-router-dom';


let navigationPromise: Promise<NavigateFunction>;

let resolveNavigation: (navigate: NavigateFunction) => void;

navigationPromise = new Promise((resolve) => {
  resolveNavigation = resolve;
});


export const setNavigation = (navigate: NavigateFunction) => {
  resolveNavigation(navigate);
};


export const getNavigation = async (): Promise<NavigateFunction> => {
  return navigationPromise;
};


export const useNavigationSetup = () => {
  const navigate = useNavigate();

  useEffect(() => {
    setNavigation(navigate);
  }, [navigate]);
};

 

 

2. 앱의 Root 단에서 navigate를 초기화

// src/components/Root.tsx

import { Box } from "@chakra-ui/react";
import { Outlet } from "react-router-dom";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import Header from "./Header";
import { useNavigationSetup } from "../lib/useNavigationSetup";	// **

// **
const NavigationSetup: React.FC = () => {
  useNavigationSetup();
  return null;
};

export default function Root() {
  return (
    <Box>
      <NavigationSetup />
      <Header />
      <Outlet />
      <ReactQueryDevtools />
    </Box>
  );
}

 

NavigationSetup을 컴포넌트화해서 아래와 같이 Root에 포함시킨다.

  * NavigationSetup -> useNvigationSetup -> navigationPromise를 초기화 한다.

   (1번의 로직이 Root 내의 NavigationSetup이 불러와지면서 실행)

 

3. navigate를 원하는 곳에서 전달받아서 사용 (Promise 이므로 async, await 활용)

// src/api.ts

import { getNavigation } from "./lib/useNavigationSetup";  // **


export const axiosInstance = axios.create({
  baseURL: `${serverURL}/api/v1`
})

async function handleLogoutAndRedirect(): Promise<void> {
  store.dispatch(tokenLogout());
  store.dispatch(userLogout());
  delete axiosInstance.defaults.headers.common["Authorization"];
  alert("로그인이 필요합니다.");
  const navigate = await getNavigation();  // **
  navigate('/');                           // **
}

 

navigationPromise를 얻을 수 있는 getNavigation 함수를 통해 navigate를 전달받아서 사용

728x90
반응형