useQuery 의 queryKey 배열에 대해 설명해주세요
useQuery
의 queryKey
는 React Query에서 캐싱 및 상태 관리의 핵심 요소입니다. queryKey
는 쿼리의 고유한 식별자로 사용되며, React Query는 이를 기반으로 데이터 캐싱, 데이터 무효화, 리패칭 등을 관리합니다.
queryKey
의 역할
- 데이터 캐싱 및 식별:
queryKey
는 쿼리의 고유 식별자입니다. 동일한queryKey
를 가진 쿼리는 동일한 데이터 캐시를 공유합니다.- React Query는
queryKey
를 사용하여 캐시된 데이터를 저장하고 검색합니다.
- 쿼리 무효화 및 리패칭:
- 특정
queryKey
를 가진 쿼리를 무효화하면, React Query는 해당queryKey
에 대한 데이터를 무효화하고, 필요에 따라 다시 패칭할 수 있습니다. - 예를 들어,
queryClient.invalidateQueries(['todos'])
를 호출하면['todos']
키를 가진 모든 쿼리가 무효화됩니다.
- 특정
- 조건부 쿼리 실행:
queryKey
배열의 요소를 사용하여 조건부로 쿼리를 실행할 수 있습니다.- 예를 들어, 특정 사용자의 데이터를 가져올 때 사용자 ID를
queryKey
에 포함하여 해당 사용자의 데이터만 가져올 수 있습니다.
queryKey
의 구성
- 기본 키: 주로 문자열로 구성된 고유 식별자입니다. 예:
['todos']
,['user', userId]
. - 다중 요소 키: 배열 형태로, 여러 요소를 포함할 수 있습니다. 예:
['user', userId, { filter: 'active' }]
.
예제
단일 요소 queryKey
import { useQuery } from '@tanstack/react-query';
const fetchTodos = async () => {
const response = await fetch('/api/todos');
return response.json();
};
function Todos() {
const { data, isLoading, error } = useQuery(['todos'], fetchTodos);
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<ul>
{data.map(todo => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
);
}
다중 요소 queryKey
import { useQuery } from '@tanstack/react-query';
const fetchUser = async ({ queryKey }) => {
const [_key, userId] = queryKey;
const response = await fetch(`/api/users/${userId}`);
return response.json();
};
function User({ userId }) {
const { data, isLoading, error } = useQuery(['user', userId], fetchUser);
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<h1>{data.name}</h1>
<p>{data.email}</p>
</div>
);
}
조건부 쿼리 실행
import { useQuery } from '@tanstack/react-query';
const fetchUser = async ({ queryKey }) => {
const [_key, userId] = queryKey;
if (!userId) throw new Error('No user ID provided');
const response = await fetch(`/api/users/${userId}`);
return response.json();
};
function User({ userId }) {
const { data, isLoading, error } = useQuery(['user', userId], fetchUser, {
enabled: !!userId, // userId가 있을 때만 쿼리 실행
});
if (!userId) return <div>Please provide a user ID</div>;
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<h1>{data.name}</h1>
<p>{data.email}</p>
</div>
);
}
요약
- 고유 식별자:
queryKey
는 쿼리의 고유한 식별자로 사용되어 데이터 캐싱, 검색, 무효화 등을 관리합니다. - 다중 요소: 배열 형태로 여러 요소를 포함할 수 있어 조건부 쿼리 실행, 고유한 데이터 관리 등에 유용합니다.
- 조건부 실행:
queryKey
의 요소를 사용하여 쿼리를 조건부로 실행할 수 있습니다.
React Query에서 queryKey
는 데이터의 고유성을 보장하고, 상태 관리 및 캐싱 메커니즘을 효율적으로 처리하는 데 중요한 역할을 합니다.
queryKey 배열의 두번째 요소로 accessToken 을 넣는 경우?
queryKey
배열에 accessToken
을 두 번째 요소로 넣는 이유는 React Query의 캐시 키를 더 구체적으로 만들어주기 위해서입니다. 이를 통해 여러 사용자 또는 여러 세션에서 서로 다른 데이터를 관리하고, 캐시를 효과적으로 분리할 수 있습니다.
이유 및 효과
- 캐시 분리:
queryKey
배열의 각 요소는 캐시 키의 일부로 사용됩니다.["user", accessToken]
을 사용하면, 각accessToken
에 대해 고유한 캐시 엔트리가 생성됩니다.- 예를 들어, 사용자 A와 사용자 B의
accessToken
이 다르기 때문에, 두 사용자에 대해 고유한 캐시가 생성됩니다. 따라서 사용자 A의 데이터는 사용자 B의 데이터와 섞이지 않습니다.
- 데이터 무효화:
queryKey
는 쿼리를 무효화할 때도 사용됩니다.invalidateQueries(["user", accessToken])
을 호출하면 특정accessToken
에 대한 데이터만 무효화됩니다.- 이 방식으로 특정 사용자의 데이터만 무효화하거나 새로고침할 수 있습니다.
예시 코드
아래 예시에서는 accessToken
이 쿼리 키의 일부로 사용됩니다. 이를 통해 사용자별로 고유한 데이터를 관리합니다.
import { useQuery, UseQueryOptions } from "@tanstack/react-query";
import { useAuthStore } from "@/zustand/auth.store";
import api from "@/api/api";
import { AuthData, AxiosReturn } from "@/types/d";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { useShallow } from "zustand/react/shallow";
import { AxiosError } from "axios";
interface ChangeProfileParams {
accessToken: string | null;
data: AuthData;
}
function useAuth() {
const queryClient = useQueryClient();
const { user, isLoggedIn, logOut, setUser, setLoggedIn } = useAuthStore(
useShallow((state) => ({
user: state.user,
isLoggedIn: state.isLoggedIn,
logOut: state.logOut,
setUser: state.setUser,
setLoggedIn: state.setLoggedIn,
}))
);
const accessToken = localStorage.getItem("accessToken");
const { data: userData, isLoading: userLoading } = useQuery<AxiosReturn, AxiosError>({
queryKey: ["user", accessToken ?? ""],
queryFn: () => api.auth.getUser(accessToken!),
staleTime: Infinity,
retry: 0,
onError: (error) => {
if (error instanceof AxiosError) {
console.log("유저 데이터 가져오기 실패 => ", error.response?.data.message);
} else {
console.log("유저 데이터 가져오기 실패");
}
},
});
// 이 효과를 사용하여 쿼리가 성공적으로 완료된 후 데이터를 설정합니다.
useEffect(() => {
if (userData && userData.id && userData.nickname) {
setUser({
userId: userData.id,
avatar: userData.avatar ? userData.avatar : null,
nickname: userData.nickname,
});
setLoggedIn(true);
}
}, [userData, setUser, setLoggedIn]);
const { mutateAsync: signUp } = useMutation({
mutationFn: (data: AuthData) => api.auth.signUp(data),
});
const { mutateAsync: logIn } = useMutation({
mutationFn: (data: AuthData) => api.auth.logIn(data),
});
const { mutateAsync: changeProfile } = useMutation({
mutationFn: ({ accessToken, data }: ChangeProfileParams) =>
api.auth.changeProfile(accessToken, data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["user", accessToken ?? ""] });
},
});
return { user, isLoggedIn, signUp, logIn, changeProfile, logOut, userLoading };
}
export default useAuth;
요약
- 캐시 분리:
queryKey
에accessToken
을 포함하여 각 사용자의 데이터를 별도로 관리합니다. - 데이터 무효화: 특정 사용자의 데이터를 무효화하거나 새로고침할 때
accessToken
을 사용하여 정확한 데이터를 타겟팅할 수 있습니다. - 보안: 각 사용자의 데이터를 별도로 관리함으로써 보안을 강화하고 데이터의 혼합을 방지합니다.
이 방식은 React Query의 강력한 캐싱 및 데이터 관리 기능을 활용하여 응용 프로그램의 성능과 유연성을 향상시키는 데 매우 유용합니다.
'library' 카테고리의 다른 글
[240620 TIL] Tanstack Query + Zustand (0) | 2024.06.20 |
---|---|
[240617 TIL] Optimistic Update with Tanstack Query (1) | 2024.06.17 |
[240614 TIL] TanstackQuery - Life Cycle (0) | 2024.06.14 |
[240613 TIL] Auth with Tanstack Query (0) | 2024.06.13 |
[240605 TIL] Tanstack Query - 2 (0) | 2024.06.05 |