티스토리 뷰

생활코딩 이고잉님의 유투브 `React 서버 통신에 회의가 든다면 - RTK Query`를 기반으로 작성하였습니다.

Server Hook 사용법

./app/api 의 api 객체를 이용하기

RTK Query로 서버와 통신

읽기: useGetCountQuery ({name})

쓰기: userSetCountMutation()

-첫번째 원소 함수({name, value}) 로 데이터 전송

 

RTK Query의 특징

1️⃣ use-Query는 읽기 전용(read-only)

- 객체를 리턴 // {}

- data, isFetching, isLoading 중요

  • isLoading은 state가 초기화되기 전(undefined)일때 첫번째 로딩시
  • isFetching은 loading시마다 실행

- 자동실행

2️⃣ use-Mutation은 쓰기 전용

- 배열을 리턴 // []

- 첫번째 원소 함수로 수동실행

- 첫번째 원소 함수는 promise로 응답 값 전달(서버에 요청한 응답값을 바로 받아서 즉시 사용할 수 있음 async-await을 같이 활용)

  • 추가 설명: 첫번째 원소인 함수는 promise를 리턴한다. => 즉 이 함수를 async await으로 감싸고 변수에 결과값을 담아주면 바로 확인해 볼 수 있다.

- 두번째 원소 객체의 isLoading

  • useMutation은 isFetching이 따로 없이 isLoading만 존재 => 서버와 데이터를 통신할 때마다 isLoading가 true값으로 바뀐다. 

서버 캐시

클라이언트에서 임시로 가지고 있는 서버의 데이터(언제든지 변할 수 있는) 

각각의 컴포넌트가 구독하고 있는 전역 저장소의 서버 데이터 정도로 이해했다. 클라이언트 상태관리 라이브러리와 다른 점이라고 하면 `태그`라는 개념을 사용하여 이 `태그`를 기준으로 최신값을 서버에서 가져와 업데이트할 수 있다. 

  • redux devtools로 확인가능하다. 하단에 RTK Query를 클릭하면 된다.

 

RTK Query는 Redux와 같이 사용할 수도 있고 단독으로 사용하는 것도 가능하다.

RTK Query (without Redux)

src>app>api.js

*참고로 redux toolkit에서 권장하는 폴더 구조가 있는 거 같습니다. 확인 필요

import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";

// /count의 GET / POST / PATCH / DELETE 등 operation하나를 endpoint라고 한다.

// createApi함수는 각각의 엔드포인트(에시에서 getCount)를 자동으로 갱신할 수 있는 hook을 생성해준다. e.g. useGetCountQuery
export const api = createApi({
   baseQuery: fetchBaseQuery({ baseUrl: "https://example.com/api" }),
   //tagTypes: 전역적으로 사용하고 있는 태그 이름 지정
   tagTypes: ["Count"],
   endpoints: (builder) => ({
      getCount: builder.query({
         query: ({ name }) => `count/${name}`,
         // 서버 캐시를 컴포넌트가 구독하고 있는데 이 캐시가 비게되면 서버에서 자동으로 새 값을 넣어준다.
         // result는 서버 데이터값, error는 에러났을 때 내용, arg는 useGetCountQuery({name})에서 name이 arg로 들어간다.
         providesTags: (result, error, arg) => [
            { type: "Count", id: arg.name },
         ],
      }),
      setCount: builder.mutation({
         //뮤테이션의 경우는 쿼리의 프로퍼티로 함수를 지정해주면 된다.
         //뮤테이션 훅이 반환하는 배열의 첫번째 원소인 함수로 전달받는 인자가 query 함수의 매개변수로 자동으로 들어오게 된다.

         //query에는 서버와 통신하는 정책을 명시해주는 곳이라고 생각하면 된다.
         query: ({ name, value }) => {
            return {
               url: `count/${name}`,
               method: "POST",
               body: { value },
            };
         },
         //서버 캐시를 무효화만들어서 지워버린다 라는 의미,
         //이 아래의 태그에 해당하는 서버 캐시를 지워버린다는 의미 => 서버 캐시가 지워졌으니 새로운 값을 서버에서 update하겠죠?
         invalidatesTags: (result, error, arg) => [
            { type: "Count", id: arg.name },
         ],
      }),
   }),
});
  • 여기서 만든 api를 index.jsx에서 주입해줘야한다. (vite에서는 main.jsx)

src>main.jsx(index.jsx와 같습니다. vite를 사용하고 있어 이름이 다름!)

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.jsx";
import "./index.css";
import { ApiProvider } from "@reduxjs/toolkit/dist/query/react";
import { api } from "./app/api.js";

ReactDOM.createRoot(document.getElementById("root")).render(
   <React.StrictMode>
      <ApiProvider api={api}>
         <App />
      </ApiProvider>
   </React.StrictMode>
);
  • ApiProvider 컴포넌트를 임포트해오고 위에서 만들어 놓은 api를 프롭으로 넘겨준다.
  • 끝 ㅎ

RTK Query (with Redux)

createApi로 만든 api를 store에 등록해줘야 한다. 이때, api에 추가되는 것이 있는데 바로 reducerPath이다.

  • reducerPath의 값은 state의 이름이라고 생각하면 된다.

src>app>api.js

export const api = createApi({
   reducerPath: "countApi", // 기본적으로 정하지 않으면 api로 된다.
   baseQuery: fetchBaseQuery({ baseUrl: "https://example.com/api" }),
   //tagTypes: 전역적으로 사용하고 있는 태그 이름 지정
   tagTypes: ["Count"],
   // (코드 중략)

 store를 만들어놓지 않았으니 store를 만들어준다.

src>app>store.js

import { configureStore } from "@reduxjs/toolkit";
import { api } from "./api";

//스토어에 리듀서를 등록해주고, 미들웨어도 등록(이 로직은 저도 잘 모름)
export const store = configureStore({
   reducer: {
      //api state이름과 api로 자동생성된 리듀서 함수도 등록
      [api.reducerPath]: api.reducer,
   },
   middleware: (getDefaultMiddleware) =>
   getDefaultMiddleware().concat(api.middleware)
});

다시 index.jsx(vite: main.jsx)로 돌아와 만들어놓은 store를 주입해준다.

기존 리덕스처럼 Provider컴포넌트에 store를 주입해주면 된다.

src>main.jsx

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.jsx";
import "./index.css";
import { Provider } from "react-redux";
import { store } from "./app/store.js";

ReactDOM.createRoot(document.getElementById("root")).render(
   <React.StrictMode>
      <Provider store={store}>
         <App />
      </Provider>
   </React.StrictMode>
);

 

 

댓글