[TS] useQuery, useMutation์ ์ ๋ค๋ฆญ ํ์ ์ดํด๋ณด๊ธฐ / Base Query ํ ๋ง๋ค๊ธฐ
React Query์์ ์ ๊ณตํ๋ useQuery
, useMutation
์ ๊ทธ๋๋ก ์ฌ์ฉํ๋ฉด ํญ์ ์ฟผ๋ฆฌํค์ ์ฟผ๋ฆฌ ํจ์๋ฅผ ์ง์ ํด์ผ ํ๋ ๋ฒ๊ฑฐ๋ก์์ด ์๋ค. API ์ ํ์ ๋ฐ๋ผ Base Query|Mutation ์ปค์คํ
ํ
์ ๋ง๋ค์ด ๋๊ณ ์ฌ์ฉํ๋ฉด ์ฟผ๋ฆฌํค์ ์ฟผ๋ฆฌ ํจ์๋ฅผ ์ผ์ผ์ด ์ง์ ํ์ง ์๊ณ ์ฌ๋ฌ ๊ณณ์์ ์ฌ์ฌ์ฉํ๊ธฐ ์ข๋ค.
ํ์ง๋ง ์ปค์คํ
ํ
์ ๋ง๋ค ๋ ์ฟผ๋ฆฌ ํ์
์ ์ง์ ํ์ง ์์ผ๋ฉด ๋ฐ์ดํฐ๊ฐ unknown
, any
์ธ ๊ฒฝ์ฐ๊ฐ ๋ง์์ ์ด๋ค ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฃจ๋์ง ์๊ธฐ ์ด๋ ต๋ค. ์ฟผ๋ฆฌ ํ์
์ ๋๋ถ๋ถ ์ ๋ค๋ฆญ์ผ๋ก ๋์ด ์๋๋ฐ ์ด๋ฅผ ์ ์ฌ์ฉํ๋ฉด ํ์
์ ๋ณด์ฅ ๋ฐ์ผ๋ฉด์ ๋ฐ์ดํฐ๋ฅผ ํธ๋ฆฌํ๊ฒ ๋ค๋ฃฐ ์ ์๋ค.
useQuery
์ ๋ค๋ฆญ ํ์ ํบ์๋ณด๊ธฐ โก๏ธ
export declare function useQuery<
TQueryFnData = unknown,
TError = unknown,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey // string | readonly unknown[];
>
โถ TQueryFnData
: ์ฟผ๋ฆฌ ํจ์ ๋ฆฌํด ํ์
(AxiosResponse<T>
๋ฑ)
โท TError
: ์ฟผ๋ฆฌ ํจ์ ์๋ฌ ํ์
(AxiosError
๋ฑ)
const { data, error } = useQuery<number, AxiosError>("todo", getTodo);
// data ํ์
: number | undefined
// error ํ์
: AxiosError | null
โธ TData
: select
ํจ์๋ก ์ฟผ๋ฆฌ ํจ์ ๋ฆฌํด๊ฐ์ ๊ฐ๊ณตํ ๋ ์ฌ์ฉํ๋ ์ต์ข
๋ฆฌํด ํ์
. ๊ธฐ๋ณธ๊ฐ TQueryFnData
TQueryFnData is the data returned from your query function, and TData is the eventual data. The eventual data can be different from the query function data when using select for example. — TanStack
const { data } = useQuery<Todo[], AxiosError, number>("todos", getTodos, {
select: (todos) => todos.length,
});
// select ํจ์๊ฐ number(todos.length) ํ์
์ ๋ฐํํ๋ฏ๋ก
// TData(3๋ฒ์งธ ์ ๋ค๋ฆญ ํ์
) ํ์
๋ number ํ์
์ผ๋ก ์ง์ ํด์ค๋ค
โน TQueryKey
: ์ฟผ๋ฆฌ ํค ํ์
. ๊ธฐ๋ณธ๊ฐ ๋ฌธ์์ด ํน์ ๋ฐฐ์ด.
์ฟผ๋ฆฌํค ๋ฐฐ์ด ๊ฐ ์์์ ์ด๋ค ํ์
์ด ๋ค์ด๊ฐ์ง TQueryKey
ํ์
์ ์ง์ ์ง์ ํ ์ ์๋ค.
useQuery<Todo[], AxiosError, Todo[], [string, number]>(["todos", id], getTodo);
์ฐธ๊ณ ๋ก QueryKey
ํ์
์ ๋ฌธ์์ด ํน์ ๋ฐฐ์ด์ด์ง๋ง ์ฟผ๋ฆฌ ํจ์์ context
์ธ์์ ํญ์ ๋ฐฐ์ด๋ก ์ ๋ฌ๋๋ค. ์ฆ ์ฟผ๋ฆฌํค๋ฅผ ๋ฌธ์์ด๋ก ์ง์ ํด๋ ์ฟผ๋ฆฌ ํจ์์ ํญ์ ๋ฐฐ์ด๋ก ์ ๋ฌ๋๋ ๊ฒ. e.g. 'todo'
→ ['todo']
queryFn: (context: QueryFunctionContext) => Promise<TData>
Base Query ์ปค์คํ ํ
ํ๋ผ๋ฏธํฐ๊ฐ ์์ ๋
ํ
์ ์ฌ์ฉํ๋ ๊ณณ์์ ์ฟผ๋ฆฌ ์ต์
์ ๋๊ธธ ์ ์๋๋ก options
ํ๋ผ๋ฏธํฐ๋ฅผ ์ถ๊ฐํ๋ค. ์ฟผ๋ฆฌ ์ต์
์ React Query์์ ์ ๊ณตํ๋ UseQueryOptions
ํ์
์ import ํด์ ์ฌ์ฉํ๋ฉด ๋๋ค. UseQueryOptions
, useQuery
๋์ ํ์
์ธํฐํ์ด์ค๋ ๋์ผํ๋ค.
UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>
// ์ปค์คํ
ํ
์ ์
import { useQuery, UseQueryOptions } from "react-query";
// ...
export default function useClientQuery<T = Client[]>(
options?: UseQueryOptions<Client[], AxiosError, T>,
) {
const { getClientList } = ClientAPI;
return useQuery<Client[], AxiosError, T>(
queryKeys.CLIENT_LIST,
getClientList, // resolved data๋ฅผ ๋ฐํํ๋ ์ฟผ๋ฆฌ ํจ์
options,
);
}
useClientQuery
์ ๋ค๋ฆญ ํ์
T
์ ๊ธฐ๋ณธ๊ฐ์ Client[]
๋ก ์ค์ ํ์ผ๋ฏ๋ก select
ํจ์๋ฅผ ๋๊ธฐ์ง ์์์ ๋ data
ํ์
์ Client[]
๊ฐ ๋๋ค. select
ํจ์๋ฅผ ๋๊ธฐ๋ฉด select
ํจ์๊ฐ ๋ฐํํ๋ ๊ฐ์ ํ์
์ด data
ํ์
์ด ๋๋ค.
๐ก select ํจ์ ์ธ์์ ์ฟผ๋ฆฌ ํจ์์ ๋ฆฌํด ๊ฐ์ด ์ ๋ฌ๋๋ค. ์ ์์์์ Client[]
ํ์
์ data๊ฐ ์ ๋ฌ๋๋ค. ์ฐธ๊ณ ๋ก select ํจ์๋ ์บ์์ ์ ์ฅ๋๋ ๋ด์ฉ์ ์ํฅ์ ์ฃผ์ง ์๊ณ , ๋ฐํํ ๋ฐ์ดํฐ ๊ฐ์๋ง ์ํฅ์ ์ค๋ค.
// ์ปค์คํ
ํ
์ฌ์ฉ
// ๋ค๋ฅธ ์ปดํฌ๋ํธ์์ ์ปค์คํ
ํ
์ ์ฌ์ฉํ ๋
const selectFn = (data: Client[]) => { /* ... */ };
const { data, ...queryResults } = useClientQuery({ select: selectFn /* ... */ });
ํ๋ผ๋ฏธํฐ๊ฐ ์์ ๋
URL์ ๋์ path
๋ฅผ ์ถ๊ฐํด์ผ ํ๋ API๋ผ๋ฉด(/orders/{userId}
) Base Query ํ
์ด path
๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์ ์ ์๋๋ก ํ๊ณ ์ฟผ๋ฆฌ ํจ์๋ก path
๋ฅผ ๋๊ธฐ๋ฉด ๋๋ค. ์๋ ์์์์ ์ฟผ๋ฆฌ ํจ์๊ฐ resolved data๋ฅผ ๋ฐํํ๊ธฐ ๋๋ฌธ์ ์ฟผ๋ฆฌ ํจ์๋ฅผ async () => (await queryFn(params)).data
ํํ๋ก ์์ฑํ๋ค.
// ์ปค์คํ
ํ
์ ์
import { useQuery, UseQueryOptions } from "react-query";
// ...
export default function useOrderQuery<T = Order[]>(
userId: number,
options?: UseQueryOptions<Order[], AxiosError, T>,
) {
const { getOrdersById } = OrderAPI;
return useQuery<Order[], AxiosError, T>(
queryKeys.ORDER_LIST,
async () => (await getOrdersById(userId)).data, // resolved data๋ฅผ ๋ฐํํ๋ ์ฟผ๋ฆฌ ํจ์
options,
);
}
์ฟผ๋ฆฌ ํจ์ ๋ฐํ๊ฐ AxiosResponse vs Resolved Data โก๏ธ
์ฟผ๋ฆฌ ํจ์๊ฐ AxiosResponse
๋ฅผ ๋ฐํํ๋ฉด ์ฟผ๋ฆฌ์ TQueryFnData
ํ์
๋ ๋์ผํ๊ฒ ์ง์ ํด์ผ ํ๋ค.
//์ฟผ๋ฆฌ ํจ์๊ฐ AxiosResponse๋ฅผ ๋ฐํํ ๋
// AxiosResponse๋ฅผ ๋ฐํํ๋ ์ฟผ๋ฆฌ ํจ์
const getClientList = async () => {
return await axios.get<Client[]>("clients");
};
// useClientQuery ํ
return useQuery<AxiosResponse<Client[]>, AxiosError, T>(/* ... */);
์ฟผ๋ฆฌ ํจ์๊ฐ resolved data๋ฅผ ๋ฐํํ๋ฉด TQueryFnData
ํ์
์ ๋ฐํ ๋ฐ์ดํฐ ํ์
(Client[]
)๋ง ์ง์ ํ๋ฉด ๋๋ค.
// ์ฟผ๋ฆฌ ํจ์๊ฐ resolved data๋ฅผ ๋ฐํํ ๋
// resolved data๋ฅผ ๋ฐํํ๋ ์ฟผ๋ฆฌ ํจ์
const getClientList = async () => {
const { data } = await axios.get<Client[]>("clients");
return data;
};
// useClientQuery ํ
return useQuery<Client[], AxiosError, T>(/* ... */);
useMutation
์ ๋ค๋ฆญ ํ์ ํบ์๋ณด๊ธฐ โก๏ธ
export function useMutaion<
TData = unknown,
TError = unknown,
TVariables = void,
TContext = unknown
>
โถ TData
: mutation
ํจ์ ์คํ ๊ฒฐ๊ณผ ํ์
(AxiosResponse<T>
๋ฑ)
TData
ํ์
์ โถonSuccess
์ฝ๋ฐฑ์ ์ธ์ ํ์
๊ณผ โทuseMutation
ํ
์ด ๋ฐํํ๋ data
ํ์
์ด ๋๋ค.
const { data } = useMutation<AxiosResponse<boolean>>(postTodo, {
onSuccess: (res) => { /* ... */ },
});
// data ํ์
: AxiosResponse<boolean> | undefined
// onSuccess ์ฝ๋ฐฑ์ res ์ธ์ ํ์
: AxiosResponse<boolean>
โท TError
: mutation
ํจ์ ์๋ฌ ํ์
(AxiosError
๋ฑ)
TError
ํ์
์ โถonError
์ฝ๋ฐฑ์ ์ธ์ ํ์
๊ณผ โทuseMutation
ํ
์ด ๋ฐํํ๋ error
ํ์
์ด ๋๋ค.
const { error } = useMutation<AxiosResponse<boolean>, AxiosError>(postTodo, {
onError: (err) => { /* ... */ },
});
// error ํ์
: AxiosError | null
// onError ์ฝ๋ฐฑ์ err ์ธ์ ํ์
: AxiosError
โธ TVariables
: mutation
ํจ์์ ์ธ์ ํ์
TVariables ํ์
์ mutate
ํจ์์ onSuccess
๋ฑ ์ต์
๋ฉ์๋ ์ฝ๋ฐฑ ํจ์์ ์ธ์ ํ์
์ด ๋๋ค.
const { mutate } = useMutation<AxiosResponse<boolean>, AxiosError, number>(
postTodo, // mutationFn
{
onSuccess: (res, id) => { /* ... */ },
onError: (err, id) => { /* ... */ },
onMutate: (id) => { /* ... */ },
onSettled: (res, err, id) => { /* ... */ },
},
);
const onClick = () => mutate(8);
โน TContext
: onMutate
์ฝ๋ฐฑ ํจ์์ ๋ฆฌํด ํ์
onMutate
์ฝ๋ฐฑ ํจ์๋ mutation
ํจ์ ์คํ ์ ์ ์คํ๋๋ฉฐ, onMutate
๊ฒฐ๊ณผ ๊ฐ์ onSuccess
๊ฐ์ ์ต์
๋ฉ์๋์ ์ฝ๋ฐฑ ํจ์ ์ธ์๋ก ์ ๋ฌ๋๋ค.
const { mutate } = useMutation<
AxiosResponse<boolean>,
AxiosError,
number,
number
>(postTodo, {
onSuccess: (res, id, nextId) => { /* ... */ },
onError: (err, id, nextId) => { /* ... */ },
onMutate: (id) => id + 1, // onMutate ์ฝ๋ฐฑ ํจ์์ ์ธ์(id) ํ์
์ TVariables
onSettled: (res, err, id, nextId) => { /* ... */ },
});
// onSuccess, onMutate, onSettled ์ฝ๋ฐฑ์ nextId ์ธ์๋ onMutate์ ๋ฆฌํด๊ฐ(number)
Base Mutation ์ปค์คํ ํ
Base Query ์ปค์คํ
ํ
์ ์ ์ํ ๊ฒ์ฒ๋ผ ํ
์ ์ฌ์ฉํ๋ ๊ณณ์์ mutation ์ต์
์ ๋๊ธธ ์ ์๋๋ก options
ํ๋ผ๋ฏธํฐ๋ฅผ ์ถ๊ฐํ๋ค. ๋ง์ฐฌ๊ฐ์ง๋ก UseQueryOptions
ํ์
์ importํด์ ์ฌ์ฉํ๋ค.
๐ invalidateQueries vs setQueryData : invalidation is less code on the frontend, but one more network request. That's the tradeoff (via TanStack Discussions)
- invalidateQueries : refetch related queries that should be stale after the mutation
- setQueryData : merge mutation response to current query data
mutation ํจ์๋ฅผ ํธ์ถํ ์ดํ ๊ธฐ๋ณธ์ ์ผ๋ก ์ํํ ์์
์ด ์๋ค๋ฉด onSuccess
, onError
๊ฐ์ ์ต์
๋ฉ์๋๋ฅผ ๋ฏธ๋ฆฌ ์ ์ํด๋ ์๋ ์๋ค. ์ฐธ๊ณ ๋ก mutation์ ๋ช
์ํ ์ฟผ๋ฆฌํค๋ฅผ stale ๋ฐ์ดํฐ๋ก ์ทจ๊ธํ๋๋กํด์ re-fetching์ ํธ๋ฆฌ๊ฑฐํ๋ queryClient.invalidateQueries
๋ฉ์๋์ ์์ฃผ ์ฌ์ฉํ๋ค.
// ์ปค์คํ
ํ
์ ์
import { useMutation, UseQueryOptions } from "react-query";
// ...
export default function useAddTodoMutation(
options?: UseMutationOptions<PostTodoResponse, AxiosError, PostTodoPayload>,
) {
// PostTodoResponse ํ์
: AxiosResponse<boolean>
// PostTodoPayload ํ์
: number
return useMutation<PostTodoResponse, AxiosError, PostTodoPayload>(
// AxiosResponse๋ฅผ ๋ฐํํ๋ mutation ํจ์ ↓
// id ํ๋ผ๋ฏธํฐ ํ์
์ PostTodoPayload ↓
(id) => postTodo(id),
options,
);
}
useAddTodoMutation
ํ
์ด ๋ฐํํ๋ mutate
ํจ์๋ก mutation์ ์คํ์ํฌ ์ ์๋ค. mutate
ํจ์์ ์ฒซ๋ฒ์งธ ์ธ์๋ mutation ํจ์๋ก ์ ๋ฌ๋๋ฉฐ, useMutation
3๋ฒ์งธ ์ ๋ค๋ฆญ ํ์
์ผ๋ก ๋๊ธด PostTodoPayload
ํ์
(TVariables
)์ ๊ฐ๋๋ค. ํ์์ ๋ฐ๋ผ onSuccess
๊ฐ์ ์ต์
๋ฉ์๋๋ฅผ ๋๊ธธ ์๋ ์๋ค.
// ์ปค์คํ
ํ
์ฌ์ฉ
const onSuccess = () => { /* ... */ };
const { mutate, ...useMutationResults } = useAddTodoMutation({ onSuccess });
const onClick = (id: PostTodoPayload) => mutate(id);
// mutate ํจ์ ๋๋ฒ์งธ ์ธ์์ { onError, onSettled, onSuccess }๋ฅผ ๋๊ธธ ์๋ ์๋ค.
// ex) mutate(id, { onSuccess: () => {} })
// ์คํ ์์ : useMutation ์ต์
๋ฉ์๋ -> mutate ์ต์
๋ฉ์๋
์ ๋ค๋ฆญ์ผ๋ก ๊น๋ํ๊ฒ ์์ฑํ๊ธฐ โก๏ธ
useQuery|Mutation์ ์ ๋ค๋ฆญ์ ์ฌ์ฉํ๋ ค๋ฉด ์ฟผ๋ฆฌ(API) ํจ์๋ ์ ๋ค๋ฆญ์ ๋ฐ์ ์ ์๋๋ก ์์ ํด์ผ ํ๋ค.
const getClientList = async <T = Client[],>() => {
const { data } = await axios.get<T>("clients");
return data;
};
Query ํ
์ ์ ๋ค๋ฆญ T
๋ ์ฟผ๋ฆฌ ํจ์ ๋ฆฌํด ํ์
(TQueryFnData
)์ผ๋ก, K
๋ ์ต์ข
๋ฆฌํด ํ์
(TData
)์ผ๋ก ๋ค์ด๊ฐ๋๋ก ์์ ํ๋ค. T
, K
๋ชจ๋ ๊ธฐ๋ณธ๊ฐ์ ์ง์ ํ์ผ๋ฏ๋ก, ์ง์ ํ ํ์
๊ณผ ๋์ผํ๋ค๋ฉด ๋ฐ๋ก ๋๊ธฐ์ง ์์๋ ๋๋ค.
// useQuery ์ ๋ค๋ฆญ ์ฌ์ฉ ์์
export default function useClientQuery<T = Client[], K = T>(
options?: UseQueryOptions<T, AxiosError, K>,
) {
const { getClientList } = ClientAPI;
return useQuery<T, AxiosError, K>(
queryKeys.CLIENT_LIST,
getClientList,
options,
);
}
Mutation ํ
์ ์ ๋ค๋ฆญ T
๋ mutation ํจ์ ์คํ ๊ฒฐ๊ณผ ํ์
(TData
)์ผ๋ก, K
๋ mutation ํจ์์ ์ธ์ ํ์
(TVariables
)์ผ๋ก ๋ค์ด๊ฐ๋๋ก ์์ ํ๋ค. ๋ง์ฐฌ๊ฐ์ง๋ก ์ฟผ๋ฆฌ ํจ์๊ฐ ์ ๋ค๋ฆญ์ ๋ฐ์ ์ ์์ด์ผ ํ๋ค.
// useMutation ์ ๋ค๋ฆญ ์ฌ์ฉ ์์
export default function useAddTodoMutation<
T = PostTodoResponse,
K = PostTodoPayload,
>(options?: UseMutationOptions<T, AxiosError, K>) {
return useMutation<T, AxiosError, K>((id) => postTodo(id), options);
}
๋ ํผ๋ฐ์ค
- useMutation | TanStack Query Docs
- [React Query] ๋ฆฌ์กํธ ์ฟผ๋ฆฌ useQuery ์ค์ฉ ํธ
- react-query์ typescript ์ ์ฉํ๊ธฐ - ๋ฆฌ์กํธ ์ฟผ๋ฆฌ, ํ์ ์คํฌ๋ฆฝํธ
๊ธ ์์ ์ฌํญ์ ๋ ธ์ ํ์ด์ง์ ๊ฐ์ฅ ๋น ๋ฅด๊ฒ ๋ฐ์๋ฉ๋๋ค. ๋งํฌ๋ฅผ ์ฐธ๊ณ ํด ์ฃผ์ธ์
'๐ช Programming' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
๋๊ธ
์ด ๊ธ ๊ณต์ ํ๊ธฐ
-
๊ตฌ๋
ํ๊ธฐ
๊ตฌ๋ ํ๊ธฐ
-
์นด์นด์คํก
์นด์นด์คํก
-
๋ผ์ธ
๋ผ์ธ
-
ํธ์ํฐ
ํธ์ํฐ
-
Facebook
Facebook
-
์นด์นด์ค์คํ ๋ฆฌ
์นด์นด์ค์คํ ๋ฆฌ
-
๋ฐด๋
๋ฐด๋
-
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
-
Pocket
Pocket
-
Evernote
Evernote
๋ค๋ฅธ ๊ธ
-
[TS] ์๋ฆฌ๋จผํธ์ ๊ธฐ๋ณธ ์ดํธ๋ฆฌ๋ทฐํธ ์ธํฐํ์ด์ค/ํ์ ์ฌ์ฉํ๊ธฐ
[TS] ์๋ฆฌ๋จผํธ์ ๊ธฐ๋ณธ ์ดํธ๋ฆฌ๋ทฐํธ ์ธํฐํ์ด์ค/ํ์ ์ฌ์ฉํ๊ธฐ
2024.05.12 -
[JS] ์ JSX ์์์ if ๋ฌธ์ ์ฌ์ฉํ ์ ์์๊น? ํํ์๊ณผ ๋ฌธ ์ฐจ์ด์
[JS] ์ JSX ์์์ if ๋ฌธ์ ์ฌ์ฉํ ์ ์์๊น? ํํ์๊ณผ ๋ฌธ ์ฐจ์ด์
2024.05.12 -
[React] ๋ฆฌ์กํธ ์ฟผ๋ฆฌ(React Query) staleTime์ ์ค์ ํ๋ 3๊ฐ์ง ๋ฐฉ๋ฒ
[React] ๋ฆฌ์กํธ ์ฟผ๋ฆฌ(React Query) staleTime์ ์ค์ ํ๋ 3๊ฐ์ง ๋ฐฉ๋ฒ
2024.05.12 -
[Git] ๋ณํฉ(Merge) ์ถฉ๋ ๋ฐฉ์ง๋ฅผ ์ํ ๋ฆฌ๋ฒ ์ด์ค Rebase
[Git] ๋ณํฉ(Merge) ์ถฉ๋ ๋ฐฉ์ง๋ฅผ ์ํ ๋ฆฌ๋ฒ ์ด์ค Rebase
2024.05.12