TIL_2024_02_17

이종현·2024년 2월 18일
0

Today_I_Learned

목록 보기
139/145
post-thumbnail

Today 요약

  1. 리액트 쿼리 강의

1. What I Learned?

리액트 쿼리 강의

useQuery의 select 옵션

select 옵션은 가져온 데이터를 변환하거나 특정 부분만을 선택해서 반환할 때 사용한다. 이때 select 옵션에 전달하고 싶은 함수를 명시한다.

import { useQuery } from 'react-query';

const fetchUsers = async () => {
  const response = await fetch('/api/users');
  if (!response.ok) {
    throw new Error('Network response was not ok');
  }
  return response.json();
};

const UsersComponent = () => {
  const { isLoading, error, data } = useQuery('users', fetchUsers, {
    select: data => data.map(user => user.name),
  });

  if (isLoading) return 'Loading...';
  if (error) return 'An error has occurred: ' + error.message;

  return (
    <ul>
      {data.map(name => (
        <li key={name}>{name}</li>
      ))}
    </ul>
  );
};

그럼 useQuery의 data 객체에 필터링된 데이터가 반영된다.

강의 예시도 살펴보자.

select 함수를 useCallback으로 감싸서 좀 더 안정적으로 동작하도록 한다.

const selectFn = useCallback(
  (data: AppointmentDateMap, showAll: boolean) => {
    if (showAll) return data

    return getAvailableAppointments(data, userId)
  },
  [userId]
)

getAvailableAppointments 라는 예약된 사용자만 보여주는 필터링 함수를 구현한다.

export function getAvailableAppointments(
  appointments: AppointmentDateMap,
  userId: number | null
): AppointmentDateMap {
  const filteredAppointments = { ...appointments }

  Object.keys(filteredAppointments).forEach((date) => {
    const dateNum = Number(date)
    filteredAppointments[dateNum] = filteredAppointments[dateNum].filter(
      (appointment: Appointment) =>
        (!appointment.userId || appointment.userId === userId) &&
        !appointmentInPast(appointment)
    )
  })

  return filteredAppointments
}

useQuery에 적절하게 select 옵션을 정의한다.

const { data: appointments = fallback } = useQuery({
  queryKey: [queryKeys.appointments, monthYear.year, monthYear.month],
  queryFn: () => getAppointments(monthYear.year, monthYear.month),
  select: (data) => selectFn(data, showAll)
})

refetch

강의에서는 스파 예약을 관리하고 직원들과 어떤 서비스가 있는지를 보여주는 앱을 활용하기 때문에 데이터의 활용 목적에 따라 데이터를 다시 서버에 refetch 요청을 보내는 옵션을 어떻게 설정하는가가 중요하다.

스파 앱에서는 직원들과 서비스는 자주 바뀌는 데이터가 아니다. 따라서 staleTime과 gcTime을 늘리고 refetch 되는 옵션도 최대한 false로 설정하는 것이 좋다.

하지만 예약화면의 경우, 여러명이 동시에 접속해서 예약을 하는 경우도 생길 수 있기 때문에 이때는 주기적으로 refetch 될 수 있도록 하는 것이 좋다.

그렇다면 생각을 해봐야 한다. 지금 현재 앱이 refetch가 반복적으로 이루어져야 하는 서비스가 많은지 아니면 그 반대가 많은지 생각해봐야 한다.

스파앱의 경우, 예약의 경우만 refetch를 최대한 활용할 것이기 때문에 전역에 공통적으로 적용할 다른 옵션들을 queryClient에 적용하고 예약 부분 관련된 useQuery에 오버라이딩 해서 정의하는 게 효율적일 것이다.

export const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 60000,
      gcTime: 90000,
      refetchOnWindowFocus: false
    }
  },
  queryCache: new QueryCache({
    onError: (error) => errorHandler(error.message)
  })
})
useEffect(() => {
  const nextMonthYear = getNewMonthYear(monthYear, 1)

  queryClient.prefetchQuery({
    queryKey: [
      queryKeys.appointments,
      nextMonthYear.year,
      nextMonthYear.month
    ],
    queryFn: () => getAppointments(nextMonthYear.year, nextMonthYear.month),
    ...commonOptions
  })
}, [monthYear, queryClient])

const fallback: AppointmentDateMap = {}

const { data: appointments = fallback } = useQuery({
  queryKey: [queryKeys.appointments, monthYear.year, monthYear.month],
  queryFn: () => getAppointments(monthYear.year, monthYear.month),
  select: selectFn,
  refetchOnWindowFocus: true,
	// 정기적으로 예약 관련된 데이터를 받아오기 위해 refetchInterval을 10초로 설정.. 사실 강의에서는 1분으로 설정
  refetchInterval: 10000,
  ...commonOptions
})

그리고 commonOptions 처럼 반복되는 것들을 정의해서 사용할 수 있다.

const commonOptions = {
  staleTime: 0,
  gcTime: 30000
}
profile
데이터리터러시를 중요하게 생각하는 프론트엔드 개발자

0개의 댓글