TanStack-query

Jtiiin:K·2023년 12월 25일
0
post-thumbnail

< TanStack-query란? >

  • 서버 상태관리를 쉽게 하도록 도와주는 라이브러리
    (서버 상태(server state)란 서버에 요청하고 응답받는 모든 과정과 연관된 데이터들을 의미)
  • 서버 상태관리를 쉽게한다는 것은?
  1. fetching : 서버에서 데이터 받아오기
  2. caching : 서버에서 받아온 데이터를 따로 보관해서 동일한 데이터가 단 시간 내에 다시 필요할 시 서버요청없이 보관된 데이터에서 꺼내쓰기
  3. synchronizing : 서버상의 데이터와 보관 중인 캐시 데이터(서버상태)를 동일하게 만들기 (동기화)
  4. updating : 서버 데이터 변경 용이 (mutation & invalidateQueries)
// React Query 미사용 시
const [todos, setTodos] = useState([]);
const [isLoading, setIsLoading] = useState(false);

const getTodos = async () => {
  setIsLoading(true);
	const data = await axios.get(`${API_URL}/todos`).then(res => res.data);
	setTodos(data);
  setIsLoading(false);
}
useEffect(() => {
	getTodos();
}, []);


// React Query 사용 시
const getTodos = () => axios.get(`${API_URL}/todos`).then(res => res.data);

const { data: todos, isLoading } = useQuery(["todos"], getTodos);

✅ stale-while-revalidate (swr) 전략

  • 신규 데이터가 도착하는 동안 일단 기존 캐싱된 데이터를 사용하도록 하는 전략
  • 서버의 헤더응답 설정 Cache-Control 에서 아이디어 기원

    ⬆️ 클라이언트가 0~1초 사이에 다시 데이터를 요청하면, 서버 호출없이 캐시 데이터를 바로 사용

    ⬆️ 클라이언트가 1 ~ 60s 사이에 다시 데이터 요청하면, 일단 캐시 데이터를 사용하고 서버에서 신규데이터를 주면 그것으로 교체

✅ 캐시 데이터는 어디에 보관하는가?

  • QueryClientProvider 는 React Context API를 내부적으로 사용
  • QueryClientProvider 의 자식으로 있는 모든 컴포넌트들은 캐시 데이터에 접근할 수 있음
  • 페이지 컴포넌트 외부에 상태가 존재한다는 점에서 캐시 데이터는 전역 상태로 볼 수 있음
// App.jsx
const queryClient = new QueryClinet();

const App = () => {
	return (
		<QueryClientProvider client={queryClient}>
			<Router />
		</QueryClientProvider>
	);
}

✅ 리액트 쿼리의 데이터 흐름

  • 헌거 먼저, 리렌더링되면서 새거로 교체


< React Query vs. Tanstack Query >

✅ Tanstack query 란?

  • React Query의 v4 부터 라이브러리명이 Tanstack Query로 변경되었음
    (리액트 뿐만 아니라 Vue 등 다른 SPA 프레임워크에도 적용할 계획)
  • 주의 : v4 부터는 query key를 반드시 배열형태로 써줘야 함
useQuery("todos", getTodos); ❌ 에러 발생

useQuery(["todos"], getTodos); ✅ 정상 동작


< React Query 의 Lifecycle >

  • fresh 하면 👉 새 것이 필요없음!
  • stale 하면 👉 새 것이 필요함!

✅ default config (기본설정)

기본 설정의미
staleTime: 0useQuery 또는 useInfiniteQuery에 등록된 queryFn 을 통해 fetch 받아온 데이터는 항상 stale data 취급
refetchOnMount: trueuseQuery 또는 useInfiniteQuery 가 있는 컴포넌트가 마운트 시 stale data 를 refetch 자동 실행
refetchOnWindowFocus: true실행중인 브라우저 화면을 focus 할 때 마다 stale data를 refetch 자동 실행
refetchOnReconnect: trueNetwork 가 끊겼다가 재연결 되었을 때 stale data를 refetch 자동 실행

| cacheTime: 5분
(1000 60 5 ms) | useQuery 또는 useInfiniteQuery가 있는 컴포넌트가 언마운트 되었을 때 inactive query라 부르며, inactive 상태가 5분 경과 후 GC(가비지콜렉터)에 의해 cache data 삭제 처리 |
| retry: 3 | useQuery 또는 useInfiniteQuery에 등록된 queryFn 이 API 서버에 요청을 보내서 실패하더라도 바로 에러를 띄우지 않고 총 3번까지 재요청을 자동으로 시도 |

✅ isLoading vs. isFetching

isLoading

  • 새로운 캐시 데이터를 서버에서 받고 있는 지 여부
  • 캐시 데이터가 있는 경우 isLoading은 false, isFetching은 true

isFetching

  • 서버에서 데이터를 받고 있는 지 여부

🤔 메인페이지와 상세페이지가 모두 useQuery(”todos”, getTodos) 를 가질 때, 메인페이지 → 상세페이지 → 메인페이지 순으로 이동할 때 메인페이지 재 마운트 시의 isLoading, isFetching 의 콘솔로그값은?

// MainPage.jsx
function MainPage() {
	const { isLoading, isFetching } = useQuery("todos", getTodos);
	console.log("isLoading: ",isLoading); // true, false
	console.log("isFetching: ", isFetching); // true, true
}


< useQuery 실행 시 cacheTime > 0 인 것과 queryFn 실행간의 관계? >

  • cacheTime > 0 인 것과 queryFn 실행은 상관 없음
    (staleTime 과 관계)
  • cacheTime > 0 이면, 캐시 데이터가 존재하고, 이 경우 useQuery 를 실행할 때 stale data을 우선 받고 staleTime이 0이면 queryFn을 실행한 리턴값으로 리렌더링하면서 바꿔줌
  • cacheTime 이 0이 되면 캐시 데이터가 삭제되기 때문에, 이 경우 useQuery 로 data 호출 시 undefined 값을 우선 받고, queryFn을 실행한 리턴값으로 리렌더링하면서 바꿔줌


< useQuery에서 자주 사용하는 must-know 옵션들 >

✅ enabled

useQuery(["todos"], getTodos, { enabled: true })
  • boolean type: true or false
  • true인 경우에만 queryFn 실행
  • enabled 옵션이 없을 경우, 기본값으로 true 로 생각하고 useQuery 자동실행

적용 예제(1): Disabling/Pausing Queries (이벤트 발생 시에만 수동 실행하고 싶을 때)

const { data, refetch } = useQuery(["todos"], getTodos, {
  enabled: false
});

return (
	<div>
    <button onClick={() => refetch()}>데이터 불러오기</button>
  </div>
);

적용 예제(2) : Dependent Queries (useQuery 2개 이상이며 실행순서 설정 필요할 때)

// Dependent Query 예제 (순차적 query 실행)
// Get the user
const { data: user } = useQuery({
  queryKey: ['user', email],
  queryFn: getUserByEmail,
})

const userId = user?.id

// Then get the user's projects
const {
  status,
  fetchStatus,
  data: projects,
} = useQuery({
  queryKey: ['projects', userId],
  queryFn: getProjectsByUser,
  // The query will not execute until the userId exists
  enabled: !!userId
})
// 여기서 !!userId 는 Boolean(userId)와 같습니다.

✅ select

  • queryFn 에 의해 리턴된 값을 변형시킨 후에 useQuery 의 리턴 data로 넘겨줌
    (단, cache data 는 queryFn 에서 리턴받은 값 그대로임)
import { useQuery } from 'react-query'

function User() {
  const { data } = useQuery(['user'], fetchUser, {
    select: (user) => user.username,
  })
  return <div>Username: {data}</div>
}
profile
호기심 많은 귀차니즘의 공부 일기

0개의 댓글