본문 바로가기
react deep dive

[ZIMZIM] 지금 tanstack-query 필요할까?

by rami_ 2024. 8. 22.

로그인한 유저에게는 token을 부여하는데 그 토큰은 쿠키에 저장하기로 했다.

쿠키에 저장한 이유는 로컬 스토리지, 세션 스토리지는 이전에 구현해 보기도 했고 XSS(교차 사이트 스크립트)공격에 취약한 점이 있어 보안에 강한 쿠키를 사용해보기로 했다.

로그인한 유저에게는 서버에서 쿠키에 토큰을 넣어 보내주며 이후에도 쿠키에서 토큰을 꺼내 사용하는 것으로 했다.

 

최초에는 redux의 useSelector를 통해 값을 가져왔지만 이렇게 할 경우 새로고침을 하게 되면 메모리에 저장해 두었던게 날라가기 때문에 적절한 방법은 아니었다.

import React from 'react';
import { Outlet, Navigate, useLocation } from 'react-router-dom';
import { useSelector } from 'react-redux';

import { RootState } from '#stores/store';

const App = () => {
  const isAuthenticated = useSelector(
    (state: RootState) => state.auth.user !== null,
  );
  const location = useLocation();

  if (
    !isAuthenticated &&
    location.pathname !== '/login' &&
    location.pathname !== '/sign-up'
  ) {
    return <Navigate to="/login" />;
  }

  return (
    <main className="font-roboto">
      <Outlet />
    </main>
  );
};

export default App;

 

그 이후에는 App에서 사용자의 정보에 대한 요청을 보내는 것을 생각했지만 무의미한 api 요청이 많을것이라고 생각되었다.

로그인 이후 쿠키에 토큰을 저장해두고 사용자가 로그인 했다는 것을 캐싱해 두기 위해 어떤 방법이 있는지 고려했고, tastack-query를 도입하기로 했다.

 

먼저, tasntack-query를 설치하고 App에 프로바이더와 라우터들을 옮겼다.

import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { Provider } from 'react-redux';

import { store } from '#stores/store';

import Dashboard from '#/pages/Dashboard';
import Login from '#/pages/Login';
import SignUp from '#/pages/SignUp';

import AuthGuard from '#components/common/AuthGuard';

const App = () => {
  const queryClient = new QueryClient();

  return (
    <Provider store={store}>
      <QueryClientProvider client={queryClient}>
        <Router>
          <AuthGuard>
            <Routes>
              <Route path="/" element={<Dashboard />} />
              <Route path="/login" element={<Login />} />
              <Route path="/sign-up" element={<SignUp />} />
            </Routes>
          </AuthGuard>
        </Router>
      </QueryClientProvider>
    </Provider>
  );
};

export default App;

 

 

store > auth > action

export const login = createAsyncThunk(
  'auth/login',
  async (userInfo: LoginPayload, thunkAPI) => {
    try {
      const response = await axiosInstance.post('auth/login', userInfo);

      const queryClient = new QueryClient();
      queryClient.setQueryData(['user'], response);

      return response;
    } catch (error) {
      if (axios.isAxiosError(error)) {
        return thunkAPI.rejectWithValue(error.response?.data);
      } else {
        return thunkAPI.rejectWithValue('An unexpected error occurred');
      }
    }
  },
);

이런식으로 구현했었는데 문제가 생겼다.

 

tanstack-query도 메모리에 저장하는 것으로 새로고침을 사용하면 날라간다.

 

그래서 새로고침에 대비하기 위해 Authguard에서 사용자 정보가 없을 경우 다시 user 정보를 가져올 수 있는 api를 요청하기로 했다.

import React, { ReactNode, useEffect } from 'react';

import { Navigate } from 'react-router-dom';
import { useSelector, useDispatch } from 'react-redux';

import { getUserInfo } from '#stores/user/actions';
import { AppDispatch, RootState } from '#/stores/store';

const AuthGuard = ({ children }: { children: ReactNode }) => {
  const dispatch = useDispatch<AppDispatch>();
  const user = useSelector((state: RootState) => state.user);

  useEffect(() => {
    if (!user) {
      dispatch(getUserInfo());
    }
  }, [dispatch, user]);

  if (
    !user &&
    location.pathname !== '/login' &&
    location.pathname !== '/sign-up'
  ) {
    return <Navigate to="/login" />;
  }

  return <>{children}</>;
};

export default AuthGuard;

 

 

redux-toolkit의 공식문서에 tanstack-query를 사용하는 방법에 대한 튜토리얼도 있다(https://redux-toolkit.js.org/tutorials/rtk-query).

 

하지만 지금 상황에서 tanstack-query까지는 필요 없는 것으로 고려되어 도입하지 않기로 했다.

 

추후 필요한 상황이 오면 그때 다시 고려해 볼 예정이다.