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}
</>
)
}
옵션인 딜레이를 주어도 마찬가지로, 요청은 3번만 반복된다.
중복되는 요소가 있을 때 RTK 쿼리는 하나의 요청만 하고 두 요소는 동기화 되게 된다.
'개발 공부 시리즈 > Redux-ToolKit' 카테고리의 다른 글
[RTK] Typescript todoList 만들기 (6) - webpack 적용해보기 (0) | 2022.11.02 |
---|---|
[RTK] Typescript todoList 만들기 (5) - eslint, prettier 적용해보기- 2 (0) | 2022.11.01 |
[RTK] Typescript todoList 만들기 (5) - eslint, prettier 적용해보기- 1 (0) | 2022.11.01 |
[RTK] Typescript todoList 만들기 - (4) 컴포넌트 분리하기 (0) | 2022.10.28 |
[RTK] Typescript todoList 만들기 - (3) 삭제 기능 만들기 (2) | 2022.10.27 |
댓글