redux toolkit 에는 createAsyncThunk
를 사용해서,
thunk 를 만들어서 async 요청을 관리할 수 있도록 하는 기능이 있다.
그런데 createAsyncThunk
의 경우 async 요청의 응답을 따로 createSlice
를 활용해서 관리해줘야 해서, 코드가 늘어날 수 밖에 없다.
RTK Query는 Redux Toolkit 의 createSlice
와 createAsyncThunk
의 기능을 동시에 사용하면서,
react-query 와 유사하게 백엔드에서 넘어온 데이터를 관리할 수 있는 기능이 만들어져있다.
또한 캐싱 관련 기능도 지원하기 때문에, 불필요한 요청을 줄일 수도 있다.
그리고 post 하자마자 자동으로 get 하는 기능까지도 구현할 수 있다.
뿐만 아니라, 프론트 데이터와 api 데이터 관리하는 로직 자체를 분리시킬 수도 있습니다.
백엔드와의 요청을 통해서 받아오는 데이터 → RTK Query 로 관리
프론트엔드만의 데이터 → createSlice 로 관리
terminal
yarn create react-app 프로젝트명
yarn add @reduxjs/toolkit react-redux
최상단에 .env파일 만들어서 백엔드 주소 관리
REACT_APP_API_URL=https://jsonplaceholder.typicode.com
이렇게 작성하면 추후에 baseUrl
에 process.env.REACT_APP_API_URL
라고 작성할 수 있다.
(src/api/postApi.js)
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
export const api이름 = createApi({
reducerPath: '리듀서이름',
baseQuery: fetchBaseQuery({
baseUrl: process.env.REACT_APP_API_URL,
}),
endpoints: (builder) => ({
요청이름: builder.query({
query: (주소에 넘길 값) => 'api 주소값/(주소에 넘길 값)',
}),
}),
});
export const {
use요청이름Query
} = postApi;
endpoints
에 보내고자 하는 요청 이름을 명시하면,
자동으로 use요청이름Query 를 사용할 수 있게 된다.
요청을 명시할 때 builder.query
라고 작성하게 되면, get 요청
을 보낼 수 있게 되고,
builder.mutation
이라고 작성하면, post 혹은 put 요청
등을 보낼 수 있게 된다.
예를들어 아래 주소에 get요청을 보내는 endpoints를 작성하려면 아래처럼 작성하면 된다.
https://jsonplaceholder.typicode.com/posts
getPosts: builder.query({
query: () => 'posts',
}),
(src/api/postApi.js)
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
export const postApi = createApi({
reducerPath: 'postApi',
baseQuery: fetchBaseQuery({
baseUrl: process.env.REACT_APP_API_URL,
}),
endpoints: (builder) => ({
getPosts: builder.query({
query: () => 'posts',
}),
}),
});
export const {
useGetPostsQuery
} = postApi;
api작성을 완료하면 store를 만들어준다.
(src/store/index.js)
import { configureStore } from '@reduxjs/toolkit';
import { postApi } from '../api/postApi';
import { setupListeners } from '@reduxjs/toolkit/query';
export const store = configureStore({
reducer: {
[postApi.reducerPath]: postApi.reducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(postApi.middleware),
});
setupListeners(store.dispatch);
위에서 setupListeners 는 서비스 내에서 사용자의 행동이나 요소의 변화 등을 지켜보다가,
요청을 보낼 수 있도록 listener 를 준비해놓기 위한 함수로
액션을 실행할 때인 store.dispatch
로 설정했다.
스토어 작성이 완료되었으면 컴포넌트를 작성해준다.
(src/components/Post.jsx)
import React from 'react';
import { useGetPostsQuery } from '../api/postApi';
function Posts() => {
const { data: posts, isLoading, isError } = useGetPostsQuery();
// 위처럼 사용하면, 동작을 수행하면서 자동으로 isLoading, isError, data 를 반환해준다.
// 직접 isError 등을 정의해줄 필요없이 바로 받아와서 사용해줄 수 있다.
if (isLoading) {
return <div>로딩 중...</div>
}
if (isError || !posts) {
return <div>오류 발생</div>;
}
return (
<div>
{posts.map((post) => (
<>
<p>{post.title}</p>
<p>{post.body}</p>
</>
))}
</div>
);
};
export default Posts;
마지막으로 app.js는 기존처럼 provider로 래핑해준다.
(app.js)
import { Provider } from 'react-redux';
import Posts from './components/Posts';
import {store} from './store/index';
function App() {
return (
<Provider store={store}>
<Posts/>
</Provider>
);
}
export default App;
(postApi.js)
import {createApi, fetchBaseQuery} from '@reduxjs/toolkit/query/react'
export const postApi = createApi({
reducerPath: 'postApi',
tagTypes: ['Posts'],
baseQuery: fetchBaseQuery({
baseUrl: process.env.REACT_APP_API_URL,
}),
endpoints: (builder) => ({
getPosts: builder.query({
query: () => 'posts',
providesTags: [{ type: 'Posts', id: 'LIST'}]
}),
getPostById: builder.query({
query: (postId) => `posts/${postId}`,
providesTags: (result, error, postId) => [{type: 'Posts', id: postId}]
}),
createPost: builder.mutation({
query: ({data}) => ({
url: 'posts',
method: 'POST',
body: data,
}),
invalidatesTags: (result) => result ? [{type: 'Posts', id: 'LIST'}] : []
})
})
})
export const { useGetPostsQuery, useGetPostByIdQuery, useCreatePostMutation } = postApi
위에서 Tag는 각 요청마다의 cache 이름을 뜻한다.
invalidatesTags는 기존에 요청보냈던 결과 cache를 없애고 해당 Tag에 대한 요청을 다시 보내는 것을 말한다.
그래서 위 createPost 메서드의 경우 post요청 이후에 자동으로 get요청이 가는 것을
개발자모드 네트워크탭에서 확인할 수 있다.
이처럼 RTK Query는 백엔드 Api 데이터를 관리하는 로직을 프론트엔드 데이터와 분리시킬 수 있을 뿐만 아니라 가독성이 좋고, 캐싱관련 기능도 지원하기 때문에 좋은 활용 방법인 것 같다.