본문 바로가기
개발 공부 시리즈/Redux-ToolKit

[RTK] RTK-Query 공식문서보며 직접 사용해보기

by 그레이웅 2022. 11. 7. 18:05
반응형

RTK-Query 공식문서 보며 직접 사용해보기

 

RTK 공식문서에 있는 Query 예제를 따라 해 보면서 실습하겠다.

 

나는 빠른 진행을위해 타입 스크립트 rtk 템플릿을 다운로드하였다.

 

// Redux + TypeScript template
// 가운데 rtk-ts는 자신이 생성할 프로젝트 명이다.

npx create-react-app rtk-ts --template redux-typescript

 

 


1. createApi 작성

 

 

/src 폴더 하위에 service 폴더를 만들고 pokemon.ts 파일을 생성한다.

생성 후 아래와 같이 작성한다.

 

/src/service/pokemon.ts    

 

//pokemon.ts

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


export const pokemonApi = createApi({
    reducerPath : 'pokemonApi',
    baseQuery : fetchBaseQuery({ baseUrl : 'https://pokeapi.co/api/v2'}),
    endpoints : (builder) => ({
        getPokemonByName : builder.query({
            query : (name: string) => `pokemon/${name}`,
        })
    })
})

export const { useGetPokemonByNameQuery } = pokemonApi

 

- createApi() : RTK 쿼리의 기능의 핵심이다. 데이터를 패치 및 변환하는 설정을 포함 엔드포인트들에서 어떻게 데이터를 패치하는지 정의할 수 있다. 대부분은 베이스 URL 하나당 하나의 API Slice를 사용한다. 

- fetchBaseQuery() : 간단한 요청을 위한 fetch의 wrapper이다. 대부분의 사용자에게 createApi의 baseQuery로 사용된다.

- reducerPath : 서비스가 스토어에 탑재될 고유 키이다. 애플리케이션에서 두 번 이상 호출하는 경우에는 매번 고유한 값을 제공해야 한다. 기본값은 'api'이다.

- baseQuery :  쿼리를 처리하기 위해 사용하며 fetchBaseQuery와 함께 사용한다.

- endpoints : 서버에 대해 수행하려 하는 작업 집합이다. 빌더 구문을 사용하여 객체로 정의한다.

 

 


2. store에 서비스 추가하기

 

/src/app/store.ts를 다음과 같이 변경한다.

 

import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit';
import { counterSlice } from '../features/counter/counterSlice';
// Or from '@reduxjs/toolkit/query/react'
import { setupListeners } from '@reduxjs/toolkit/query';
import { pokemonApi } from '../service/pokemon';

export const store = configureStore({
  reducer: {
    counter : counterSlice.reducer,
    // Add the generated reducer as a specific top-level slice
    [pokemonApi.reducerPath]: pokemonApi.reducer,
  },
  // Adding the api middleware enables caching, invalidation, polling,
  // and other useful features of `rtk-query`.
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().concat(pokemonApi.middleware),
});

setupListeners(store.dispatch);

export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof store.getState>;
export type AppThunk<ReturnType = void> = ThunkAction<
  ReturnType,
  RootState,
  unknown,
  Action<string>
>;

 

RTK-Query는 루트 리듀서에 포함되는 슬라이스 리듀서와 데이터를 가져와 처리하는 커스텀 미들웨어를 만든다.

둘 다 store에 추가되어야 한다. 

 

미들웨어는 데이터 캐싱, 요청 취소, 폴링 등등 유용한 rtk-query의 기능들을 위한 api 미들웨어이다.

- setupListeners : refetchOnFocus, refetchOnReconnect 동작을 활성화하는 데 사용된다. 권장 값으로 리스너를 구성할 수 돼있고, 세부적으로 콜백을 사용할 수 있다.

 

  • refetchOnFocus : 이 설정을 사용하면 응용 프로그램 창이 포커스를 얻은 후 RTK 쿼리가 모든 구독된 쿼리를 가져올지 여부를 제어할 수 있다. 기본값은 false
  • refetchOnReconnect : 이 설정을 사용하면 RTK 쿼리가 네트워크 연결을 다시 얻은 후 구독된 모든 쿼리를 다시 가져올지 여부를 제어한다. 기본값은 false

 

 


3. provider 추가

/src/index.tsx 에서 애플리케이션을 provider로 감싸준다.

 

import React from 'react';
import { createRoot } from 'react-dom/client';
import { Provider } from 'react-redux';
import { store } from './app/store';
import App from './App';
import reportWebVitals from './reportWebVitals';
import './index.css';

const container = document.getElementById('root')!;
const root = createRoot(container);

root.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
);

 

 


4. 위의 작성한 쿼리를 컴포넌트에서 사용하기

 

/src/App.tsx를 다음과 같이 변경해준다.

 

import React from 'react';
import { useGetPokemonByNameQuery } from './service/pokemon';

function App() {

  // 자동으로 데이터를 패치하고 쿼리 값을 가져오는 쿼리 hook을 사용
  const { data, error, isLoading } = useGetPokemonByNameQuery('bulbasaur');

  // 각각의 hooks은 생성된 엔드포인트에서도 접근 가능함

  
  return (
    <div className="App">
      {error ? (
        <>Oh no, there was an error</>
      ) : isLoading ? (
        <>Loading...</>
      ) : data ? (
        <>
          <h3>{data.species.name}</h3>
          <img src={data.sprites.front_shiny} alt={data.species.name} />
        </>
      ) : null}
    </div>
  );
}

export default App;

 

코드를 보면 자동으로 데이터를 api를 보낸 뒤 data, status , error에 처리 여부 및 상태가 저장되게 된다. 

RTK-Query는 isLoading, isFetching, isSuccess, isError을 불리언 값으로 가장 최근에 요청에 대한 값을 제공하게 된다. 

 

 

요청 후 받은 화면

 

요청이 성공하였다.

 

 

만약 여러 개의 데이터를 요청하면 어떻게 될까?

코드를 아래와 같이 변경한다.

 

아래 코드는 여러 가지 포켓몬의 리스트를 받도록 변경한 것이다.

이 코드는 하나의 포켓몬만 불러오는 것이 아니라 여러 포켓본도 배열만큼 돌아서 불러오는 것인데,

보고자 하는 것은 배열에 'bulbasaur'가 두 가지가 있다 RTK쿼리는 같은 중복에 대한 요청은 자동으로 최적화하게 된다.

배열은 4가지라 4번 요청할 것 같지만 , 3번 요청하게 된다.

import React, {useState} from 'react';
import { useGetPokemonByNameQuery } from './service/pokemon';

//api를 보낼 데이터 배열
const pokemon = ['bulbasaur', 'pikachu', 'ditto', 'bulbasaur'];

function App() {

  const [pollingInterval, setPollingInterval] = useState(0)
 
  return (
    <div className="App">
    <select
      onChange={(change) => setPollingInterval(Number(change.target.value))}
    >
      <option value={0}>Off</option>
      <option value={1000}>1s</option>
      <option value={5000}>5s</option>
    </select>
    <div>
    //배열만큼 반복
      {pokemon.map((poke, index) => (
        <Pokemon key={index} name={poke} pollingInterval={pollingInterval} />
      ))}
    </div>
  </div>
  );
}

export default App;



//Pokemon component
export const Pokemon = ({
  name,
  pollingInterval,
}: {
  name: string
  pollingInterval: number
}) => {
  const { data, error, isLoading, isFetching } = useGetPokemonByNameQuery(
    name,
    {
      pollingInterval,
    }
  )

  return (
    <>
      {error ? (
        <>Oh no, there was an error</>
      ) : isLoading ? (
        <>Loading...</>
      ) : data ? (
        <>
          <h3>
            {data.species.name} {isFetching ? '...' : ''}
          </h3>
          <img src={data.sprites.front_shiny} alt={data.species.name} />
        </>
      ) : null}
    </>
  )
}

 

 

 

4마리의 포켓몬 리스트가 보이는 화면
하지만 요청은 3번 가게 된다.

 

옵션인 딜레이를 주어도 마찬가지로, 요청은 3번만 반복된다.

중복되는 요소가 있을 때 RTK 쿼리는 하나의 요청만 하고 두 요소는 동기화 되게 된다.

반응형

댓글