React

React : TanStack Query 심화 (Query Cancellation)

고래고래00 2024. 6. 14. 14:39

Query Cancellation

  • 다운로드 UI 가 있을 때, 또는 UX 저해시키는 불필요한 네트워크 요청을 제거하기 위해 사용
  • 대용량 fetching을 중간에 취소하거나 사용하지 않는 컴포넌트에서 fetching이 진행 중이면 자동으로 취소
    => 불필요한 네트워크 비용 감소
  • queryFn 의 매개변수로 Abort Signal 을 받을 수 있고, 이를 이용해서 Query 취소 가능
더보기

동작원리

let controller;
const url = "video.mp4";

const downloadBtn = document.querySelector(".download"); // 다운로드 버튼
const abortBtn = document.querySelector(".abort"); // 취소 버튼 

// 다운로드 버튼 클릭시 fetchVideo 실행
downloadBtn.addEventListener("click", fetchVideo);

// 취소 버튼 클릭시 controller의 abort가 실행되면서 다운로드 취소
abortBtn.addEventListener("click", () => {
  if (controller) {
    controller.abort();
    console.log("Download aborted");
  }
});

// 다운로드 버튼 클릭시 실행
function fetchVideo() {
  controller = new AbortController(); // AbortController로 생성하는 instance
  const signal = controller.signal; // controller의 signal을 변수화
  fetch(url, { signal })	// fetch의 두 번째 인자로 signal => 다운로드 취소할 준비
    .then((response) => {
      console.log("Download complete", response);
    })
    .catch((err) => {
      console.error(`Download error: ${err.message}`);
    });
}

https://developer.mozilla.org/en-US/docs/Web/API/AbortController

 

AbortController - Web APIs | MDN

The AbortController interface represents a controller object that allows you to abort one or more Web requests as and when desired.

developer.mozilla.org

사용 방법

QueryFunctionContext

queryFn 은 매개변수로 QueryFunctionContext 이란 객체를 받음

더보기
// queryFn에 getTodos를 넣을 때, 명시적으로는 아무 인자도 넣지않았지만 
// TanStack-Query에서는 queryFnContext를 이미 인자로 주고 있었음
export const getTodos = async (queryFnContext) => {
  const { queryKey, pageParam, signal, meta } = queryFnContext;
	// queryKey: 배열형태의 쿼리키
	// pageParam: useInfiniteQuery 사용 시 getNextPageParam 실행 시 적용
	// signal: AbortSignal 을 의미 (네트워크 요청을 중간에 중단시킬 수 있는 장치)
	// meta: query에 대한 정보를 추가적으로 메모를 남길 수 있는 string 필드

  const response = await axios.get("http://localhost:5000/todos", { signal });
  return response.data;
};

useQuery({
  queryKey: ["todos"],
  queryFn: getTodos,
})
// example: <div onClick={(event) => {}} 여기처럼 알아서 인자를 넣어주고 있었음

https://tanstack.com/query/latest/docs/framework/react/guides/query-functions

 

Query Functions | TanStack Query React Docs

Does this replace [Redux, MobX, etc]? react

tanstack.com

쿼리를 중간에 취소하는 경우 2가지

  1. 데이터를 가져오는 도중에 컴포넌트가 unmount 될 때
  2. 명시적(수동)으로 취소

페이지 컴포넌트 umount 시 Query 취소

API 요청 시 기본설정은 컴포넌트가 unmount 되도 네트워크 요청은 중단 X

GET 요청 시 abort signal 이 옵션으로 들어간 경우에만 unmount 시 자동으로 네트워크 취소

더보기
import axios from 'axios'

const query = useQuery({
  queryKey: ['todos'],
  queryFn: ({ signal }) => 
    axios.get('/todos', {
      // Pass the signal to `axios`
      signal,
    }),
})
// queryFn 안에서 queryFunctionCntext로부터받은 signal을 axios의 option객체에 signal을 넣어줌
// => 쿼리 취소(abort) 준비 끝 => unmount시 자동으로 취소

 

수동으로 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'] })
    }}
  >
    Cancel
  </button>
)
// queryClient를 사용해 버튼 클릭시(수동) 쿼리 취소

 

주의사항

  • 불필요한 네트워크 요청을 최소화 한다는 명분으로 단순하게
    모든 GET 요청마다 Abort Signal 을 심는 것은 작업부하를 올림
  • 동영상 다운로드 같은 대용량 fetching이 아닌 이상
    대부분의 GET 요청은 빠르게 완료 및 캐싱처리되어 성능에 유의미한 영향 X
  • 대용량 fetching이 있는 경우 또는 Optimistic UI 를 구현할 때처럼 필요한 경우에만 적용