ํ์ฌ ํ๋ก์ ํธ ๊ฐ๋ฐ ์ค ์ ์ญ์ ์ผ๋ก ์ ๋ณด๊ฐ ํ์ํ ๋น๋๊ธฐ ๋ฐ์ดํฐ(์ํฌ์คํ์ด์ค, ์ฑ๋, ์ ์ ์ ๋ณด ๋ฑ๋ฑ...)๋ฅผ ๋ค๋ฃฐ ๋, ํด๋น ๋น๋๊ธฐ ๋ฐ์ดํฐ๋ฅผ ์บ์ฑํ ํ์์ฑ์ด ์์์ต๋๋ค.
React-query์ Redux ToolKit Query ์ค ์ด๋ ๊ฒ์ ์ ์ฉํ ์ง์ ๋ํ ๊ณ ๋ฏผ์ ํ๋๋ฐ, ๊ธฐ์กด ํ๋ก์ ํธ ํ๋ก ํธ์์ ์ด๋ฏธ Redux ToolKit๋ฅผ ์ฌ์ฉํ๊ณ ์๊ณ ๊ธฐ๋ฅ๋ React-query์ ๋น์ทํด์ Redux ToolKit Query๋ฅผ ์ ์ฉํ๊ธฐ๋ก ๊ฒฐ์ ํ์์ต๋๋ค.
์ฌ์ฉํ๋ค๋ณด๋, axios ๋์ ์ฌ์ฉํด๋ ๊ด์ฐฎ์ ๊ฑฐ ๊ฐ๋ค๋ ์๊ฐ์ด ๋๋ค์ ๐
ํด๋น ๊ธ์ ๊ณต์๋ฌธ์๋ฅผ ์ฐธ๊ณ ํ์ฌ Redux ToolKit Query ์ ์ฉ ๋ฐ ์ ์ฉํ ๊ธฐ๋ฅ์ ๋ํ์ฌ ๊ธฐ์ฌํ ๊ธ์ ๋๋ค.
์ง์์ ์ผ๋ก ์ ๋ฐ์ดํธ ์์ ์ด๋ฉฐ, ์ธ๊ธ๋์ง ์์ ์ต์ ๋ ๋ง์ผ๋ ํ์ํ ๊ธฐ๋ฅ์ด ์๋ค๋ฉด ์๋์ Redux Toolkit ๊ณต์๋ฌธ์๋ฅผ ์ดํด๋ณด๋ ๊ฒ์ ์ถ์ฒํฉ๋๋ค
- RTK Query ์ฌ์ฉ๊ฐ์ด๋ : https://redux-toolkit.js.org/rtk-query/overview
- RTK Query API ๊ฐ์ด๋: https://redux-toolkit.js.org/rtk-query/api/createApi
0. RTK(Redux ToolKit) Query๋?
Redux ToolKit(์ดํ RTK) Query๋ ์น ์ ํ๋ฆฌ์ผ์ด์ ์์ ๋ฐ์ดํฐ๋ฅผ ๋ก๋ํ๋ ์ผ๋ฐ์ ์ธ ๊ฒฝ์ฐ๋ฅผ ๋จ์ํํ์ฌ ๋ฐ์ดํฐ ๋ก๋ ๋ฐ ์บ์ฑ์ ๊ฐํธํ๊ฒ ์ฌ์ฉํ ์ ์๊ฒ ๋์์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค.
๋ํ, ๊ธฐ์กด์ Redux๋ก ์ํ๋ฅผ ๊ด๋ฆฌํ์์ง๋ง, ๋ช ๋ ๋์ React ์ปค๋ฎค๋ํฐ๋ฅผ ํตํด ์ํ๊ด๋ฆฌ์ ๋ฐ์ดํฐ ๋ก๋ ๋ฐ ์บ์ฑ์ ๋ค๋ฅธ ๊ด์ฌ์ฌ๋ก ๋ณด๊ณ ์ ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค.
RTK Query๋ฅผ ์ฌ์ฉํ๋ฉด ์๋ ๋์๋ค์ ์์ฝ๊ฒ ํ ์ ์์ต๋๋ค.
- UI ์คํผ๋๋ฅผ ํ์ํ๊ธฐ ์ํ ๋ก๋ ์ํ ์ถ์
- ๋์ผํ ๋ฐ์ดํฐ์ ๋ํ ์ค๋ณต ์์ฒญ ๋ฐฉ์ง
- UI๊ฐ ๋ ๋น ๋ฅด๊ฒ ๋๊ปด์ง๋๋ก ์ ๋ฐ์ดํธ
- ์ฌ์ฉ์๊ฐ UI์ ์ํธ ์์ฉํ ๋์ ์บ์ ์๋ช ๊ด๋ฆฌ
1. RKT Query ๊ตฌ์กฐ ๋ฐ Store ๋ฑ๋ก ๋ฐฉ๋ฒ
RTK Query๋ ์ด๋ฆ๊ณผ ๊ฐ์ด Redux Toolkit์ผ๋ก ๋ง๋ค์ด์ก๊ธฐ ๋๋ฌธ์, ๊ธฐ๋ณธ RTK์ store์ ๋ฑ๋กํ๋ ๋ฐฉ๋ฒ์ด ์ ์ฌํฉ๋๋ค. (RTK Query์ ๊ดํ ๊ธ์ด๊ธฐ ๋๋ฌธ์ RTK์ ๊ดํ ์ค๋ช ์ ์ ์ธํ๊ฒ ์ต๋๋ค.)
RTK์ฒ๋ผ createSlice()๋ก ์ฌ๋ผ์ด์ค๋ฅผ ๋ง๋๋ ๊ฒ์ด ์๋, createApi ๋ฉ์๋๋ฅผ ์ด์ฉํด ๋ง๋ค๊ฒ ๋ฉ๋๋ค.
export const apiSlice = createApi({
reducerPath: 'api', // store์ ๋ฑ๋ก๋ Api ๊ณ ์ ํค
baseQuery: refreshFetchBase, // endpoints์ ๊ธฐ๋ณธ์ผ๋ก ์ฌ์ฉ๋ baseQuery. baseQuery๋ฅผ ์ด์ฉํด axios์ ํค๋ ์ค์ , intercept ๊ธฐ๋ฅ์ ๋ฃ์ ์ ์์
endpoints: (builder) => ({ // api๋ฅผ ๊ธฐ์ฌํ๋ ์์ญ
tagTypes: ['Item'], // ๋ฌธ์์ด ํ๊ทธ ๋ฐฐ์ด. ํด๋น ํ๊ทธ๋ก ์ฐ๊ด ๋ฐ์ดํฐ ๊ฐฑ์ ๊ฐ๋ฅ
...
}),
});
1-1. endpotins ๋ถํ
๊ธฐ๋ณธ ์ ์ผ๋ก RTK Query๋ ํ ๊ณณ์์ Api๋ฅผ ์ ์ํ๊ฒ ๋ฉ๋๋ค. ํ์ง๋ง ์๋น์ค ํฌ๊ธฐ๊ฐ ์ปค์ง๋ฉด API๊ฐ ๋ง์์ ธ ํ์ผ์ด ์ ์ ์ปค์ง๋ฉด์ ๊ด๋ฆฌ๊ฐ ์ด๋ ค์ ์ง ๊ฒ์ ๋๋ค.
๋๋ฌธ์ injectEndpoints๋ฅผ ์ฌ์ฉํ์ฌ ์ฐ๊ด๋ ๋ฒ์ ๋ณ(๋ณดํต tag ๋ณ)๋ก endpoints๋ฅผ ๋๋๋ ๊ฒ ์ข์ต๋๋ค. (์์ฝ๊ฒ๋ createApi ์ ์ ์๋ ๊ธฐ๋ณธ ์ค์ ์ ๋ณ๊ฒฝํ ์ ์๋ค๊ณ ํฉ๋๋ค.)
injectEndpoints
๊ธฐ์กด api ์ฌ๋ผ์ด์ค์ ์กด์ฌํ๋ endpoints์ ๊ฐ์ ์ด๋ฆ์ผ๋ก endpoints๋ฅผ ์ ์ํ๋ค๋ฉด overrideํฉ๋๋ค. (๋ค๋ง, overrideExisting์ true๋ก ์ค์ ํ์ง ์์ผ๋ฉด, ์ฌ์ ์ ๋์ง ์๊ณ ๊ฒฝ๊ณ ํ์๋ฅผ ๋ ธ์ถํฉ๋๋ค. ๊ธฐ๋ณธ false)
export const itemEndpoints = apiSlice.injectEndpoints({
endpoints: (builder) => ({
...
}),
// overrideExisting: true, ์ ์ํ์ง ์์ผ๋ฉด false
});
1-2. store์ ๋ฑ๋ก
์์ ์ฃผ์์ ๊ธฐ์ฌ๋ ๊ฒ ์ฒ๋ผ Api ๊ณ ์ ํค๋ฅผ ์ด์ฉํ์ฌ store์ api๋ฅผ ๋ฑ๋กํ ์ ์์ต๋๋ค.
export default configureStore({
reducer: {
...
[apiSlice.reducerPath]: apiSlice.reducer, // reducerPath(ํค) : reducer(๊ฐ) ๋ฐฉ์์ผ๋ก ๋ฑ๋ก
...
},
middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: false }).concat(apiSlice.middleware), // middleware๋ก ์ฐ๊ฒฐํด ์ค
});
2. endpoints ์ ์ ๋ฐฉ๋ฒ ๋ฐ ์ฌ์ฉ๋ฒ
์์ฃผ ์ฌ์ฉ๋๋ endpoints ์ ์ํ๋ ๋ฐฉ๋ฒ์ ์๋์ ๊ฐ์ต๋๋ค.
๊ฐ๋จํ๊ฒ ์ฃผ์์ ์์ฑํ์์ผ๋ฉฐ, ์์ธํ ๋ด์ฉ์ ์๋์์ ํ์ธํ๋ฉด ๋๊ฒ ์ต๋๋ค. (์ฃผ์์ผ๋ก ์ด๋ ๋ถ๋ถ์ ๋ด์ผํ๋์ง ํ์ํ์์ต๋๋ค.
export const transformResponse = (baseQueryReturnValue, meta, arg) => baseQueryReturnValue.body;
// ์๋ฌ notify ๋
ธ์ถ
const showErrorNotify = (response, message) => {
notify.error(message);
return response;
};
export const itemEndpoints = apiSlice.injectEndpoints({
endpoints: (build) => ({
...
// ์์ดํ
์ ๋ณด ์กฐํ
fetchItem: build.query({
query: (itemNo) => `/items/${itemNo}`, // api ์ ์(2-1. build์ query, mutation ์ฐธ๊ณ )
providesTags: ['Item'], // invalidatesTags์ ๊ฐ์ ํ๊ทธ๊ฐ ๋ถ์ ๋ฉ์๋๋ฅผ ์คํ ์ ํด๋น api๊ฐ ๊ฐฑ์ (2-2. tag์์คํ
์ฐธ๊ณ )
transformResponse, // response๋ฅผ ๋ณํ์ํฌ ์ ์์(2-3. custom response ๊ณ )
transformErrorResponse: (response) => showErrorNotify(response, '์์ดํ
์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค์ง ๋ชปํ์ด์'), // error response๋ฅผ ๋ณํ์ํฌ ์ ์์(2-3. custom response ์ฐธ๊ณ )
}),
// ์์ดํ
์ ๋ณด ๋ณ๊ฒฝ
updateItem: build.mutation({
query: ({ itemNo, ...data} ) => ({
url: `/items/${itemNo}`,
method: 'PUT',
body: data,
}),
invalidatesTags: ['Item'], // ํด๋น ํ๊ทธ๊ฐ ๋ถ์ ๋ฉ์๋๋ฅผ ์คํ ์ providesTags์ ๊ฐ์ ํ๊ทธ๊ฐ ๋ถ์ api๋ฅผ ๊ฐฑ์ (2-2. tag์์คํ
์ฐธ๊ณ )
}),
// ์์ดํ
์ ์กฐํ
fetchItemCount: build.query({
query: (itemNo) => `/items/${itemNo}/members/count`,
providesTags: [{ type: 'Item', id: tags.membersCount }],
transformResponse,
transformErrorResponse: (response) => showErrorNotify(response, '์์ดํ
์๋ฅผ ๊ฐ์ ธ์ค์ง ๋ชปํ์ด์'),
}),
...
}),
});
// react์์ ์ฌ์ฉํ ์ ์๋ hook ํ์์ผ๋ก export, RTK Query๊ฐ hook์ ์ ๊ณต
// use + ๋ฉ์๋ ์ด๋ฆ + Query/Mutation ์ผ๋ก ์ด๋ฃจ์ด์ง
// 2-1. build์ query, mutation ์ฐธ๊ณ
export const {
useFetchItemQuery,
useUpdateItemMutation,
useLazyFetchItemCountQuery, // Lazy ์ฆ ์๋์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ ๊ณต
...
}
2-1. build์ query(๋ฐ์ดํฐ ์กฐํ), mutation(๋ฐ์ดํฐ ๋ณ๊ฒฝ)
๋ฐ์ดํฐ ์กฐํ ๋ฐ ๋ณ๊ฒฝ ์ endpoints์ query(์กฐํ)์ mutation(๋ณ๊ฒฝ) ๋ฉ์๋์ฌ์ฉํ ์ ์์ต๋๋ค.
ํด๋น api๋ฅผ ์์ฒญํ ๋, RTK Query๊ฐ ์ ๊ณตํด์ฃผ๋ hook์ ์ฌ์ฉํ๋๋ฐ, api์ ์ฌ๋ฌ๊ฐ์ parameter๊ฐ ํ์ํ๋ค๋ฉด ๋ฐ๋์ ๊ฐ์ฒด๋ก ์ ๊ณตํ์ฌ์ผ ํฉ๋๋ค.
query
๋ฐ์ดํฐ ์กฐํ ์ ์ฌ์ฉํ๋ endpoints build ๋ฉ์๋์ ๋๋ค. javascript fetch ๋ฉ์๋๋ฅผ ์ด์ฉํ fetchBaseQuery๋ก ์ด๋ฃจ์ด์ ธ ์๋ค๊ณ ํฉ๋๋ค.
ํด๋น build ๋ฉ์๋๋ก endpoints๋ฅผ ์ ์ํ๊ณ RTK Query๊ฐ ์ ๊ณตํด์ฃผ๋ ์กฐํ hook์ ์ฌ์ฉํ์ฌ ํด๋น API ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
RTK Query๊ฐ ์ ๊ณตํด์ฃผ๋ query ๊ด๋ จ hook์ ์๋์ ๊ฐ์ด 5๊ฐ์ง๊ฐ ์๋๋ฐ, ์ฌ๊ธฐ์๋ useQuery, useLazyQuery๋ง ๋ค๋ฃจ๊ฒ ์ต๋๋ค.
- useQuery : api ๋ฐ์ดํฐ ์์ฒญ or ์บ์ ๋ฐ์ดํฐ ์กฐํ
- useLazyQuery : api ๋ฐ์ดํฐ ์กฐํ๋ฅผ ์๋์ผ๋ก ์ ์ด
- useQueryState : ์์ฒญ ์ํ ๋ฐ ์บ์๋ ๋ฐ์ดํฐ๋ฅผ ์กฐํ
- useQuerySubscription : ์ฌ์กฐํ ๋ฐ ์บ์ ๋ฐ์ดํฐ ๊ตฌ๋
- useLazyQuerySubscription : ์ฌ์กฐํ ๋ฐ ์บ์ ๋ฐ์ดํฐ ๊ตฌ๋ ์ ์๋์ผ๋ก ์ ์ด
useQuery
useQuery๋ use + endpoints์ด๋ฆ + Query ํ์์ผ๋ก ์ ๊ณต๋ฉ๋๋ค. hook์ parameter์ ๋ค์ด๊ฐ ๊ฐ๊ณผ return๊ฐ์ ์๋์ ๊ฐ์ต๋๋ค.
- useQuery hook์ parameter : api์ ์ฌ์ฉ๋ parameter์ queryOptions์ ๋ฃ์ ์ ์์ต๋๋ค.
- useQuery hook์ return : ๊ฐ์ฒด๋ก ๋ฐํ isLoading, isSuccess๊ฐ์ ์์ฒญ์ํ ๊ฐ๊ณผ data,error, ์์/์ข ๋ฃ์๊ฐ ๋ฑ data๊ฐ์ ์ป์ ์ ์์ต๋๋ค.
๊ธฐ๋ณธ์ ์ธ ์ฌ์ฉ๋ฒ์ ์๋์ ๊ฐ์ต๋๋ค.
// itemEndpoints.js
export const itemEndpoints = apiSlice.injectEndpoints({
endpoints: (build) => ({
// ์์ดํ
์ ๋ณด ์กฐํ
fetchItem: build.query({ // query ๋ฉ์๋๋ก api ์ ์
query: (itemNo) => `/items/${itemNo}`,
providesTags: ['Item'],
transformResponse,
transformErrorResponse: (response) => showErrorNotify(response, '์์ดํ
์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค์ง ๋ชปํ์ด์'),
}),
}),
});
export const {
useFetchItemQuery, // RTK Query hook ์ถ์ถ
}
// ItemInfo.jsx
const ItemInfo = ({ itemNo }) => {
...
// ์๋์ผ๋ก api๋ฅผ ์์ฒญ,
// api.endpoints.fetchItem.useQuery(itemNo) ํ์์ผ๋ก ์ฌ์ฉ ๊ฐ๋ฅ
const { data: item = [], isSuccess } = useFetchItemQuery(itemNo); // hook์ ํ๋ผ๋ฏธํฐ๋ก api์ ์ฌ์ฉ๋ ํ๋ผ๋ฏธํฐ๋ฅผ ์ ๋ฌ
}
๊ธฐ์กด์ axios๋ฅผ ํ์ฉํ ๋น๋๊ธฐ ์์ฒญ์ ๋ฐ์ดํฐ๋ฅผ ๋ฃ์ state๊ฐ ํ์ํ๋๋ฐ, RTK Query์ hook์ ์ฌ์ฉํ๋ฉด ์์ ๊ฐ์ด hook์ ์์ฑํ ํ data๋ฅผ ๊ฐ์ ธ๋ค ์ฌ์ฉํ๋ฉด ๋ฉ๋๋ค. (์ถ๊ฐ๋ก isLoading ๊ฐ์ ์ํ๊ฐ๋ ์ฌ์ฉํ ์ ์์)
RTK Query์ parameter, return ๊ฐ์ ์๋ ๋งํฌ์์ ํ์ธํ ์ ์์ต๋๋ค.
useLazyQuery
useLazyQuery๋ ์๋์ผ๋ก ์ ์ดํ ์ ์๊ธฐ ๋๋ฌธ์ ์คํ ๋ฉ์๋๋ฅผ ์ ๊ณตํฉ๋๋ค.
hook์ ํ์์ useLazy + endpoints์ด๋ฆ + Query ํ์์ผ๋ก ์ ๊ณต๋ฉ๋๋ค.
hook์ parameter์ ๋ค์ด๊ฐ ๊ฐ๊ณผ return๊ฐ์ ์๋์ ๊ฐ์ต๋๋ค.
- useLazyQuery hook์ parameter : api ์คํ ์์ queryOptions์ ๋ฃ์ ์ ์์ต๋๋ค.
- useLazyQuery hook์ return : ๋ฐฐ์ด๋ก ๋ฐํํ๋ฉฐ, [ ์คํ ๋ฉ์๋, ๊ฒฐ๊ณผ ๊ฐ์ฒด, lastPromiseInfo ] ๋ก ๋์ด์์ต๋๋ค.
๊ธฐ๋ณธ์ ์ธ ์ฌ์ฉ๋ฒ์ ์๋์ ๊ฐ์ต๋๋ค.
// itemEndpoints.js
export const itemEndpoints = apiSlice.injectEndpoints({
endpoints: (build) => ({
// ์์ดํ
์ ์กฐํ
fetchItemCount: build.query({ // query ๋ฉ์๋๋ก api ์ ์
query: (itemNo) => `/items/${itemNo}/count`,
providesTags: [{ type: 'Item', id: tags.count }],
transformResponse,
transformErrorResponse: (response) => showErrorNotify(response, '์์ดํ
์๋ฅผ ๊ฐ์ ธ์ค์ง ๋ชปํ์ด์'),
}),
}),
});
export const {
useLazyFetchItemCountQuery, // RTK LazyQuery hook ์ถ์ถ
}
// ItemAuthorityManage.jsx
const ItemAuthorityManage = ({ itemNo, setSelectedMember }) => {
...
// ํด๋น ์คํ ๋ฉ์๋๋ฅผ ์คํํจ์ผ๋ก์จ api๋ฅผ ์์ฒญ (์๋์ผ๋ก api๋ฅผ ์์ฒญ X)
// api.endpoints.fetchItemCount.useLazyQuery() ํ์์ผ๋ก ์ฌ์ฉ ๊ฐ๋ฅ
const [fetchItemCount] = useLazyFetchItemCountQuery();
const [fetchInvited] = useLazyFetchInvitedQuery();
const moveMemberInvite = useCallback(async () => {
const [itemCount, invitedMembers] = await axios.all([
fetchItemMembersCount(itemNo).unwrap(), // unwrap()์ ์ฌ์ฉํ์ง ์์ผ๋ฉด ๊ฒฐ๊ณผ ๊ฐ์ฒด๊ฐ ๋ฐํ๋จ
fetchInvited(itemNo).unwrap(),
]);
...
}
}
useLazyQuery๋ฅผ ํ์ฉํ๋ฉด ์๋์ผ๋ก api๋ฅผ ์์ฒญํ๋๋ฐ, useLazyQuery๋ฅผ ์ฌ์ฉํ๋ค๋ฉด, ์คํ๋ฉ์๋ ์คํ ์์ ์ api๋ฅผ ์์ฒญํ๊ฒ ๋ฉ๋๋ค.
์ด ๋, unwrap() ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ง ์์ผ๋ฉด ์์ฒญ์ response๊ฐ ๋ฐํ๋์ง ์๊ณ isFetching, data ๋ฑ๋ฑ์ด ๋ค์ด์๋ useLazyQuery์ ๊ฒฐ๊ณผ ๊ฐ์ฒด๊ฐ ๋ฐํ๋ฉ๋๋ค.
RTK LazyQuery์ parameter, return ๊ฐ์ ์๋ ๋งํฌ์์ ํ์ธํ ์ ์์ต๋๋ค.
mutation
๋ฐ์ดํฐ ๋ณ๊ฒฝ ์ ์ฌ์ฉํ๋ endpoints build ๋ฉ์๋์ ๋๋ค.
ํด๋น build ๋ฉ์๋๋ก endpoints๋ฅผ ์ ์ํ๊ณ RTK Query๊ฐ ์ ๊ณตํด์ฃผ๋ ๋ณ๊ฒฝ hook์ ์ฌ์ฉํ์ฌ ํด๋น API ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
RTK Query๊ฐ ์ ๊ณตํด์ฃผ๋ ๋ณ๊ฒฝ hook์ useMutation ํ๋ ๋ฟ์ ๋๋ค.
useMutation
useMutation๋ use + endpoints์ด๋ฆ + Mutation ํ์์ผ๋ก ์ ๊ณต๋ฉ๋๋ค.hook์ parameter์ ๋ค์ด๊ฐ ๊ฐ๊ณผ return๊ฐ์ ์๋์ ๊ฐ์ต๋๋ค.
- useMutation hook์ parameter : api ์คํ ์์ queryOptions์ ๋ฃ์ ์ ์์ต๋๋ค.
- useMutation hook์ return : ๋ฐฐ์ด๋ก ๋ฐํํ๋ฉฐ, [ ์คํ ๋ฉ์๋, ๊ฒฐ๊ณผ ๊ฐ์ฒด ] ๋ก ๋์ด์์ต๋๋ค.
๊ธฐ๋ณธ์ ์ธ ์ฌ์ฉ๋ฒ์ ์๋์ ๊ฐ์ต๋๋ค.
// itemEndpoints.js
export const itemEndpoints = apiSlice.injectEndpoints({
endpoints: (build) => ({
// ์์ดํ
์ ๋ณด ๋ณ๊ฒฝ
updateItem: build.mutation({ // mutation ๋ฉ์๋๋ก api ์ ์
query: ({ itemNo, ...data }) => ({ // ์ฌ๋ฌ ๋ฐ์ดํฐ๊ฐ ์๋ค๋ฉด ๊ฐ์ฒด ํ๋ผ๋ฏธํฐ ์ ๊ณต
url: `/items/${itemNo}`,
method: 'PUT',
body: data,
}),
invalidatesTags: ['Item'],
}),
}),
});
export const {
useUpdateItemMutation, // RTK Mutation hook ์ถ์ถ
}
// ItemInfo.jsx
const ItemInfo = ({ itemNo }) => {
// ํด๋น ์คํ ๋ฉ์๋๋ฅผ ์คํํจ์ผ๋ก์จ api๋ฅผ ์์ฒญ
// api.endpoints.updateItem.useMutation() ํ์์ผ๋ก ์ฌ์ฉ ๊ฐ๋ฅ
const [updateItem] = useUpdateItemMutation();
...
const onUpdateItem = async (data) => {
const params = {
itemNo: data.no,
itemName: data.name,
};
try {
await updateItem(params).unwrap(); // unwrap()์ ์ฌ์ฉํ์ง ์์ผ๋ฉด ๊ฒฐ๊ณผ ๊ฐ์ฒด๊ฐ ๋ฐํ๋จ
notify.info('์์ดํ
์ ๋ณด๊ฐ ๋ณ๊ฒฝ๋์์ต๋๋ค.');
closeModal('profileSettings');
} catch (error) {
notify.error('์์ดํ
์ ๋ณด ์์ ์ ์คํจํ์์ต๋๋ค.');
}
}
}
useMutation์ ์คํ๋ฉ์๋ ์คํ ์์ ์ api๋ฅผ ์์ฒญํ๊ฒ ๋ฉ๋๋ค.
์ด ๋, unwrap() ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ง ์์ผ๋ฉด ์์ฒญ์ response๊ฐ ๋ฐํ๋์ง ์๊ณ isFetching, data ๋ฑ๋ฑ์ด ๋ค์ด์๋ useMutation์ ๊ฒฐ๊ณผ ๊ฐ์ฒด๊ฐ ๋ฐํ๋ฉ๋๋ค.
RTK MutationQuery์ parameter, return ๊ฐ์ ์๋ ๋งํฌ์์ ํ์ธํ ์ ์์ต๋๋ค.
2-2. tag ์์คํ (์ฐ๊ด ๋ฐ์ดํฐ ์๋ ๊ฐฑ์ )
๊ธฐ๋ณธ์ ์ผ๋ก RTK Query๋ ์บ์ ๋ฐ์ดํฐ๊ฐ ์กด์ฌํ์ง ์๋ ๊ฒฝ์ฐ์๋ง API ์์ฒญ์ด ์ ์ก๋ฉ๋๋ค. ํ์ง๋ง Mutation์ ํตํ์ฌ ๋ฐ์ดํฐ๋ฅผ ๋ณ๊ฒฝํ ๋, ์ฐ๊ด ๋ฐ์ดํฐ๋ฅผ ์ฌ์์ฒญํด์ผํฉ๋๋ค.
์ด๋ฐ ๊ฒฝ์ฐ์ tag ์์คํ ์ ์ฌ์ฉํ๋ฉด ์ฐ๊ด ๋ฐ์ดํฐ๋ฅผ ์๋์ผ๋ก ๊ฐฑ์ ํ ์ ์์ต๋๋ค. (RTK Query์์๋ ๊ฐ endpoint + ๋งค๊ฐ๋ณ์ ์กฐํฉ์ผ๋ก CacheKey๋ฅผ ๋ง๋ค์ด, ํน์ ์บ์ ๋ฐ์ดํฐ๋ฅผ ๊ตฌ๋ถํ ์ ์์ต๋๋ค.)
// tag์ type
export const tagTypes = {
item: 'Item',
paperTemplate: 'PaperTemplate',
};
export const apiSlice = createApi({
reducerPath: 'api',
baseQuery: refreshFetchBase,
tagTypes: [Object.values(tagTypes)], // ['Item', 'PaperTemplate']๊ณผ ๊ฐ์. ๋ฌธ์์ด ํ๊ทธ ๋ฐฐ์ด. ํด๋น ํ๊ทธ๋ก ์ฐ๊ด ๋ฐ์ดํฐ ๊ฐฑ์ ๊ฐ๋ฅ
endpoints: (builder) => ({
// ์์ดํ
์ ๋ณด ์กฐํ
fetchItem: build.query({
query: (itemNo) => `/items/${itemNo}`,
providesTags: [item], // invalidatesTags์ ๊ฐ์ ํ๊ทธ๊ฐ ๋ถ์ ๋ฉ์๋๋ฅผ ์คํ ์ ํด๋น api๊ฐ ๊ฐฑ์
transformResponse,
transformErrorResponse: (response) => showErrorNotify(response, '์์ดํ
์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค์ง ๋ชปํ์ด์'),
}),
// ์์ดํ
์ ๋ณด ๋ณ๊ฒฝ
updateItem: build.mutation({
query: ({ itemNo, ...data }) => ({
url: `/items/${itemNo}`,
method: 'PUT',
body: data,
}),
invalidatesTags: [item], // ํด๋น ํ๊ทธ๊ฐ ๋ถ์ ๋ฉ์๋๋ฅผ ์คํ ์ providesTags์ ๊ฐ์ ํ๊ทธ๊ฐ ๋ถ์ api๋ฅผ ๊ฐฑ์
}),
// ์์ดํ
์ ์กฐํ
fetchItemCount: build.query({
query: (itemNo) => `/items/${itemNo}/count`,
providesTags: [{ type: item, id: tagIds.count }], // id๋ฅผ ์ง์ ํจ์ผ๋ก์จ ์บ์ ๋ฐ์ดํฐ๋ฅผ ๋ํ
์ผํ๊ฒ ๊ตฌ๋ถ ๊ฐ๋ฅ
transformResponse,
transformErrorResponse: (response) => showErrorNotify(response, '์์ดํ
์๋ฅผ ๊ฐ์ ธ์ค์ง ๋ชปํ์ด์'),
}),
}),
});
tagTypes
Slice์์ ์ฌ์ฉ๋๋ ํ๊ทธ๋ค์ tagTypes์ ๋ฌธ์์ด ๋ฐฐ์ด๋ก ๊ธฐ์ฌํด์ผํฉ๋๋ค.
providesTags
์ด๋ฆ๊ณผ ๊ฐ์ด ์บ์๋ ๋ฐ์ดํฐ์ ํ๊ทธ๋ฅผ ์ ๊ณตํฉ๋๋ค. ํ๊ทธ์ ํํ ๋ฐฉ๋ฒ์ ์๋์ ๊ฐ์ต๋๋ค.
- ['Item'] : tagTypes์ ๊ฐ์ ํํ๋ก ์ ๊ณต
- ๋ฐฐ์ด๋ก ๋์ด ์๋ ๊ฒ์ผ๋ก ์ ์ถํ ์ ์์ง๋ง, ์ฌ๋ฌ ๊ฐ์ ํ๊ทธ ์ง์ ๊ฐ๋ฅ
- ์ฆ, ์์ fetchWorkspace endpoint์ ['Workspace', 'EvaluationTemplate'] ๋ก ์ง์ ํด์ค๋ค๋ฉด EvaluationTemplate ์ ์ฐ๊ด๋์ด ๊ฐฑ์ ์ด ๊ฐ๋ฅํ๋ค๋ ์๋ฏธ
- [{type: 'Item'}] : ๊ฐ์ฒด ํํ๋ก ์ ๊ณต
- ํํ๋ง ๋ค๋ฅผ ๋ฟ 1๋ฒ๊ณผ ๊ฐ์
- [{type: 'Item', id: 1}] : type๊ณผ id ํํ๋ก ์ ๊ณต
- id๋ฅผ ์ด์ฉํ์ฌ ์กฐ๊ธ ๋ ๋ํ ์ผํ๊ฒ ์บ์ ๋ฐ์ดํฐ๋ฅผ ๊ตฌ๋ถ ๊ฐ๋ฅ
id๋ฅผ ์ด์ฉํ๋ ๋ฐฉ๋ฒ
providesTags์ ํ๊ทธ๋ฅผ ์ง์ ํ ๋, ๋ฐฐ์ด๋ฟ๋ง ์๋๋ผ (result, error, arg) => {} ๊ฐ์ ์ฝ๋ฐฑ ํจ์ ํํ๋ก ๋ฃ์ด์ค ์๋ ์์ต๋๋ค.
ํด๋น ํจ์์ ํ๋ผ๋ฏธํฐ๋ฅผ ํ์ฉํ์ฌ ์๋์ ์์์ฒ๋ผ id๋ก ๋ํ ์ผํ๊ฒ ์บ์ ๋ฐ์ดํฐ ๊ตฌ๋ถ์ด ๊ฐ๋ฅํฉ๋๋ค.
providesTags: (result, error, arg) => result ? [ ...result.map(({id}) => ({ type: 'Item', id })) ] : [{type: 'Item', id: 'LIST' }];
์ฌ๊ธฐ์ ์ฃผ๋ชฉํ ์ ์ [{type: 'Workspace', id: 'LIST' }] ์ฒ๋ผ id์ ๋ฌธ์์ด ๊ฐ์ ๋ฃ์ด ๋ํ ์ผํ ๋ถ๋ถ์ ์์๋ก ์ง์ ํ ์๋ ์๋ค๋ ๊ฒ์ ๋๋ค.
invalidatesTags
ํด๋น ์์ฑ์ ์ ์ํ Mutation์ ์คํํ๋ฉด providesTags์ ์ ์๋ ํ๊ทธ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํน์ ์บ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐฑ์ ํ ์ ์์ต๋๋ค.
providesTags์ ๋ง์ฐฌ๊ฐ์ง๋ก ์ฝ๋ฐฑ ํจ์๋ฅผ ์ ๊ณตํฉ๋๋ค.
ํ๊ทธ ๊ฐฑ์ ๋์ ๋ฒ์
type์ id๋ฅผ ์ง์ ํ์ง ์์ ๊ฒฝ์ฐ type๊ณผ ๊ด๋ จ๋ ์ ์ฒด ์บ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐฑ์ ํ๊ฒ ๋๋ ์ฃผ์ํ์ฌ์ผ ํฉ๋๋ค.
| Invalidated / Provided | General tag A['Post'] / [{ type: 'Post' }] |
General tag B['User'] / [{ type: 'User' }] |
Specific tag A1[{ type: 'Post', id: 1 }] |
Specific tag A2[{ type: 'Post', id: 'LIST' }] | Specific tag B1[{ type: 'User', id: 1 }] |
Specific tag B2[{ type: 'User', id: 2 }] |
|
General tag A
['Post'] / [{ type: 'Post' }]
|
โ๏ธ | โ๏ธ | โ๏ธ | |||
|
General tag B
['User'] /
[{ type: 'User' }] |
โ๏ธ | โ๏ธ | โ๏ธ | |||
|
Specific tag A1
[{ type: 'Post', id: 1 }]
|
โ๏ธ | |||||
|
Specific tag A2
[{ type: 'Post', id: 'LIST' }]
|
โ๏ธ | |||||
|
Specific tag B1
[{ type: 'User', id: 1 }]
|
โ๏ธ | |||||
|
Specific tag B2
[{ type: 'User', id: 2 }]
|
โ๏ธ |
2-3. custom response(response ๊ตฌ์ฑ ๋ณ๊ฒฝ)
transformResponse์ transformErrorResponse๋ฅผ ์ด์ฉํ๋ฉด endpoint์ ๊ฒฐ๊ณผ(์ฑ๊ณต/์คํจ) ๊ฐ์ ๋ฐ์ ๋, ๊ฒฐ๊ณผ ๊ฐ์ Customํ์ฌ ๋ฐ์ ์ ์์ต๋๋ค.
transformResponse
ํน์ endpoint๊ฐ ์์ธ๋ฅผ ๋์ง์ง ์์ผ๋ฉด ์๋์ ๊ฐ์ ํด๋น ์ฝ๋ฐฑ ํจ์๋ฅผ ํตํด์ ๊ฒฐ๊ณผ๊ฐ์ ๋ฐํํฉ๋๋ค.
transformResponse: (response, meta, arg) => response.body;
transformErrorResponse
ํน์ endpoint๊ฐ ์์ธ๋ฅผ ๋์ง๋ฉด ์๋์ ๊ฐ์ ํด๋น ์ฝ๋ฐฑ ํจ์๋ฅผ ํตํด์ ๊ฒฐ๊ณผ๊ฐ์ ๋ฐํํฉ๋๋ค.
transformErrorResponse: (response, meta, arg) => response.body;
๋๊ธ