
처음 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']
그리고 이러한 싱글 스트링 키의 경우엔 아래 경우 유용하다고 명시되어 있다.
물론 이제 쓸 일은 없다.
요즘 트렌드인 서버상태관리 도구이기도 하고 내가 처음 접한 서버관리 도구이기도 한만큼 관심이 있었는데,
이전에 사용할 때 몸으로 부딪히며 쓰면서 배웠던 걸 다시 문서를 읽어보며 정리하니 좀 더 머릿속도 정리되는 느낌이 들어서 좋다.
좀더 문서를 읽어보면서 더 정리해야겠다.