6/16 TIL (react-query 심화)

Hwi·2024년 6월 16일

TIL

목록 보기
49/96

📖 진행한 공부 📖

  • react-query 심화 이론

01. Query Cancellation

1. 무엇이며, 언제 사용하나?

  • 다운로드 UI가 있을 때, 또는 UX를 저해시키는 불필요한 네트워크 요청을 제거하기 위해 사용된다.

  • 대용량 fetching을 중간에 취소하거나 사용하지 않는 컴포넌트에서 fetching이 진행 중이면 자동으로 취소시켜 불필요한 네트워크 비용을 줄일 수 있다.

  • queryFn의 매개변수로 Abort Singnal을 받을 수 있고, 이를 이용해서 Query 취소를 가능하게 한다.

2. 사용방법
(2)-1. QueryFunctionContext

  • queryFn 은 매개변수로 QueryFunctionContext이란 객체를 받음
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) => {}}

(2)-2. 페이지 컴포넌트 unmount 시 Query 취소

  • API 요청 시 기본 설정은 컴포넌트가 unmount 되어도 네트워크 요청은 중단되지 않는다.
  • GET 요청 시 abort signal 이 옵션으로 들어간 경우에만 unmount 시 자동으로 네트워크 취소가 된다.
import axios from 'axios'

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

(2)-3. 수동으로 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>
)

3. 사용 시 주의사항
모든 GET 요청마다 Abort Signal 을 심으면 좋을까?

불필요한 네트워크 요청을 최소화한다는 명분으로 단순하게 모든 GET 요청마다 Abort Signal 을 심는 것은 작업부하를 올리기에 바람직하지 않음

동영상 다운로드 같은 대용량 fetching이 아닌 이상 대부분의 GET 요청은 빠르게 완료 및 캐싱처리되어 성능에 유의미한 영향을 끼치지 못함

대용량 fetching이 있는 경우 또는 Optimistic UI를 구현할 때 처럼 필요한 경우에만 적용하는 것을 권장함

02. Optimistic Updates

서버 요청이 정상임을 가정하고 더 나은 UX를 제공할 수 있는 방법인 낙관적 업데이트

서버 요청이 정상적으로 잘 될 거란 가정 하에 UI 변경을 먼저 하고, 서버 요청하는 방식
혹시라도 서버 요청이 실패하는 경우, UI를 원상복구(revert / roll back)한다.

03. Prefetching

특정 데이터를 백그라운드에서 가져올 수 있는 기능

  • 페이지 이동 전에 이동할 페이지의 쿼리를 백그라운드에서 미리 호출(prefetching) 한다.
  • 캐시 데이터가 있는 상태로 해당 페이지로 이동 시 로딩없이 바로 UI를 볼 수 있음

04. Paginated / Lagged Queries

다른 페이지 클릭 시 매번 Loading UI를 보여주기보단 기존 UI를 유지하다가 서버로부터 새로운 데이터를 받아왔을 때 바꾸는 방식을 적용할 수 있음

useQuery의 옵션 중 keepPreviousData를 true로 바꾸면 이전 캐시데이터를 기반으로 isLoading 여부를 판단하게 함

05. Infinite Queries

(1) Infinite Queries

Data Fetching 이 일어날 때 마다 기존 리스트 데이터에 Fetched Data를 추가하고자 할 때 유용하게 사용할 수 있는 Hook

더보기 UI 또는 무한 스크롤 UI 에 사용하기에 적합

const fetchProjects = async ({ pageParam = 0 }) => {
    const res = await fetch('/api/projects?cursor=' + pageParam)
    return res.json()
  }

  const {
    data,
    error,
    fetchNextPage,
    hasNextPage,
    isFetching,
    isFetchingNextPage,
    status,
  } = useInfiniteQuery({
    queryKey: ['projects'],
    queryFn: fetchProjects,
    getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
  })

실행 순서 정리

queryFn 실행 ➡️ 캐시 데이터 등록 { pages, pageParam } ➡️ getNextPageParam 실행 (리턴된 NextPageParam은 훅 내부 메모리에 저장함 캐시 저장X) ➡️ (NextPageParam 이 undefined가 아니면) hasNextPage true로 상태변경 ➡️ fetchNextPage 실행 ➡️ queryFn 실행 (이때 내부적으로 저장돼 있던 NextPageParam을 queryFn의 매개변수로 넘겨줌)

pages 와 pageParams 를 갖는 캐시 데이터

  • useQuery 에서는 QueryFn 의 반환값이 캐시데이터로 등록
  • useInfiniteQuery 에서는 QueryFn 의 반환값은 pages 배열의 요소로 추가되고, 매개변수로 받았던 pageParam 은 PageParams 배열의 요소로 추가

useinfiniteQuery 사용 시 주의사항

  • 훅 내부적인 동작 원리로 인해 예상보다 잦은 리렌더링이 발생할 수 있음
  • 연산량이 많은 코드가 있는 경우 useMemo 와 같은 memoization 적용을 특히 고려해야 함
  • 리렌더링이 발생한다고 해서 실제 브라우저 렌더링이 발생하는 것은 아님 (가상 DOM 원리 이해 필요)
profile
개발자가 되고 싶어~~~

0개의 댓글