🔥 쿼리 키

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 영상

채널 보기
펑터 합성 | 프로그래머를 위한 카테고리 이론
NestJS 가드, 바이딩과 스코프 | NestJS 가이드
NestJS 파이프가 뭔가요? 컨트롤러를 보호하는 방법 | NestJS 가이드
미들웨어 vs 가드, 왜 NestJS에서는 가드가 더 똑똑할까? | NestJS 가이드
Git Worktree로 여러 피처 동시에 개발하기 | AI 코딩 시대의 필수 스킬
Pro펑터, 입력과 출력을 동시에 다루는 펑터 | 프로그래머를 위한 카테고리 이론
매번 ValidationPipe 복붙하세요? NestJS 전역 파이프로 한 번에 해결하기 | NestJS 가이드
변환 파이프로 컨트롤러 코드 깔끔하게 만들기 | NestJS 가이드