ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Toy Project 기록하기 9] React-Query로 서버 상태 관리하기
    Frontend/Projects 2023. 3. 20. 02:15

    리액트에서 state는 떼려야 뗄 수 없는 것으로 state의 상태 관리를 위한 다양한 라이브러리가 존재합니다. 지난번 Client-side에서는 프로젝트의 클라이언트의 복잡도가 높지 않고, 공통으로 사용하는 전역 상태의 데이터도 많지 않아 리액트의 context를 통해 로그인한 유저의 데이터를 저장했습니다. 그렇다면 Server-side에서 가져오는 데이터를 위한 상태 관리도 필요할 텐데요. 오늘은 제 프로젝트에서 Server-side에서 state 상태 관리를 위해 도입한 React-Query에 대해 알아보고 활용한 방법에 대해 적어보겠습니다. 

     


     

    Server State를 관리하며 발생할 수 있는 문제

    원격 데이터는 서버에서 관리되는 데이터를 말합니다. 데이터를 가져오고 업데이트하기 위해서 비동기 API가 필요합니다. 그런데 해당 데이터가 클라이언트가 모르게 원격에서 변경되면 응답은 구식(out-of-date)이 될 수 있습니다. 

     

    React-Query란

    서버의 값을 클라이언트에 가져오거나 캐싱, 업데이트, 에러핸들링 등 비동기 로직을 지원하는 data fetching 라이브러리입니다. 

     

    React-Query가 제공하는 다음과 같은 기능을 통해 Server State를 더욱 효율적으로 관리할 수 있습니다.

    • 데이터 캐싱
    • 데이터를 캐시에서 유지할 시간
    • 리프레시 간격
    • 서버 데이터 중복 호출 제거
    • 페이지네이션, 레이지 로딩 데이터의 성능 최적화
    • 쿼리 결과에 대한 성공, 로딩, 에러 콜백

     

    React-Query의 라이프 사이클

    fetching: 요청 중인 데이터

    fresh: 신선한 데이터(↔ stale한 데이터). 컴포넌트가 마운트, 업데이트되어도 데이터를 다시 요청하지 않습니다.

    stale: 만료된 데이터. 컴포넌트가 마운트, 업데이트 되면 데이터를 다시 요청합니다.

    inactive: 사용하지 않는 데이터로 일정 시간이 지나면 가비지 컬렉터가 캐시에서 제거합니다.

    delete: 가비지 컬렉터에 의해 캐시에서 제거된 상태를 말합니다.

     

     

    ➡️ 기본적으로 데이터를 fetching 해온 후 데이터를 캐싱하며 백그라운드에서 서버에 주기적으로 polling을 하여 데이터가 유효한지 검사하고 유효하지 않으면(stale) 업데이트해 데이터를 동기화합니다.

     

    React-Query 사용하기

    react-query 설치하기

    npm i react-query

     

    QueryClientProvider

    React Query는 캐시를 관리하기 위해 QueryClient 인스턴스를 사용합니다. 그리고 QueryClientProvider로 최상단에서 감싸줍니다.

    React Query DevTools는 쿼리 상태를 이해하는 데 큰 도움을 줍니다. 또한 현재 캐시에 있는 데이터를 알려주기 때문에 디버깅을 쉽게 할 수 있습니다.

    import React from 'react';
    import ReactDOM from 'react-dom/client';
    import { BrowserRouter } from 'react-router-dom';
    import App from './App';
    import { QueryClient, QueryClientProvider } from 'react-query';
    import { UserProvider } from './context/UserContext';
    import { ReactQueryDevtools } from 'react-query/devtools';
    
    const root = ReactDOM.createRoot(
      document.getElementById('root') as HTMLElement
    );
    
    const queryClient = new QueryClient({
      defaultOptions: {
        queries: {
          // 실패 시 retry하는 횟수 (default: 3)
          retry: 1,
          // 윈도우 창이 포커스 될 때마다 refetch (default: true)
          refetchOnWindowFocus: false,
        },
      },
    });
    
    root.render(
      <QueryClientProvider client={queryClient}>
        // 데이터의 상태를 확인할 수 있는 개발자 도구
        <ReactQueryDevtools initialIsOpen={true} />
        <BrowserRouter>
          <UserProvider>
            <App />
          </UserProvider>
        </BrowserRouter>
      </QueryClientProvider>
    );

     

    React Query는 API 요청이 Query와 Mutation으로 나누어져 있습니다.

     

    useQuery

    서버에서 데이터를 가져올 때 사용하는 요청입니다.

    • 첫 번째 인자로 Unique Key를 받습니다. 해당 Key를 통해 다른 곳에서도 데이터를 꺼내올 수 있습니다.
    // FeedDetail.tsx
    
    import { useQuery } from 'react-query';
    import { getFeed } from '../services/feed';
     
    const { isLoading, isError, data, error } = useQuery(
      ['getFeed', _id], // queryKey: 데이터를 캐시할 때 사용하는 Unique Key
      () => getFeed({ _id }) // fetchFn: Promise를 반환하는 함수
    );
    
    if (isLoading) {
      return <Loading />;
    }
    
    if (isError && isAxiosError(error)) {
      return <NotFound />;
    }
    
    if (!isLoading && !data?.data) {
      return <NotFound />;
    }
    
    // ...

     

    useMutation

    서버의 데이터 변경(생성, 수정, 삭제)을 요청할 때 사용합니다.

    // FeedDetail.tsx
    
    import { toggleLikeFeed } from '../services/feed';
    
    const queryClient = useQueryClient();
     
    const { mutate: likeFeedMutation } = useMutation(
      // mutation 요청을 수행하기 위한 Promise를 반환하는 함수
      toggleLikeFeed, {
        // 성공 시 실행
        onSuccess: () => {
          // Mutation이 성공하면 쿼리를 refetch 함
          queryClient.invalidateQueries(['getFeed', _id]);
        },
        // 에러 발생 시 실행
        onError: (error: TError) => {
          if (error.response.status === 401) {
            alert('로그인 후 이용해주세요.');
          }
        setIsLiked((prev) => !prev);
      },
    });

     

    React Query에는 이 밖에도 많은 옵션과 다양한 기능들이 있습니다. React Query 공식 홈페이지에서 필요한 상황에 따라 알맞은 기능을 찾아보고 적용하는 것이 좋을 것 같습니다. 참고로 React Query의 useInfiniteQuery는 무한 스크롤을 간편하게 만들 수 있는 기능이며 다음 포스팅에서 다루겠습니다!  

     

     

    🧗‍♀️ 제가 진행한 프로젝트가 궁금하다면

    🔽 프론트엔드는 이곳에서 확인하실 수 있습니다.

    https://github.com/Team-Madstone/doljabee-fe

     

    GitHub - Team-Madstone/doljabee-fe: Climbing Community

    Climbing Community. Contribute to Team-Madstone/doljabee-fe development by creating an account on GitHub.

    github.com

     

    🔽 백엔드는 이곳에서 확인하실 수 있습니다.

    https://github.com/Team-Madstone/doljabee-be

     

    GitHub - Team-Madstone/doljabee-be: Climbing Community

    Climbing Community. Contribute to Team-Madstone/doljabee-be development by creating an account on GitHub.

    github.com

     

     

    참고 자료

    https://tanstack.com/query/v3/

    https://tech.kakaopay.com/post/react-query-1/

    https://www.youtube.com/watch?v=MArE6Hy371c&t=2195s

    반응형

    댓글

Designed by Tistory.