본문 바로가기

FrontEnd

리액트 쿼리 : 쿼리 취소하기

반응형

리액트 쿼리

쿼리 취소에 관하여:

직접 useFetch와 같은 훅을 구현할 경우, unmount된 경우에도 상태 설정 훅을 호출하여 개발 환경에서 오류 메세지를 마주치는 경우가 많죠.

즉, 보통은 요청을 일단 보내면 이후에 필요 없어져도 요청은 그대로 두고 응답받은 내용을 사용 안 하는 식으로만 구현합니다.

React-Query와 같은 라이브러리를 사용하여도 해당 전략은 마찬가지입니다.

...기본적으로 프로미스가 리졸브되기 전에 마운트 해제되거나 사용되지 않는 쿼리는 취소되지 않습니다.
즉, 프로미스가 리졸브된 후 결과 데이터를 캐시에서 사용할 수 있음을 의미합니다.
이 동작은 쿼리 수신을 시작했지만 완료되기 전에 컴포넌트를 마운트 해제한 경우 유용합니다.
컴포넌트를 다시 마운트하였을 때, 쿼리가 아직 가비지 수집되지 않은 경우 해당 데이터를 사용할 수 있습니다.

간단한 HTTP 요청은 응답이 꽤 빠르기 때문에 괜찮을 수도 있지만,

무거운 요청(프로미스)의 경우는 불필요한 네트워크 트래픽을 낭비하게 되거나, 커넥션을 차지하고 있으므로 취소하는 것이 좋습니다.

React Query를 사용하면 아주 쉽게 요청(프로미스) 취소를 위임할 수 있는 것, 알고 계셨나요?


자동으로 쿼리 취소하기

공식문서에는 다음과 같이 적혀 있습니다.

React Query는 런타임 환경에서 사용 가능한 경우, AbortSignal instance와 함께 각 쿼리 함수를 제공합니다.
(폴리필 가능)
쿼리 실행 도중 쿼리가 비활성화(컴포넌트에서 사용하지 않음) 되었거나, 새로운 결과가 필요하면 해당 쿼리를 중단합니다.

이는 다음과 같은 행위를 의미합니다.

  •  컴포넌트가 마운트 해제될 때, React Query가 진행 중인 쿼리 실행 / 요청을 취소할 수 있도록 합니다.
  • 혹은 사용자가 버튼을 클릭하여 요청을 취소할 수 있도록 합니다.
이는 리액트 쿼리 자체의 로직과 프로미스를 취소할 수 있으며, 원하는 경우 쿼리 함수 내에서 취소 신호에 응답할 수 있음을 의미합니다.
 
이것의 가장 좋은 점은 자동 취소의 모든 이점을 얻으면서 일반 async/await 구문을 계속 사용할 수 있다는 것입니다.
또한 이 솔루션은 TypeScript와 매우 잘 동작합니다.
 
AbortSignal을 사용하거나 Promise에 cancel 메서드를 추가하면, 쿼리 취소와 함께 Promise(예 : 가저오기)도 중단됩디다.
쿼리를 취소하면 이전 상태로 되돌아갑니다.
주 : Promise의 cancel 메서드는 Bluebird 사용 시
참고 : https://benlesh.medium.com/promise-cancellation-is-dead-long-live-promise-cancellation-c6601f1f5082

예시 : fetch 사용

const query = useQuery({
  queryKey: ['todos'],
  queryFn: async ({ signal }) => {
    const todosResponse = await fetch('/todos', {
      // Pass the signal to one fetch
      signal,
    })
    const todos = await todosResponse.json()

    const todoDetails = todos.map(async ({ details }) => {
      const response = await fetch(details, {
        // Or pass it to several
        signal,
      })
      return response.json()
    })

    return Promise.all(todoDetails)
  }
})

예시 : axios 버전 0.22.0+ 이상 사용 시

import axios from 'axios'

const query = useQuery({
  queryKey: ['todos'],
  queryFn: ({ signal }) =>
    axios.get('/todos', {
      // Pass the signal to `axios`
      signal,
    }),
})

위와 같이 사용하기 정말 쉽습니다.

이 외에도 여러 API에 대한 예제가 있으니 공식 문서를 확인하시기 바랍니다.


수동으로 쿼리 취소하기

쿼리를 수동으로 취소할 수 있습니다.
예를 들어 요청을 완료하는 데 시간이 오래 걸리는 경우 사용자가 직접 취소 버튼을 클릭하여 요청을 중지하도록 할 수 있습니다.
쿼리를 취소하고 이전 상태로 되돌리는 queryClient.cancelQueries({ queryKey })를 호출하기만 하면 됩니다.
(주 : 이 경우에도 signal을 이용한 구현이 되어있지 않으면, fetch 이후 로직만 취소합니다. promise를 취소하는게 아니에요)
promise.cancel을 사용할 수 있거나 쿼리 함수의 인자로 전달된 신호를 사용한다면, React Query는 추가로 Promise도 취소합니다.
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'] })
   }}>Cancel</button>
)
 
주 : 리액트 쿼리의 쿼리를 중단하는 것과, promise 취소는 다른 것이니 주의할것!
promise가 resolve된 이후 로직의 실행을 중단하는것임
 

참고

https://blog.outsider.ne.kr/1602

 

AbortController로 요청 취소하기 :: Outsider's Dev Story

[AbortController](https://developer.mozilla.org/ko/docs/Web/API/AbortController)는 웹 요청을 취소할 수 있게 해주는 기능이다. 보통 웹에서 요청을 일단 보내면 이후에 필요 없어져도 취소할 방법이 없어서...

blog.outsider.ne.kr

https://benlesh.medium.com/promise-cancellation-is-dead-long-live-promise-cancellation-c6601f1f5082

 

Promise Cancellation Is Dead — Long Live Promise Cancellation!

Not very long ago Domenic Denicola (worth a follow, a very smart guy) withdrew his proposal for promise cancellation from the TC39. A lot…

benlesh.medium.com

https://medium.com/@icjoseph/using-react-to-understand-abort-controllers-eb10654485df

 

Using React to understand Abort Controllers

In case you didn’t know, browsers support an API called AbortController, which is typically used to cancel ongoing fetch requests. More…

medium.com

 

반응형