처음 React Query(TanStack Query)를 도입하면서 사용법이 낯설다 보니 많이 헤맸었다.
그 중 특히나 queryKey에 대한 것이 어렵기도 하고, 헷갈리기도 했었는데 이번에 한번 정리를 해보려한다.
React Query에서 제공하는 hooks인 useQuery와 useMutation을 사용하면 해당 hooks의 queryKey
를 지정하게 된다.
At its core, React Query manages query caching for you based on query keys. Query keys can be as simple as a string, or as complex as an array of many strings and nested objects. As long as the query key is serializable, and unique to the query's data, you can use it!
- TanStack Query Docs: Query Keys Article
위 글은 React Query Docs에서 소개하고 있는 queryKey의 역할이다. (아래 해석/정리)
문자열
, 문자열의 배열
혹은 중첩된 객체(nested object)
로 지정 가능하다.v4
부터 바뀐 점으로 queryKey를 작성할 때는 key값만 작성하더라도 배열([]
)안에 담아줘야한다.
그리고 만약 query에 대한 추가적인 정보가 필요하면 이에 대한 내용을 추가적인 내용을 배열
안에 작성해 줄 수 있다.
id
, index
및 기타 기본값
을 전달할 때나는 두번째 경우를 많이 경험했던 것 같다. 처음엔 도대체가 쿼리 스트링을 어케 전달하지?! 하다가 깨달은 후 매우 유용하게 편해졌었다.
// A list of todos
useQuery({ queryKey: ['todos'], ... })
// Something else, whatever!
useQuery({ queryKey: ['something', 'special'], ... })
위 예시처럼 hooks안의 값을 객체{}
로 하고, 이에 대한 키값을 queryKey로 하여 배열을 key값으로 지정해줄 수 있다.
또한 아래처럼 간소화하여 작성하는 것도 가능하며, 이 때 인식되는 순서는 다음과 같다.
// 1. queryKey / 2. queryFn / 3. queryOptions // ------------------------------------------- useQuery([queryKeys], queryFn, queryOptions )
// A list of todos
useQuery(['todos'], ... )
// Something else, whatever!
useQuery(['something', 'special'], ... )
앞에서 언급했던 것처럼 query에 대한 추가적인 정보를 같이 넣어줘야할 때 아래와 같이 작성해주면 된다.
// An individual todo
useQuery(['todo', 5], ...)
// queryKey === ['todo', 5]
// An individual todo in a "preview" format
useQuery(['todo', 5, { preview: true }], ...)
// queryKey === ['todo', 5, { preview: true }]
// A list of todos that are "done"
useQuery(['todos', { type: 'done' }], ...)
// queryKey === ['todos', { type: 'done' }]
가끔 문장을 변역해서 이해하는 것보다 영어 자체로 이해하는 것이 좋을 때가 있다. 이 경우가 그러한데, docs에서는 아래로 작성된 문구이다.
Query Keys are hashed deterministically!
이게 무슨 뜻이가 문장만으로 백날 이해하려 해봤자 안 된다(적어도 나는 그랬다). 예시로 이해해보자.
아래의 경우엔 queryKey외의 정보가 {}
를 통해 묶여있다. 이러면 객체({}
) 안의 키 순서가 어떤지 간에 동일한 작성문으로 취급된다.
useQuery(['todos', { status, page }], ...)
useQuery(['todos', { page, status }], ...)
useQuery(['todos', { page, status, other: undefined }], ...)
그러나 아래처럼 {}
로 묶이지 않고 배열안에 같은 위치상으로 작성된 경우엔 모두 다르게 이해된다. 즉, 작성 순서가 중요해진다.
useQuery(['todos', status, page], ...)
useQuery(['todos', page, status], ...)
useQuery(['todos', undefined, page, status], ...)
[본문]
If your query function depends on a variable, include it in your query key.
- Since query keys uniquely describe the data they are fetching, they should include any variables you use in your query function that change.
😮처음 든 생각:
이게 뭔 소리지..? 어우 말이 너무 어렵다.
영어 본문을 처음 읽었을때 든 생각인 진짜 '뭔 말이지?'였다.
그래서 본문과 예문을 뚫어져라 쳐다보다가.. 깨닳았다!
function Todos({ todoId }) {
const result = useQuery(['todos', todoId], () => fetchTodoById(todoId))
}
위 예문을 보면 fetchTodoById
라는 queryFn에서 todoId라는 변수에 의존적으로 작동하고 있다.
즉, 이렇게 queryFn에서 사용되는 변수가 있다면 이 변수는 queryKeys에도 포함되어야 한다는 것이다.
그래서 다시 위를 보면 queryKeys안에 ['todos', todoId]
라고 todoId변수가 배열 안에 있는 것을 볼 수 있다.
그냥 이랬었다~ 하는 참고 자료정도로 읽고 넘어가면 되겠다. 이제는 사용되지 않는 key방식이다.
사실 docs를 읽을 때 처음에 v3 docs를 읽어서 정리했다가 지우기 아까워서ㅎㅎㅎ
v3
기준에서 쓸 수 있었던 queryKey의 가장 단순한 형식으로써, 배열이 아닌 string
으로만 지정된 query key이다.
물론 이 시절엔 결국 키가 전달된 후 내부적으로 해당 스트링 키를 지니는 배열로 변환했다. 이는 아래 주석을 보면 이해가 편할 것이다.
그래서인지
v4
부터는 아예 배열로만 사용하도록 바뀌었다.
// A list of todos
useQuery('todos', ...) // queryKey === ['todos']
// Something else, whatever!
useQuery('somethingSpecial', ...) // queryKey === ['somethingSpecial']
그리고 이러한 싱글 스트링 키의 경우엔 아래 경우 유용하다고 명시되어 있다.
물론 이제 쓸 일은 없다.
요즘 트렌드인 서버상태관리 도구이기도 하고 내가 처음 접한 서버관리 도구이기도 한만큼 관심이 있었는데,
이전에 사용할 때 몸으로 부딪히며 쓰면서 배웠던 걸 다시 문서를 읽어보며 정리하니 좀 더 머릿속도 정리되는 느낌이 들어서 좋다.
좀더 문서를 읽어보면서 더 정리해야겠다.