🔥 쿼리 취소
강의 목차
TanStack Query는 각 쿼리 함수에 AbortSignal 인스턴스를 제공합니다. 쿼리가 오래되거나 비활성화되면 이 신호가 중단됩니다. 이는 모든 쿼리를 취소할 수 있으며, 원한다면 쿼리 함수 내에서 취소에 대응할 수 있음을 의미합니다. 이 방식의 가장 큰 장점은 일반적인 async/await 문법을 그대로 사용하면서도 자동 취소의 모든 이점을 누릴 수 있다는 점입니다.
AbortController API는 대부분의 실행 환경에서 사용할 수 있습니다. 하지만 실행 환경이 이를 지원하지 않는다면 폴리필을 제공해야 합니다. 여러 가지 폴리필을 사용할 수 있습니다.
기본 동작
기본적으로 프로미스가 해결되기 전에 언마운트되거나 사용되지 않게 된 쿼리는 취소되지 않습니다. 이는 프로미스가 해결된 후에도 결과 데이터를 캐시에서 사용할 수 있음을 의미합니다. 쿼리 수신을 시작했지만 완료되기 전에 컴포넌트를 언마운트한 경우에 유용합니다. 컴포넌트를 다시 마운트하고 쿼리가 아직 가비지 컬렉션되지 않았다면 데이터를 사용할 수 있습니다.
그러나 AbortSignal을 사용하면 프로미스가 취소됩니다(예: fetch 중단). 따라서 쿼리도 취소해야 합니다. 쿼리를 취소하면 상태가 이전 상태로 되돌아갑니다.
fetch 사용하기
const query = useQuery({
queryKey: ['todos'],
queryFn: async ({ signal }) => {
const todosResponse = await fetch('/todos', {
// 하나의 fetch에 신호 전달
signal,
})
const todos = await todosResponse.json()
const todoDetails = todos.map(async ({ details }) => {
const response = await fetch(details, {
// 또는 여러 개에 전달
signal,
})
return response.json()
})
return Promise.all(todoDetails)
},
})
const query = useQuery({
queryKey: ['todos'],
queryFn: async ({ signal }) => {
const todosResponse = await fetch('/todos', {
// 하나의 fetch에 신호 전달
signal,
})
const todos = await todosResponse.json()
const todoDetails = todos.map(async ({ details }) => {
const response = await fetch(details, {
// 또는 여러 개에 전달
signal,
})
return response.json()
})
return Promise.all(todoDetails)
},
})
axios 사용하기 (v0.22.0 이상)
import axios from 'axios'
const query = useQuery({
queryKey: ['todos'],
queryFn: ({ signal }) =>
axios.get('/todos', {
// `axios`에 신호 전달
signal,
}),
})
import axios from 'axios'
const query = useQuery({
queryKey: ['todos'],
queryFn: ({ signal }) =>
axios.get('/todos', {
// `axios`에 신호 전달
signal,
}),
})
v0.22.0 미만의 axios 사용하기
import axios from 'axios'
const query = useQuery({
queryKey: ['todos'],
queryFn: ({ signal }) => {
// 이 요청에 대한 새로운 CancelToken 소스 생성
const CancelToken = axios.CancelToken
const source = CancelToken.source()
const promise = axios.get('/todos', {
// 소스 토큰을 요청에 전달
cancelToken: source.token,
})
// TanStack Query가 중단 신호를 보내면 요청 취소
signal?.addEventListener('abort', () => {
source.cancel('TanStack Query에 의해 쿼리가 취소되었습니다')
})
return promise
},
})
import axios from 'axios'
const query = useQuery({
queryKey: ['todos'],
queryFn: ({ signal }) => {
// 이 요청에 대한 새로운 CancelToken 소스 생성
const CancelToken = axios.CancelToken
const source = CancelToken.source()
const promise = axios.get('/todos', {
// 소스 토큰을 요청에 전달
cancelToken: source.token,
})
// TanStack Query가 중단 신호를 보내면 요청 취소
signal?.addEventListener('abort', () => {
source.cancel('TanStack Query에 의해 쿼리가 취소되었습니다')
})
return promise
},
})
XMLHttpRequest 사용하기
const query = useQuery({
queryKey: ['todos'],
queryFn: ({ signal }) => {
return new Promise((resolve, reject) => {
var oReq = new XMLHttpRequest()
oReq.addEventListener('load', () => {
resolve(JSON.parse(oReq.responseText))
})
signal?.addEventListener('abort', () => {
oReq.abort()
reject()
})
oReq.open('GET', '/todos')
oReq.send()
})
},
})
const query = useQuery({
queryKey: ['todos'],
queryFn: ({ signal }) => {
return new Promise((resolve, reject) => {
var oReq = new XMLHttpRequest()
oReq.addEventListener('load', () => {
resolve(JSON.parse(oReq.responseText))
})
signal?.addEventListener('abort', () => {
oReq.abort()
reject()
})
oReq.open('GET', '/todos')
oReq.send()
})
},
})
graphql-request 사용하기
클라이언트 요청 메서드에서 AbortSignal을 설정할 수 있습니다.
const client = new GraphQLClient(endpoint)
const query = useQuery({
queryKey: ['todos'],
queryFn: ({ signal }) => {
client.request({ document: query, signal })
},
})
const client = new GraphQLClient(endpoint)
const query = useQuery({
queryKey: ['todos'],
queryFn: ({ signal }) => {
client.request({ document: query, signal })
},
})
v4.0.0 미만의 graphql-request 사용하기
GraphQLClient 생성자에서 AbortSignal을 설정할 수 있습니다.
const query = useQuery({
queryKey: ['todos'],
queryFn: ({ signal }) => {
const client = new GraphQLClient(endpoint, {
signal,
})
return client.request(query, variables)
},
})
const query = useQuery({
queryKey: ['todos'],
queryFn: ({ signal }) => {
const client = new GraphQLClient(endpoint, {
signal,
})
return client.request(query, variables)
},
})
수동 취소
때로는 쿼리를 수동으로 취소하고 싶을 수 있습니다. 예를 들어, 요청이 완료되는 데 시간이 오래 걸리는 경우 사용자가 취소 버튼을 클릭하여 요청을 중지할 수 있게 할 수 있습니다. 이를 위해 queryClient.cancelQueries({ queryKey })를 호출하면 됩니다. 이 호출은 쿼리를 취소하고 이전 상태로 되돌립니다. 쿼리 함수에 전달된 신호를 사용했다면 TanStack Query는 추가로 프로미스도 취소합니다.
const query = useQuery({
queryKey: ['todos'],
queryFn: async ({ signal }) => {
const resp = await fetch('/todos', { signal })
return resp.json()
},
})
const queryClient = useQueryClient()
return (
<button
onClick={(e) => {
e.preventDefault()
queryClient.cancelQueries({ queryKey: ['todos'] })
}}
>
취소
</button>
)
const query = useQuery({
queryKey: ['todos'],
queryFn: async ({ signal }) => {
const resp = await fetch('/todos', { signal })
return resp.json()
},
})
const queryClient = useQueryClient()
return (
<button
onClick={(e) => {
e.preventDefault()
queryClient.cancelQueries({ queryKey: ['todos'] })
}}
>
취소
</button>
)









