[React Query] queryKey 작성방법 및 특징

정(JJeong)·2023년 4월 7일
15
post-thumbnail

처음 React Query(TanStack Query)를 도입하면서 사용법이 낯설다 보니 많이 헤맸었다.
그 중 특히나 queryKey에 대한 것이 어렵기도 하고, 헷갈리기도 했었는데 이번에 한번 정리를 해보려한다.

📌 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의 역할이다. (아래 해석/정리)

queryKey역할과 특징

  1. React Query에서는 query keys를 기반으로 해서 쿼리 캐싱을 관리한다.
  2. query key는 문자열, 문자열의 배열 혹은 중첩된 객체(nested object)로 지정 가능하다.
  3. query keys는 query data에 고유하고 직렬화하여 사용해야한다.


📌 queryKeys 규칙

querykeys는 반드시 배열이어야 한다.

v4부터 바뀐 점으로 queryKey를 작성할 때는 key값만 작성하더라도 배열([])안에 담아줘야한다.

그리고 만약 query에 대한 추가적인 정보가 필요하면 이에 대한 내용을 추가적인 내용을 배열안에 작성해 줄 수 있다.

  • Hierarchical or nested resources(계층적 or 중첩 리소스)
    • 항목을 식별하기 위한 id, index기타 기본값을 전달할 때
  • Queries with additional parameters(추가 쿼리 매개변수)
    • 쿼리 스트링으로 추가 매개변수를 전달하고자 할 때

나는 두번째 경우를 많이 경험했던 것 같다. 처음엔 도대체가 쿼리 스트링을 어케 전달하지?! 하다가 깨달은 후 매우 유용하게 편해졌었다.

기본 작성 규칙

  1. 배열로 작성되는 queryKey의 값은 string key를 먼저 작성한다.
  2. string key는 여러 개를 지정할 수 있지만 key가 연관되는 data는 고유하다.
  3. query에 필요한 추가 정보는 string key 뒤에 작성한다.


queryKeys 작성 예시

#1 Simple Query Key

// 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'], ... )

#2 Array Keys with variables

앞에서 언급했던 것처럼 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' }]


📌 기타 참고해야할 특징

query keys는 결국 해시(hashed)된다!

가끔 문장을 변역해서 이해하는 것보다 영어 자체로 이해하는 것이 좋을 때가 있다. 이 경우가 그러한데, docs에서는 아래로 작성된 문구이다.

Query Keys are hashed deterministically!

이게 무슨 뜻이가 문장만으로 백날 이해하려 해봤자 안 된다(적어도 나는 그랬다). 예시로 이해해보자.

경우#1

아래의 경우엔 queryKey외의 정보가 {}를 통해 묶여있다. 이러면 객체({}) 안의 키 순서가 어떤지 간에 동일한 작성문으로 취급된다.

useQuery(['todos', { status, page }], ...)
useQuery(['todos', { page, status }], ...)
useQuery(['todos', { page, status, other: undefined }], ...)

경우#2

그러나 아래처럼 {}로 묶이지 않고 배열안에 같은 위치상으로 작성된 경우엔 모두 다르게 이해된다. 즉, 작성 순서가 중요해진다.

useQuery(['todos', status, page], ...)
useQuery(['todos', page, status], ...)
useQuery(['todos', undefined, page, status], ...)

queryKey는 queryFunction의 변수도 포함한다.

[본문]
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변수가 배열 안에 있는 것을 볼 수 있다.


Regacy Info.

String-only Query keys

그냥 이랬었다~ 하는 참고 자료정도로 읽고 넘어가면 되겠다. 이제는 사용되지 않는 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']

그리고 이러한 싱글 스트링 키의 경우엔 아래 경우 유용하다고 명시되어 있다.

  • Generic List/Index resources(일반적/인덱스 리소스)
  • Non-hierarchical resources(비계층적 리소스)

물론 이제 쓸 일은 없다.



마무리

요즘 트렌드인 서버상태관리 도구이기도 하고 내가 처음 접한 서버관리 도구이기도 한만큼 관심이 있었는데,

이전에 사용할 때 몸으로 부딪히며 쓰면서 배웠던 걸 다시 문서를 읽어보며 정리하니 좀 더 머릿속도 정리되는 느낌이 들어서 좋다.

좀더 문서를 읽어보면서 더 정리해야겠다.



profile
2년차 응애 FE 개발자입니다👶

0개의 댓글