ReduxToolkit Query에 대해서 잘 정리된 문서 / 영상을 찾아보기가 굉장히 힘들었고
특히 한국어로 되어있는 양질의 문서는 거의 전멸에 가까운 수준이었습니다.
그나마.. 영미권 유튜브로 눈길을 돌려보면 좋은 자료들이 있었지만
그럴바에는 그냥 잘 정리된 공식문서를 보면서 해나가는 게 낫겠다.라는 결론을 내렸습니다.
제목이 Slow Start인 이유는 공식문서를 따라가며 천천히 각 기능들을 파악하는 시간을 가지면서
시작을 하고 싶은 사람(저같은 사람)들을 위해 작성했기 때문입니다.
🐕 공식문서를 따라서.
https://redux-toolkit.js.org/tutorials/rtk-query
RTK Query를 사용하는 것에 있어서 리덕스와 ReduxToolkit에 대한 지식은 별도로 필요하지 않다고 합니다.
하지만 당연히 사전지식이 있으면 좀 더 이해하기 쉽겠죠?
여유가 되신다면 Redux와 ReduxToolkit도 사용법을 익혀보시는 것을 추천드리며
RTK Query는 공식문서에 따르면 TypeScript 로 완전히 작성되었기에
TypeScript를 이용한 사용경험도 좋다고 합니다.
우선은 RTK Query를 왜 쓰는가. 부터 짚어보고 사용법을 학습하겠습니다.
Redux Toolkit 문서에서는 RTK Query를 다음과 같이 설명합니다.
웹애플리케이션에서 데이터를 요청하고 받아오는 일반적인 경우 구현하는 기능들이 있습니다.
예를 들면 데이터를 요청 중이라는 것을 나타내기 위한 isLoading state를 만들어 사용한다든지말이에요!
그 외에도 비동기 데이터들은 잘 관리하기가 애매한 부분이 있는데 그 부분들을
RTK Query는 직접 로직을 작성하지 않고도 쉽게 기능을 가져다 쓸 수 있게 구현된 툴이라는 것입니다.
웹이 발전해나가면서 웹 애플리케이션은 서버에 데이터를 요청하는 경우가 많아졌고
그 데이터를 업데이트하며 업데이트 내용을 서버로 보내고
클라이언트에 캐시된 데이터를 서버의 데이터와 동기화된 상태로 유지해야하는 필요성이 생겨났습니다.
또한 앞서 서술했듯 로딩 상태에서 보여줄 창 , 에러가 발생했을때 처리할 로직, 데이터를 잘 받아왔을때 보여줄 뷰 등
데이터의 요청 상태 및 상황에 따라 다른 동작을 구현해야했기 때문에 더더욱 복잡도가 높아졌습니다.
하지만 redux에서는 이런 어떤 애플리케이션에나 필요하고 이 문제들을 해결할 수 있는 기능을 제공하지 않았습니다.
createAsyncThunk, extraReducers 등의 내장함수를 제공하기는 하지만 상당한 수고가 필요했어요
그러던 와중 리액트를 쓰는 사람들은
우리가 지금까지 나열한 문제점의 원인인 데이터를 가져오고 / 데이터를 캐싱하는 일들이
실제 상태관리와는 조금 다른 관심사라는 것을 깨닫게 되었답니다.
We're no longer thinking about "managing state"
we now think about "managing cached data"
그래서 이부분도 재밌습니다. 우린 더이상 상태를 관리하는 것을 생각하지 않고
캐시 데이터를 관리하는 것에대해 생각하게 될 것이다. 라는 얘기에요!
하여간 그런 와중에 React Query , SWR과 같은 데이터 페칭에 있어서 하나의 솔루션이 되는 툴들이 인기를 끌었고
RTK Query는 그 툴들에서 영감을 얻어 제작되었다.
까지가 재밌는 역사 얘기입니다.
이제 RTK Query의 핵심 기능들을 살펴본 다음 직접 RTK Query를 이용해
데이터를 받아오는 간단한 애플리케이션을 제작해보도록 하겠습니다.
👻createApi와 fetchBaseQeury()
import { createApi } from '@reduxjs/toolkit/query'
/* React-specific entry point that automatically generates
hooks corresponding to the defined endpoints */
import { createApi } from '@reduxjs/toolkit/query/react'
앞서 서술했듯이 RTK Query는 기본적으로 @Redux-toolkit에 내장되어있습니다.
다만 @reduxjs/toolkit과 같은 경로로 접근하면 사용할 수 없기때문에 경로를 잘 지정해주어야합니다.
createApi 함수에 접근하기 위한 진입점 솔루션은 위 예제에서 살펴볼 수 있듯이 총 두가지가 있습니다.
CreateApi 함수는 RTK Query의 핵심 기능 중 하나로서
데이터를 가져오고 변환하는 방법에 대한 구성을 포함하며
endpoint를 통해 데이터를 검색할 수 있는 방법들을 제공해줍니다.
기본 url당 하나의 api slice를 원칙으로합니다.
공식문서의 설명은 다음과 같습니다.
create라는 이름에서 알 수 있듯이 무언가를 생성하는 함수입니다.
자세한 사용법은 fetchbasequery를 본 다음에 알아보고 지금은 일단 이런걸 사용해야하는구나 정도로 넘어가겠습니다.
// Or from '@reduxjs/toolkit/query/react'
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
다음은 fetchBaseQuery입니다.
이 fetchBaseQuery는 createApi 함수와 단짝처럼 붙어다니는 친구입니다.
fetchBaseQuery는 HTTP 요청을 단순화하는 것을 목표로하는 함수입니다.
fetch, axios와 같은 함수들을 완전히 대체하지는 않지만
대부분의 HTTP 요청에서 필요한 요구사항들은 충분히 잘 처리해줍니다.
또한 fetchBaseQuery는 모든 요청이 json이라고 가정합니다.
json이 아닌 요청을 처리하려면 따로 설정을 해주면 된다 정도로만 알고 일단 넘어가겠습니다.
이 fetchBaseQuery는 createApi의 baseQuery를 형성할 때 많이 사용하게 됩니다.
무슨 값을 반환하는 지 궁금하니까 한번 console을 찍어보도록 합시다.
console.log('fetchbasequery반환값', fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }));
이렇게 코드를 작성하고 console을 찍어봤습니다.
fetchBaseQuery는 클로저를 반환하는 함수였군요!
그리고 클로저에는 우리가 작성해준 baseUrl이 잘 들어가 있는 것을 확인할 수 있네요
export function fetchBaseQuery({
baseUrl,
prepareHeaders = (x) => x,
fetchFn = defaultFetchFn,
paramsSerializer,
isJsonContentType = defaultIsJsonContentType,
jsonContentType = 'application/json',
jsonReplacer,
timeout: defaultTimeout,
responseHandler: globalResponseHandler,
validateStatus: globalValidateStatus,
...baseFetchOptions
}: FetchBaseQueryArgs = {}): BaseQueryFn<
string | FetchArgs,
unknown,
FetchBaseQueryError,
{},
FetchBaseQueryMeta
> {
if (typeof fetch === 'undefined' && fetchFn === defaultFetchFn) {
console.warn(
'Warning: `fetch` is not available. Please supply a custom `fetchFn` property to use `fetchBaseQuery` on SSR environments.'
)
}
return async (arg, api) => {
const { signal, getState, extra, endpoint, forced, type } = api
let meta: FetchBaseQueryMeta | undefined
let {
url,
headers = new Headers(baseFetchOptions.headers),
params = undefined,
responseHandler = globalResponseHandler ?? ('json' as const),
validateStatus = globalValidateStatus ?? defaultValidateStatus,
timeout = defaultTimeout,
...rest
} = typeof arg == 'string' ? { url: arg } : arg
let config: RequestInit = {
...baseFetchOptions,
signal,
...rest,
}
다음은 fetchBaseQuery 함수의 내용입니다.
누가 대신 읽어주시기 바라고요
이제 createApi와 fetchBaseQuery 함수를 이용하여 createSlice와 비슷한 작업을 해보도록 하겠습니다.
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
export const pokemonApi = createApi({
// Set the baseUrl for every endpoint below
baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }),
endpoints: (builder) => ({
getPokemonByName: builder.query({
// Will make a request like https://pokeapi.co/api/v2/pokemon/bulbasaur
query: (name: string) => `pokemon/${name}`,
}),
updatePokemon: builder.mutation({
query: ({ name, patch }) => ({
url: `pokemon/${name}`,
// When performing a mutation, you typically use a method of
// PATCH/PUT/POST/DELETE for REST endpoints
method: 'PATCH',
// fetchBaseQuery automatically adds `content-type: application/json` to
// the Headers and calls `JSON.stringify(patch)`
body: patch,
}),
}),
}),
})
위 예제는 리덕스툴킷 공식문서가 제공하는 타입스크립트 사용시 작성 솔루션입니다.
하나하나 의미를 살펴보면 그리 어렵지 않게 파악할 수 있습니다.
baseQuery | 말그대로 기본이 되어 줄 url을 지정해주는 것입니다. 실제 로직에선 param이나 query를 이용해 url을 요청하게 될 것이지만 항상 base가 되는 url 주소는 동일할 것입니다. 그 url 주소를 명시하는 것이며 이때 fetchBaseQuery 함수를 사용합니다. |
endpoints | endpoint를 지정해주는 영역입니다. 값에는 endpoint를 조작해줄 함수가 담긴 객체가 들어가게 됩니다. |
createApi의 인자에 작성할 필수적이라고 할 수 있는 key 값들은 저 두개가 다입니다.
다음은 선택인자입니다. 필수로 넣는 건 아니고 필요할때만 넣으시면 됩니다.
사용법은 공식 문서에서 그때그때 찾아서 하시는걸 추천드려요
https://redux-toolkit.js.org/rtk-query/api/fetchBaseQuery
prepareHeaders | 모든 요청에 헤더를 삽입할 수 있습니다. authorization 과 같은 인증과 관련된 로직을 처리할 때 유용하다고 합니다. |
paramSerializer | 매개변수로 전달된 데이터에 사용자 정의 변환을 적용하는 데 사용할 수 있습니다. |
timeout | 최대 요청 대기 시간을 표기하는 숫자입니다. |
fetchFn | fetch 함수가 들어가는 키입니다. SSR 환경에서 유용하다고 합니다. |
isJsonContentType | 뭐하는 애인지 모르겠음 ㅋㅋ;;ㅎㅎ;;ㅈㅅ;; |
jsonContentType | 명시적인 콘텐츠 유형 헤더가 없는 경우 콘텐츠 유형 헤더를 자동으로 설정할 때 사용합니다. default 값은 "application/json"입니다. |
선택인자는 많기는 하지만 단 두가지밖에 없는 필수 인자로만 구성된 코드를 읽는것도
꽤 어렵게 느껴지는 것은 우리가 아직 코드에 낯설기 때문입니다.
코드를 한줄한줄 읽어나가봅시다.
baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }),
baseQuery 키를 지정하는 영역입니다. fetchBaseQuery의 반환값을 담아주는 것을 알 수 있습니다.
baseQuery는 서버에서 데이터를 가져오는 방법을 알고 있는 함수가 들어가야하는 자리입니다.
따라서 우리의 fetchBaseQuery함수가 반환해준 클로저 함수는
baseQuery에 들어가기 딱 알맞은 형태를 가지고 있다는 것입니다!
endpoints: (builder) => ({
getPokemonByName: builder.query({
// Will make a request like https://pokeapi.co/api/v2/pokemon/bulbasaur
query: (name: string) => `pokemon/${name}`,
}),
updatePokemon: builder.mutation({
query: ({ name, patch }) => ({
url: `pokemon/${name}`,
// When performing a mutation, you typically use a method of
// PATCH/PUT/POST/DELETE for REST endpoints
method: 'PATCH',
// fetchBaseQuery automatically adds `content-type: application/json` to
// the Headers and calls `JSON.stringify(patch)`
body: patch,
}),
}),
}),
다음은 endpoints를 보도록 하겠습니다.
endpoints에는 builder 함수가 들어가는데 이 builder 함수는 객체를 반환합니다.
그런데 어떤 값을 가지고 있는 객체냐면 우리가 endpoint를 조작하게 해줄 객체를 반환합니다.
작성 양식은 정말 간단하게 표현하면 다음과 같습니다.
endpoints: (builder) => ({
getCount: builder.query({
query: (pageNumber: string) => `posts/${pageNumber}`,
}),
이런식으로 작성할 수 있는거죠!
builder라는 매개변수를 받아서 객체를 반환시키는 함수를 작성할 것인데
그 반환하는 객체의 키값에는 우리가 사용할 함수의 이름을 지어주도록 하고
밸류로는 builder.query()함수의 반환값을 담도록합니다.
이런형식으로 작성하는 패턴을 builder Callback 표기법이라고 부릅니다.
bulder.addCase(actionCreator , reducer) | 액션 타입과 매핑되는 리듀서를 추가해서 action을 처리합니다. addMatcher, addDefaultCase보다 먼저 작성되어야합니다. |
builder.addMatcher(matcher,reducer) | 새로들어오는 모든 액션에 대해 주어진 패턴과 일치하는지 확인하고 리듀서를 실행합니다. |
builder.addDefaultCase(reducer) | 말그대로 default 케이스일 때 실행되는 리듀서입니다. 어떠한 case, matcher리듀서도 실행되지 않았을때 실행됩니다. |
자바스크립트로 리덕스툴킷을 입문하신 분들에게는 낯선 문법일 수 있지만
타입스크립트의 경우 호환성을 위해 Builder Callback 표기법을 통해 작성하는 것을 권장한다고 합니다.
반면 저를 더불어 자바스크립트와 더 친한 개발자시라면 이 표기법이 더 익숙하실 것입니다.
const counterReducer = createReducer(0, {
increment: (state, action) => state + action.payload,
decrement: (state, action) => state - action.payload
})
편안하네요 이러한 표기법을 Map Object 표기법이라고 부릅니다.
createReducer 함수로 감싸주는 작업은 생략해도 되지만
builder 패턴 자체는 계속 사용하기 때문에 기억해두시면 좋겠습니다.
공식문서에서는 prepareHeaders를 이용하여 api 요청에 대한 헤더를 자동으로 설정해주는 작업
등을 설명해주지만 이는 분량상 스킵하도록 하겠습니다.
마지막으로 정리하고 가봅시다.
fetchBaseQuery 특 |
기존의 axios, fetch와 비슷한 역할을 수행하는 함수다. |
클로저를 반환하는 함수이다. |
모든 응답은 json일 것이라고 생각한다. |
상태 코드가 2xx가 아닌 모든 응답을 거부하며 오류로 설정한다. |
기본 시간 제한 값이 설정되어 있지 않기때문에 요청을 해결하거나 브라우저의 기본 시간제한(보통 5분)에 도달하기 전까진 요청이 보류상태로 유지된다. 따라서 이걸 원하지 않는 경우 timeout 속성의 추가를 고려할 수 있다. |
🥶 endpoints로 조금 더 깊게
엔드포인트는 서버에 대해 수행하려는 작업의 집합이라고 할 수 있습니다.
이 엔드포인트는 앞서 살펴본것과 같이 빌더 구문을 사용해서 객체로 정의해주는 것이 기본인데
엔드포인트에는 query와 mutation 두개의 기본 유형이 존재합니다.
endpoints: (build) => ({
getPosts: build.query<PostsResponse, void>({
query: () => 'posts',
providesTags: (result) =>
result ? result.map(({ id }) => ({ type: 'Posts', id })) : [],
}),
addPost: build.mutation<Post, Partial<Post>>({
query: (body) => ({
url: `posts`,
method: 'POST',
body,
}),
invalidatesTags: ['Posts'],
}),
}),
이렇게 build.query / build.mutation으로 작성하는 형태입니다.
주목할 점은 query 부분에 콜백함수가 들어간다는 것입니다.
그리고 이 callback 함수는 문자열 혹은 객체를 반환해야 합니다.
요구하는 내용은 네트워크 통신에 익숙하시다면 대충 코드만 봐도 이해가 되시겠네요!
객체를 통해 여러정보를 주는 경우엔 endpoint에 반영되길 원하는 url을 url 키의 밸류에 할당하면 됩니다!
우리에게 필요한 모든 로직을 이제 createApi slice하나에 모두 넣어두고 관리할 수 있어졌다는 것도
눈 여겨 볼만한 지점입니다.
또한 위 예제에서 getPosts는 builder.query를 호출하는것을 볼 수있습니다.
이 getPosts 메서드는 data를 리턴하게 되며 내부의 함수가 리턴하는 문자열이 엔드포인트가 된다는 것
기억하고 가겠습니다.
🌞간단한 실습을 통해 친해져보기
스택빌츠등을 활용해 간단하게 예제를 보여드리는 방법도 있지만
이미 그런 예제는 많으니까 local 환경에서 진행해보겠습니다.
리액트 / TS 를 사용하고있다는 가정으로 작성했습니다.
npm install react-redux @reduxjs/toolkit
먼저 react-redux와 RTK를 설치해주겠습니다.
src/api/api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
export const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: 'https://jsonplaceholder.typicode.com' }),
endpoints: (builder) => ({
getCount: builder.query({
query: (pageNumber: string) => `posts/${pageNumber}`,
}),
}),
});
export const { useGetCountQuery } = api;
디렉토리를 하나 만들고 api.ts 파일을 생성해주겠습니다.
파일,폴더명은 아무렇게나 해도 되지만 관용적인 표현으로 생성했습니다.
간단한 실습을 위해 서버단을 만드는 과정은 생략하고
fake API중 하나인 jsonplaceholder를 이용하겠습니다.
또한 타입스크립트로 작성되었지만 따로 세세한 설정은 해주지 않겠습니다.
나중에 세세한 타입 설정이 필요하시다면
https://redux-toolkit.js.org/rtk-query/usage-with-typescript
위 링크를 참고하세요
createApi와 fetchBaseQuery를 불러오고 쭉 작성해줍니다.
그리고 createApi의 결과물과 useGetCountQuery를 객체디스트럭처링할당해서 내보내줍니다.
엥..? 근데 우린 useGetCountQuery같은건 만든적이 없지않나요?
createApi로 우리가 만든 api 변수를 콘솔에 찍어보면 다음과 같은 객체가 찍힙니다.
잘 찾아보면 useGetCountQuery / useLasyGetCountQuery 가 보이네요!
우리가 enpoints에서 지정해준 getCount 함수의 앞뒤에 use와 query가 붙어서 나온걸 확인할 수 있네요!
이제 내보내기를 수행해줬으니 configuring을 해보겠습니다.
다른 파일을 생성해주고 configureStore를 import 해줄게요
export const { useGetCountQuery } = api;
api에서 { useGetCountQuery }를 구조분해할당하여 내보내기해줍니다.
이런식으로 작성하는 것은 typescript 4.1 v 이상부터 가능하니까 다들 타입스크립트 버전을 체크하세요.
store/store.ts
import { configureStore } from '@reduxjs/toolkit';
import { setupListeners } from '@reduxjs/toolkit/query';
import { api } from '../api/api';
export const store = configureStore({
reducer: {
[api.reducerPath]: api.reducer,
},
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(api.middleware),
});
setupListeners(store.dispatch);
우리가 만든 apislice를 가져와보겠습니다.
필요한 항목은 다음과 같습니다.
기존 상태관리와 RTK Query가 다른 점은 api 슬라이스는 cache reducer를 상태에 추가할 수 있다는 것입니다.
또한 API슬라이스는 스토어에 추가해야하는 (must be added) 사용자 정의 미들웨어를 생성합니다.
reducer:{ [api.reducerPath] : api.reducer} 부분이 이해가 잘 되지 않을 수 있습니다.
이는 계산된 프로퍼티 이름이라고 하는 ES6에 추가된 문법입니다.
https://xionwcfm.tistory.com/242
계산된 속성 이름이 낯설다면 위 링크를 참고하세요
caching reducer가 올바른 위치에 추가되도록 하기 위해서 위와 같이 작성해줍니다.
또한 API Slice는 사용자 정의 미들웨어를 생성한다고 했습니다.
따라서 middleware key값도 설정을 해주겠습니다.
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(api.middleware)
우린 redux-thunk를 사용할때처럼 기존의 표준 미들웨어를 유지해야 합니다.
따라서 API Slice의 미들웨어는 일반적으로 이런 양식을 따릅니다.
middleware key가 제공한 getDefaultMiddleware 메서드를 호출해서 반환된 미들웨어 배열의 끝에
apiSlice.middleware를 추가하는 것으로 이것을 수행할 수 있습니다.
이렇게 미들웨어 속성을 사용하면 스토어의 디스패치 프로세스에 미들웨어를 추가할 수 있어요
미들웨어 함수가 하는 역할은 다음과 같습니다.
이 getDefaultMiddleware 함수는 redux-thunk와 redux-logger를 포함하며
@reduxjs/toolkit에서 사용을 권장하는 redux 미들웨어의 배열을 반환해줍니다.
이건 미들웨어가 없는 일반적인 리덕스의 데이터 흐름을 나타낸 다이어그램입니다.
원래는 이런 느낌의 우리에게 익숙한 FLUX 구조로 데이터가 흘렀다면
비동기와 API Middleware가 곁들여진 로직은 다음과 같이 이해할 수 있습니다.
위와 같이 전달되는 action을 미들웨어가 가로채서 캐싱하고 디스패처에게 수정한 액션을 전달합니다.
이것이 미들웨어가 추가된 형태의 다이어그램입니다.
😋간단한 실습을 통해 친해져보기 2
이제 우리가 만든 api store를 Provider를 통해 연결시켜주고
직접 사용까지 해보겠습니다.
main.tsx(vite 사용시) / index.tsx(cra 사용시)
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './index.css';
import { Provider } from 'react-redux';
import { store } from './store/store';
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<Provider store={store}>
<App />
</Provider>
);
우리가 만든 store를 import 해와서 연결시켜줍니다.
이제 App.tsx로 넘어가겠습니다.
App.tsx
이렇게 api 요청을 보내 받아온 데이터를 화면에 렌더시키는 것이 우리가 시도해보고자 하는 애플리케이션입니다.
import { useState } from 'react';
import './App.css';
import Counter from './components/Counter';
import { api } from './api/api';
import { useGetCountQuery } from './api/api';
import { fetchBaseQuery } from '@reduxjs/toolkit/query/react';
function App() {
const [checked, setChecked] = useState<boolean>(false);
const onClick = (): void => {
setChecked((bool) => !bool);
};
const { data, error, isLoading } = useGetCountQuery('1');
console.log('data입니다.', data);
console.log('error상태입니다.', error);
console.log('loading중입니다.', isLoading);
return (
<div className="App">
<button className={'dark:md:hover:bg-fuchsia-600 '} onClick={onClick}>
Save changes
</button>
<div className=" m-5 bg-orange-900 ">
{error ? (
<div>여기 에러났어요</div>
) : isLoading ? (
<div>로딩중입니다만?</div>
) : data ? (
<div>
<h3>{data.title}</h3>
</div>
) : null}
</div>
<Counter />
</div>
);
}
export default App;
tailwind css 를 사용하지 않으시는 분들은 className을 비워주시면 되겠습니다.
데이터 흐름을 쉽게 파악하기 위해 console.log()도 추가해두었습니다.
useGetCountQuery는 우리가 열심히 만들어준 query입니다.
useGetCountQuery의 반환값에는 이런게 들어있습니다.
data | 실제로 우리가 전달받은 data가 담깁니다. |
isError | 에러가 발생했는지 여부를 불린형태로 받습니다. |
isFetching | fetching 상태를 불린형태로 받습니다. |
isLoading | loading 상태를 불린형태로 받습니다. |
isSuccess | 성공 여부를 불린형태로받습니다. |
그 외에도 많은 키값들이 있지만 필요한 부분만 디스트럭처링 할당해서 사용하겠습니다.
isFetching은 서버와 통신할때마다 실행되지만
isLoading은 최초 한번만 실행된다는 차이점이 있으니 이것도 가볍게 알아두고 넘어가겠습니다.
또한 mutation을 이용해 작성하게되면 use ~ query의 형태가 아니라
use ~ mutation의 형태가 되는데 useMutation의 경우엔 첫번째 원소의 함수로 수동실행한다는 점
기억해두고 갑시다.
use~ query | use ~ mutation |
함수가 실행되면 자동실행된다. | 함수가 반환하는 배열의 첫번째 원소에 담긴 함수를 호출하는 것을 통해 실행한다. |
const [mutation] = useSetCountMutation()
mutation()
대충 요런 형태라고 생각하시면 되겠습니다.
또한 useMuatation의 반환함수는 Promise를 반환합니다.
따라서 Mutation을 사용했을때 서버의 응답값을 바로 화면에 반영시키고싶은 경우
async , promise 후속처리 메서드 등을 이용해서 바로 화면에 반영시켜주는 것도 가능합니다.
다시 query로 돌아가서 query를 하나하나 console.log로 찍어보겠습니다.
먼저 처음 상태입니다.
data는 undefined
error 상태도 undefined
loading은 true입니다.
그리고 fetchBaseQuery가 fetching이 완료되었다는 신호를 전달하자
리렌더링이 되며 data가 받아와지고 loading 상태가 false로 바뀌는 것을 확인할 수 있습니다.
따라서 우리는 isLoading / error / data의 상태에 따라서
분기처리만 제대로 해주면 로딩창 / 에러창 / 원래보여주고싶은창을
적절히 보여줄 수 있을 것입니다. 너~무 편리하겠네요
{error ? (
<div>여기 에러났어요</div>
) : isLoading ? (
<div>로딩중입니다만?</div>
) : data ? (
<div>
<h3>{data.title}</h3>
</div>
) : null}
이런식으로 작성을 할 수 있습니다.
에러가 truthy한 값으로 변한다면 여기 에러났어요가 출력될것입니다.
아니라면 isLoading이 true인 경우 로딩중입니다만이 출력될것입니다.
isLoading이 falsy가 된 경우 data의 상태가 truthy하면 데이터를 출력하고
data가 제대로 되어있지않은경우엔 화면에 아무것도 표시하지않겠습니다.
if문으로 얼리리턴하는 방식을 사용하셔도 상관은 없습니다.
취향차이입니다.
이제 간단한 예제를 만들어 보는 것 까지 완료했습니다.
이렇게 redux devtools 크롬 확장을 설치해서 사용해보시면
상태와 과정을 자세하게 찾아볼 수 있기때문에 확장과 함께 학습하시는걸 추천드리며
마무리하도록 하겠습니다.
RTK Query는 굉장히 많은 편의 기능들이 있고 useMutation은 제대로 다루지도 않았으며
미들웨어에 대해서도 다루지 않고 넘어갔습니다.
하지만 이미 분량이 너무 많아지기도 했고 당장 간단한 Get 요청만 보내고자하는 경우에는
이정도 내용만 이해하고 있어도 사용에는 문제가 없을 것입니다.
🐶레퍼런스
https://blog.hwahae.co.kr/all/tech/6946
https://redux.js.org/tutorials/essentials/part-7-rtk-query-basics
https://redux-toolkit.js.org/rtk-query/overview
https://github.com/reduxjs/redux/issues/653
다이어그램 출처.
'redux' 카테고리의 다른 글
RTK Query 데이터에 local Storage를 사용하는 방법 (0) | 2023.05.15 |
---|---|
RTK Query의 Middleware에 대하여 (0) | 2023.05.14 |
updateQueryData RTKquery로 클라이언트에서만 쿼리 데이터를 조작하고 싶다면 (1) | 2023.05.14 |
Redux와 Redux-toolkit (0) | 2023.04.15 |
상태관리 라이브러리 리덕스 이해하기 with parcel.. (0) | 2023.04.03 |