🔥 쿼리 키

378자
5분

TanStack Query는 쿼리 키를 기반으로 쿼리 캐싱을 관리합니다. 쿼리 키는 최상위 레벨에서 반드시 배열이어야 하며, 단일 문자열로 구성된 간단한 배열부터 여러 문자열과 중첩된 객체로 이루어진 복잡한 배열까지 다양한 형태를 가질 수 있습니다. 쿼리 키가 직렬화 가능하고 쿼리의 데이터에 대해 고유하다면 어떤 형태든 사용할 수 있습니다!

간단한 쿼리 키

가장 기본적인 형태의 키는 상수 값으로 구성된 배열입니다. 이러한 형식은 다음과 같은 경우에 유용합니다:

  • 일반적인 목록/인덱스 리소스
  • 계층 구조가 없는 리소스
typescript
// 할 일 목록
useQuery({ queryKey: ['todos'], ... })
 
// 다른 특별한 것
useQuery({ queryKey: ['something', 'special'], ... })
 
typescript
// 할 일 목록
useQuery({ queryKey: ['todos'], ... })
 
// 다른 특별한 것
useQuery({ queryKey: ['something', 'special'], ... })
 

변수가 포함된 배열 키

쿼리가 데이터를 고유하게 설명하기 위해 더 많은 정보가 필요한 경우, 문자열과 직렬화 가능한 객체를 조합한 배열을 사용할 수 있습니다. 이는 다음과 같은 경우에 유용합니다:

  • 계층적이거나 중첩된 리소스
    • 항목을 고유하게 식별하기 위해 ID, 인덱스 또는 다른 기본 값을 전달하는 것이 일반적입니다
  • 추가 매개변수가 있는 쿼리
    • 추가 옵션 객체를 전달하는 것이 일반적입니다
typescript
// 개별 할 일
useQuery({ queryKey: ['todo', 5], ... })
 
// '미리보기' 형식의 개별 할 일
useQuery({ queryKey: ['todo', 5, { preview: true }], ...})
 
// '완료된' 할 일 목록
useQuery({ queryKey: ['todos', { type: 'done' }], ... })
 
typescript
// 개별 할 일
useQuery({ queryKey: ['todo', 5], ... })
 
// '미리보기' 형식의 개별 할 일
useQuery({ queryKey: ['todo', 5, { preview: true }], ...})
 
// '완료된' 할 일 목록
useQuery({ queryKey: ['todos', { type: 'done' }], ... })
 

쿼리 키는 결정론적으로 해시됩니다!

이는 객체 내 키의 순서에 관계없이 다음의 모든 쿼리가 동일하게 취급된다는 것을 의미합니다:

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

하지만 다음 쿼리 키들은 서로 다릅니다. 배열 항목의 순서가 중요합니다!

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

쿼리 함수가 변수에 의존한다면 해당 변수를 쿼리 키에 포함하세요

쿼리 키는 가져오는 데이터를 고유하게 설명하기 때문에, 쿼리 함수에서 사용하는 변수 중 변경되는 모든 변수를 포함해야 합니다. 예를 들어:

typescript
function Todos({ todoId }) {
  const result = useQuery({
    queryKey: ['todos', todoId],
    queryFn: () => fetchTodoById(todoId),
  })
}
 
typescript
function Todos({ todoId }) {
  const result = useQuery({
    queryKey: ['todos', todoId],
    queryFn: () => fetchTodoById(todoId),
  })
}
 

쿼리 키는 쿼리 함수의 의존성 역할을 한다는 점에 주목하세요. 의존하는 변수를 쿼리 키에 추가하면 쿼리가 독립적으로 캐시되고, 변수가 변경될 때마다 쿼리가 자동으로 다시 가져와집니다(staleTime 설정에 따라 다름). 자세한 정보와 예제는 exhaustive-deps 섹션을 참조하세요.

추가 읽을거리

대규모 애플리케이션에서 쿼리 키를 조직화하는 팁은 효과적인 React Query 키를 확인하고 커뮤니티 리소스의 쿼리 키 팩토리 패키지를 살펴보세요.

YouTube 영상

채널 보기
Writer 카테고리 구현 해 보기 | 크라이슬리 카테고리 3편 | 프로그래머를 위한 카테고리 이론
Haskell로 배우는 Writer 모나드 | 클라이슬리 카테고리 마지막편 | 프로그래머를 위한 카테고리 이론
곱, 프로덕트 | 프로그래머를 위한 카테고리 이론
곱타입 - 튜플과 레코드(구조체) | 프로그래머를 위한 카테고리 이론
피처 모듈은 무엇이고 왜 필요할까? | NestJS 가이드
@Global() 데코레이터와 전역 모듈 | NestJS 가이드
NestJS 모듈 시스템 기초와 구조 | NestJS 가이드
모듈을 공유하는 방법은? | NestJS 가이드