🔥 타입스크립트

886자
10분

React Query는 이제 타입스크립트로 작성되어 라이브러리와 프로젝트의 타입 안전성을 보장합니다.

주의해야 할 점들:

  • 현재 타입스크립트 v4.7 이상 버전을 사용해야 합니다.
  • 이 저장소의 타입 변경은 일반적으로 중요하지 않은 것으로 간주되며, 보통 패치 수준의 버전 변경으로 릴리스됩니다. 그렇지 않으면 모든 타입 개선이 주요 버전 변경이 되어버릴 것입니다.
  • react-query 패키지 버전을 특정 패치 릴리스로 고정하고, 타입이 수정되거나 업그레이드될 수 있다는 점을 인지하고 업그레이드하는 것이 매우 권장됩니다.
  • React Query의 타입과 관련 없는 공개 API는 여전히 엄격한 의미 체계를 따릅니다.

타입 추론

React Query의 타입은 대체로 잘 흐르기 때문에 직접 타입 주석을 제공할 필요가 없습니다.

const { data } = useQuery({
  //    ^? const data: number | undefined
  queryKey: ['test'],
  queryFn: () => Promise.resolve(5),
})
 
typescript

타입스크립트 플레이그라운드

const { data } = useQuery({
  //      ^? const data: string | undefined
  queryKey: ['test'],
  queryFn: () => Promise.resolve(5),
  select: (data) => data.toString(),
})
 
typescript

타입스크립트 플레이그라운드

이는 queryFn이 명확하게 정의된 반환 타입을 가질 때 가장 잘 작동합니다. 대부분의 데이터 가져오기 라이브러리는 기본적으로 any를 반환한다는 점을 유념해야 합니다. 따라서 적절한 타입을 가진 함수로 추출하는 것이 중요합니다:

const fetchGroups = (): Promise<Group[]> =>
  axios.get('/groups').then((response) => response.data)
 
const { data } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups })
//      ^? const data: Group[] | undefined
 
typescript

타입스크립트 플레이그라운드

타입 좁히기

React Query는 쿼리 결과에 대해 상태 필드와 파생된 상태 불리언 플래그로 구분되는 구별된 유니온 타입을 사용합니다. 이를 통해 예를 들어 성공 상태를 확인하여 데이터를 정의할 수 있습니다:

const { data, isSuccess } = useQuery({
  queryKey: ['test'],
  queryFn: () => Promise.resolve(5),
})
 
if (isSuccess) {
  data
  //  ^? const data: number
}
 
typescript

타입스크립트 플레이그라운드

error 필드의 타입 지정

error의 타입은 기본적으로 Error로 설정됩니다. 대부분의 사용자가 예상하는 바이기 때문입니다.

const { error } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups })
//      ^? const error: Error
 
typescript

타입스크립트 플레이그라운드

사용자 정의 오류를 던지거나 Error가 아닌 것을 던지려면 error 필드의 타입을 지정할 수 있습니다:

const { error } = useQuery<Group[], string>(['groups'], fetchGroups)
//      ^? const error: string | null
 
typescript

하지만 이 방법은 useQuery의 다른 모든 제네릭에 대한 타입 추론이 작동하지 않게 되는 단점이 있습니다. Error가 아닌 것을 던지는 것은 일반적으로 좋은 관행이 아니므로, AxiosError와 같은 하위 클래스가 있다면 _타입 좁히기_를 사용하여 error 필드를 더 구체적으로 만들 수 있습니다:

import axios from 'axios'
 
const { error } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups })
//      ^? const error: Error | null
 
if (axios.isAxiosError(error)) {
  error
  // ^? const error: AxiosError
}
 
typescript

타입스크립트 플레이그라운드

전역 Error 등록하기

TanStack Query v5는 Register 인터페이스를 수정하여 호출 측에서 제네릭을 지정하지 않고도 모든 것에 대한 전역 Error 타입을 설정할 수 있는 방법을 제공합니다. 이렇게 하면 추론은 여전히 작동하지만 error 필드는 지정된 타입이 됩니다:

import '@tanstack/react-query'
 
declare module '@tanstack/react-query' {
  interface Register {
    defaultError: AxiosError
  }
}
 
const { error } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups })
//      ^? const error: AxiosError | null
 
typescript

meta 타입 지정하기

전역 Meta 등록하기

전역 error 타입 등록하기와 유사하게 전역 Meta 타입도 등록할 수 있습니다. 이를 통해 쿼리뮤테이션의 선택적 meta 필드가 일관되고 타입 안전하게 유지됩니다. 등록된 타입은 Record<string, unknown>을 확장해야 meta가 객체로 유지된다는 점에 주의해야 합니다.

import '@tanstack/react-query'

interface MyMeta extends Record<string, unknown> {
  // 여기에 meta 타입을 정의합니다.
}

declare module '@tanstack/react-query' {
  interface Register {
    queryMeta: MyMeta
    mutationMeta: MyMeta
  }
}
text

쿼리 옵션의 타입 지정하기

useQuery에 쿼리 옵션을 인라인으로 작성하면 자동 타입 추론을 얻을 수 있습니다. 하지만 쿼리 옵션을 별도의 함수로 추출하여 useQueryprefetchQuery 등에서 공유하고 싶을 수 있습니다. 이 경우 타입 추론을 잃게 됩니다. 이를 해결하기 위해 queryOptions 헬퍼를 사용할 수 있습니다:

import { queryOptions } from '@tanstack/react-query'

function groupOptions() {
  return queryOptions({
    queryKey: ['groups'],
    queryFn: fetchGroups,
    staleTime: 5 * 1000,
  })
}

useQuery(groupOptions())
queryClient.prefetchQuery(groupOptions())
text

더 나아가, queryOptions에서 반환된 queryKey는 관련된 queryFn에 대해 알고 있으며, 이 타입 정보를 활용하여 queryClient.getQueryData와 같은 함수도 이러한 타입을 인식하게 할 수 있습니다:

function groupOptions() {
  return queryOptions({
    queryKey: ['groups'],
    queryFn: fetchGroups,
    staleTime: 5 * 1000,
  })
}

const data = queryClient.getQueryData(groupOptions().queryKey)
//     ^? const data: Group[] | undefined
text

queryOptions를 사용하지 않으면 data의 타입은 제네릭을 전달하지 않는 한 unknown이 됩니다:

const data = queryClient.getQueryData<Group[]>(['groups'])
text

추가 읽을거리

타입 추론에 대한 팁과 요령은 커뮤니티 리소스의 React Query와 TypeScript를 참조하세요. 가능한 최고의 타입 안전성을 얻는 방법을 알아보려면 타입 안전한 React Query를 읽어보세요.

skipToken을 사용한 타입 안전한 쿼리 비활성화

타입스크립트를 사용한다면 skipToken을 이용해 쿼리를 비활성화할 수 있습니다. 이는 조건에 따라 쿼리를 비활성화하면서도 쿼리의 타입 안전성을 유지하고 싶을 때 유용합니다. 자세한 내용은 쿼리 비활성화 가이드에서 확인할 수 있습니다.

이 문서는 React Query에서 타입스크립트를 사용할 때 알아야 할 핵심 개념들을 다루고 있습니다. 타입 추론, 타입 좁히기, 오류 타입 지정, 메타 데이터 타입 지정, 쿼리 옵션 타입 지정 등의 주제를 자세히 설명하고 있어 React Query를 타입 안전하게 사용하는 데 큰 도움이 될 것입니다.

특히 주목할 만한 점은 다음과 같습니다:

  1. React Query의 타입 시스템이 대부분의 경우 자동으로 잘 작동하므로 별도의 타입 주석이 필요 없다는 점
  2. 구별된 유니온 타입을 사용해 쿼리 상태에 따라 타입을 좁힐 수 있다는 점
  3. 전역 오류 타입과 메타 타입을 등록할 수 있어 일관된 타입 안전성을 유지할 수 있다는 점
  4. queryOptions 헬퍼 함수를 통해 재사용 가능한 쿼리 옵션의 타입 추론을 유지할 수 있다는 점

이러한 기능들을 잘 활용하면 React Query를 사용하는 프로젝트에서 타입 안전성을 크게 높일 수 있을 것입니다. 또한 문서에서 제공하는 추가 읽을거리를 통해 더 깊이 있는 이해를 얻을 수 있으니, 필요에 따라 참조하는 것이 좋겠습니다.