React-Query(Tanstack-Query)
React Query는 리액트 어플리케이션에서 비동기 데이터의 페칭(Fetching)과 캐싱(caching)을 간편하게 관리할 수 있게 해주는 라이브러리다.
* 페칭과 캐싱
데이터 페칭이란?
서버로부터 데이터를 가져오는(읽는) 것
캐싱이란?
특정 데이터의 복사본을 저장하여 이후 동일한 데이터의 재접근 속도를 높이는 것
주요 기능
데이터 캐싱 : 동일한 데이터를 여러 번 요청하지 않도록 캐싱하여 성능을 향상시킴.
자동 리페칭 : 데이터가 변경되었을 때 자동으로 리페칭하여 최신 상태를 유지함.
쿼리 무효화 : 특정 이벤트가 발생했을 때 쿼리를 무효화하고 데이터를 다시 가져올 수 있음.
라이플 사이클 상태 정의
fresh : 만료되지 않은 쿼리
- 컴포넌트 마운트, 업데이트되어도 재요청하지 않음
- defalut staleTime이 0으로 설정되어 있기 때문에 호출이 끝나면 바로 stale 상태로 변함
- staleTile을 늘려줄 경우, fresh 상태가 유지되며, 쿼리가 다시 마운트되어도 페칭이 발생하지 않고 기존의 fresh값 반환
fetching : 요청 중인 쿼리
stale : 만료된 쿼리
- 컴포넌트 마운트, 업데이트되면 데이터 재요청(리페칭 요청)
inactive : 비활성화된 쿼리
- active 쿼리가 하나도 없는 쿼리
- inactive된 이후, cacheTime 동안 캐시 데이터 유지
- cacheTime 이후 가비지 컬렉터(GC)에 의해 제거
라이프 사이클
1. User 쿼리 인스턴스 mount
2. 네트워크에서 데이터 fetch -> User의 query key로 캐싱
3. 데이터는 fresh 상태에서 staleTime(default : 0) 이후 stale 상태로 변경
4. User 쿼리 인스턴스 unmount
5. 캐시는 cacheTime(default : 1000*60*5 (5분)) 유지 후 GC에 의해 제거
6. cacheTime 지나기전 User 쿼리 인스턴스가 새롭게 mount 되면, fetch를 실행하며, fresh한 값을 가져오는동안 캐시 데이터를 보여줌
사용법
yarn add @tanstack/react-query
yarn add @tanstack/react-query-devtools
react-query-devtools는 리액트 쿼리를 좀 더 쉽게 다룰 수 있게 도와주는 전용 도구다.
같이 받아 주고, 적용할 범위에 Provider를 이용해 적용하자.
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "./index.css";
import App from "./App.jsx";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; // 추가
import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; // 추가
const queryClient = new QueryClient();
createRoot(document.getElementById("root")).render(
<StrictMode>
<QueryClientProvider client={queryClient}>
<App />
<ReactQueryDevtools initialIsOpen={true} />
</QueryClientProvider>
,
</StrictMode>
);
예시
const { data, isLoading, error } = useQuery([queryKey,변수], fn, options)
// Uncaught Error: Bad argument type. Starting with v5, only the "Object" form is allowed when calling query related functions.
// Please use the error stack to find the culprit call.
// More info here: https://tanstack.com/query/latest/docs/react/guides/migrating-to-v5#supports-a-single-signature-one-object
❗ 위 예시는 사용 불가
위 타입은 사용이 불가하다고 하며 v5부터는 단일 객체 형태로만 사용 해야 한다고 한다.
const { data, isLoading, error } = useQuery({ queryKey, queryFn, ...options })
useQuery()
데이터를 가져오기 위해 사용되는 대표적인 훅이며, 쿼리 키와 비동기 함수를 인자로 받아 데이터를 가져오고 로딩 상태, 오류 상태, 데이터를 반환한다.
QueryKey
QueryKey를 기반으로 데이터 캐싱을 관리하며, 동일한 키를 가진 쿼리를 통해 캐싱된 데이터를 재활용한다.
문자열 또는 배열로 지정할 수 있다.
const { data, isLoading, error } = useQuery({ queryKey, queryFn, ...options })
// ~~~~~~~~
쿼리가 변수에 의존하는 경우에는 queryKey에도 해당 변수를 추가해주어야 한다.
const 변수 = 1;
const { data, isLoading, error } = useQuery({[queryKey,변수], queryFn, ...options});
// ~~~~~~~~~~~~~~~~
예시
const id = 1
const { data, error, isLoading } = useQuery({
queryKey: ['posts',id],
});
Query Functions
useQuery의 두번째 인자에는 promise를 반환하는 함수를 넣어주어야 하며,
useQuery나 useMutattion에서 데이터를 가져오거나 변경하는 로직을 작성한다
예시
const fetchPosts = async () => {
const response = await fetch('/api/posts');
return response.json();
};
const { data, error, isLoading } = useQuery({
queryKey: ... ,
queryFn: fetchPosts,
});
Query Options
세번째 인자에는 옵션들을 추가할 수 있는데, 양이 많아 주로 사용되는 옵션들만 확인해 보자.
1. enabled (boolean)
쿼리가 자동으로 실행되지 않게 설정하는 옵션이며, 조건부로 데이터를 가져와야 할 때 많이 사용한다.
const { data, isLoading } = useQuery({
queryKey: ["user", userId],
queryFn: fetchUser,
enabled: !!userId, // userId가 존재할 때만 실행
});
2. staleTime (number | infinity)
데이터가 fresh -> stale 상태로 변경되는데 소요되는 시간이다.
fresh 상태인 경우 쿼리 인스턴스가 새롭게 mount되어도, fetch가 발생하지 않는다.
default : 0
* stale이란?
리액트 쿼리에는 stale이란 개념이 있는데, 사전적 의미로는 신선하지 않는 뜻이다.
기본적으로 캐싱된 데이터를 stale한 상태로 여기며, 최신화가 필요한 데이터라는 의미로 stale한 상태가 되면 다음의 경우에 refetch된다.
1. 새로운 query instance가 마운트 될 때
2. 브라우저 화면을 이탈 했다가 다시 focus 할 때
3. 네트워크가 다시 연결될 때
4. 특별히 설정한 refetch interval에 의한 경우
refetchOnWindowFocus 옵션 등으로 기본 refetch 설정을 막을 수 있고 staleTime 옵션으로 설정한 시간 동안 데이터가 stale 되지 않도록 해 refetch를 막을 수도 있다.
query에 별다른 action이 없으면 inactive 상태로 캐시에 남아 있다가 5분 뒤에 메모리에서 사라진다. cacheTime 옵션을 설정해서 이 시간을 조정할 수 있다.
useQuery({
queryKey: ["posts"],
queryFn: fetchPosts,
staleTime: 1000 * 60,
});
// 1분 동안 fresh 상태이며 1분 뒤 stale 상태, default : 0
3. chcheTime (number | infinity)
inactive 상태인 캐시 데이터가 메모리에 남아있는 시간이며, 이 시간이 지나면 캐시데이터는 가비지 컬렉터(GC)에 의해 메모리에서 제거된다.
default : 1000 * 60 * 5 , 5분
useQuery({
queryKey: ["posts"],
queryFn: fetchPosts,
cacheTime: 1000 * 60 * 5,
});
4. retry (boolean | number | (failureCount: number, error: TError) => boolean)
실패한 쿼리를 재시도 하는 옵션이며, true로 설정하면 쿼리 실패시 무한 재시도 하고, false로 설정하면 재시도를 하지 않는다.
default : 3
5. refetchOnWindowFocus (boolean | "always")
데이터가 stale 상태일 경우 윈도우 포커싱 될 때 마다 refetch를 실행하는 옵션이다.
예를 들어, 크롬에서 다른 탭을 눌렀다가 다시 원래 보던 중인 탭을 눌렀을 때도 이 경우에 해당하며, 개발자도구 창을 켜서 네트워크 탭이든, 다른 탭이든 클릭하고 페이지 내부를 클릭했을 때에도 이 경우에 해당한다.
QueryClient defaultOptions 설정으로 refetch 기능들을 다 false로 꺼버렸을 경우에는 refetch 기능이 실행되지 않으며, 그럴 경우엔 refetchOnWindowFocus 옵션이 실행되게끔 true로 설정하며 된다.
default : true
fresh 상태인 동안은 아무리 다른 탭을 왔다갔다해도 fetch 요청을 하지 않는다.
const { data } = useQuery({
queryKey: ["posts"],
queryFn: fetchPosts,
refetchOnWindowFocus: false, // 창 활성화 시 refetch 비활성화
});
6. onSuccess ((data:TDdata) => void) / onError ((error:TError) => void)
쿼리 성공 또는 실패 시 실행될 콜백 함수를 정의하며, 각각의 매개변수 data는 response값, error를 받을 수있다.
const { data } = useQuery({
queryKey: ["posts"],
queryFn: fetchPosts,
onSuccess: (data) => console.log("Data fetched successfully:", data),
onError: (error) => console.log("Error fetching data:", error),
});
useMutation()
useQuery와는 다르게 서버에 데이터를 추가 / 수정 / 삭제 할 때 사용 되며, 작업의 성공, 실패, 진행중 상태를 쉽게 관리할 수 있게 한다.
const mutation = useMutation(mutationFn, {
onMutate: (variables) => {
// 작업이 시작되기 전에 실행되는 콜백
// Optimistic update를 위해 사용될 수 있음
},
onSuccess: (data, variables, context) => {
// 작업이 성공적으로 완료되었을 때 실행되는 콜백
},
onError: (error, variables, context) => {
// 작업이 실패했을 때 실행되는 콜백
},
onSettled: (data, error, variables, context) => {
// 작업이 성공 또는 실패했을 때 실행되는 콜백
// 주로 캐시를 무효화하거나 refetch를 트리거하는 데 사용됨
},
});
mutationFn (variables: TVariables) => Promise<TData>
- Required
- 비동기 작업을 수행하고 프로미스를 반환하는 함수(쉽게 말해 api 요청하는 함수)
- varables는 mutate가 전달하는 객체
onMutate: (variables: TVariables) => Promise<TContext | void> | TContext | void
- onMutate는 mutation함수가 실행되기 전에 실행되고 mutation 함수가 받을 동일한 변수가 전달 됨
- optimistic update 사용 시 유용함
* optimistic update란?
낙관적 업데이트라 부르며 요청을 보내기 전에 UI를 업데이트하는 기능이다.
대표 적인 예로는 인스타그램 좋아요 버튼이 있다.
사용자가 좋아요 버튼을 눌렀을때 UI에서 즉시 좋아요 수가 증가하도록 할 수 있는데, 이 때 서버로 요청을 보내기 전에 UI를 업데이트 하는 것이 낙관적 업데이트며, 이때 어떠한 이유에서든 업데이트가 실패하면 롤백 된다.
onSuccess: (data: TData, variables: TVariables, context?: TContext) => Promise<unknown> | void
- mutation이 성공하고 결과를 전달할 때 실행 됨
onError: (err: TError, variables: TVariables, context?: TContext) => Promise<unknown> | void
- muation이 error를 만났을 때 실행 됨
onSettled: (data: TData, error: TError, variables: TVariables, context?: TContext) => Promise<unknown> | void
- mutation이 성공해서 성공한 데이터 또는 error가 전달 될 때 실행 된다.(성공하든 실패하든)
'항해99 플러스 > React 스터디' 카테고리의 다른 글
[React 스터디] 비동기, REST API (feat: JSON) (0) | 2025.03.04 |
---|---|
[React 스터디] React Router Dom (0) | 2025.03.04 |
[React 스터디] Redux (0) | 2025.02.24 |
[React 스터디] 2주차 숙련 단계 (0) | 2025.02.17 |
[React 스터디] 2주차 입문 단계 (1) | 2025.02.16 |
React-Query(Tanstack-Query)
React Query는 리액트 어플리케이션에서 비동기 데이터의 페칭(Fetching)과 캐싱(caching)을 간편하게 관리할 수 있게 해주는 라이브러리다.
* 페칭과 캐싱
데이터 페칭이란?
서버로부터 데이터를 가져오는(읽는) 것
캐싱이란?
특정 데이터의 복사본을 저장하여 이후 동일한 데이터의 재접근 속도를 높이는 것
주요 기능
데이터 캐싱 : 동일한 데이터를 여러 번 요청하지 않도록 캐싱하여 성능을 향상시킴.
자동 리페칭 : 데이터가 변경되었을 때 자동으로 리페칭하여 최신 상태를 유지함.
쿼리 무효화 : 특정 이벤트가 발생했을 때 쿼리를 무효화하고 데이터를 다시 가져올 수 있음.
라이플 사이클 상태 정의
fresh : 만료되지 않은 쿼리
- 컴포넌트 마운트, 업데이트되어도 재요청하지 않음
- defalut staleTime이 0으로 설정되어 있기 때문에 호출이 끝나면 바로 stale 상태로 변함
- staleTile을 늘려줄 경우, fresh 상태가 유지되며, 쿼리가 다시 마운트되어도 페칭이 발생하지 않고 기존의 fresh값 반환
fetching : 요청 중인 쿼리
stale : 만료된 쿼리
- 컴포넌트 마운트, 업데이트되면 데이터 재요청(리페칭 요청)
inactive : 비활성화된 쿼리
- active 쿼리가 하나도 없는 쿼리
- inactive된 이후, cacheTime 동안 캐시 데이터 유지
- cacheTime 이후 가비지 컬렉터(GC)에 의해 제거
라이프 사이클
1. User 쿼리 인스턴스 mount
2. 네트워크에서 데이터 fetch -> User의 query key로 캐싱
3. 데이터는 fresh 상태에서 staleTime(default : 0) 이후 stale 상태로 변경
4. User 쿼리 인스턴스 unmount
5. 캐시는 cacheTime(default : 1000*60*5 (5분)) 유지 후 GC에 의해 제거
6. cacheTime 지나기전 User 쿼리 인스턴스가 새롭게 mount 되면, fetch를 실행하며, fresh한 값을 가져오는동안 캐시 데이터를 보여줌
사용법
yarn add @tanstack/react-query
yarn add @tanstack/react-query-devtools
react-query-devtools는 리액트 쿼리를 좀 더 쉽게 다룰 수 있게 도와주는 전용 도구다.
같이 받아 주고, 적용할 범위에 Provider를 이용해 적용하자.
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "./index.css";
import App from "./App.jsx";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; // 추가
import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; // 추가
const queryClient = new QueryClient();
createRoot(document.getElementById("root")).render(
<StrictMode>
<QueryClientProvider client={queryClient}>
<App />
<ReactQueryDevtools initialIsOpen={true} />
</QueryClientProvider>
,
</StrictMode>
);
예시
const { data, isLoading, error } = useQuery([queryKey,변수], fn, options)
// Uncaught Error: Bad argument type. Starting with v5, only the "Object" form is allowed when calling query related functions.
// Please use the error stack to find the culprit call.
// More info here: https://tanstack.com/query/latest/docs/react/guides/migrating-to-v5#supports-a-single-signature-one-object
❗ 위 예시는 사용 불가
위 타입은 사용이 불가하다고 하며 v5부터는 단일 객체 형태로만 사용 해야 한다고 한다.
const { data, isLoading, error } = useQuery({ queryKey, queryFn, ...options })
useQuery()
데이터를 가져오기 위해 사용되는 대표적인 훅이며, 쿼리 키와 비동기 함수를 인자로 받아 데이터를 가져오고 로딩 상태, 오류 상태, 데이터를 반환한다.
QueryKey
QueryKey를 기반으로 데이터 캐싱을 관리하며, 동일한 키를 가진 쿼리를 통해 캐싱된 데이터를 재활용한다.
문자열 또는 배열로 지정할 수 있다.
const { data, isLoading, error } = useQuery({ queryKey, queryFn, ...options })
// ~~~~~~~~
쿼리가 변수에 의존하는 경우에는 queryKey에도 해당 변수를 추가해주어야 한다.
const 변수 = 1;
const { data, isLoading, error } = useQuery({[queryKey,변수], queryFn, ...options});
// ~~~~~~~~~~~~~~~~
예시
const id = 1
const { data, error, isLoading } = useQuery({
queryKey: ['posts',id],
});
Query Functions
useQuery의 두번째 인자에는 promise를 반환하는 함수를 넣어주어야 하며,
useQuery나 useMutattion에서 데이터를 가져오거나 변경하는 로직을 작성한다
예시
const fetchPosts = async () => {
const response = await fetch('/api/posts');
return response.json();
};
const { data, error, isLoading } = useQuery({
queryKey: ... ,
queryFn: fetchPosts,
});
Query Options
세번째 인자에는 옵션들을 추가할 수 있는데, 양이 많아 주로 사용되는 옵션들만 확인해 보자.
1. enabled (boolean)
쿼리가 자동으로 실행되지 않게 설정하는 옵션이며, 조건부로 데이터를 가져와야 할 때 많이 사용한다.
const { data, isLoading } = useQuery({
queryKey: ["user", userId],
queryFn: fetchUser,
enabled: !!userId, // userId가 존재할 때만 실행
});
2. staleTime (number | infinity)
데이터가 fresh -> stale 상태로 변경되는데 소요되는 시간이다.
fresh 상태인 경우 쿼리 인스턴스가 새롭게 mount되어도, fetch가 발생하지 않는다.
default : 0
* stale이란?
리액트 쿼리에는 stale이란 개념이 있는데, 사전적 의미로는 신선하지 않는 뜻이다.
기본적으로 캐싱된 데이터를 stale한 상태로 여기며, 최신화가 필요한 데이터라는 의미로 stale한 상태가 되면 다음의 경우에 refetch된다.
1. 새로운 query instance가 마운트 될 때
2. 브라우저 화면을 이탈 했다가 다시 focus 할 때
3. 네트워크가 다시 연결될 때
4. 특별히 설정한 refetch interval에 의한 경우
refetchOnWindowFocus 옵션 등으로 기본 refetch 설정을 막을 수 있고 staleTime 옵션으로 설정한 시간 동안 데이터가 stale 되지 않도록 해 refetch를 막을 수도 있다.
query에 별다른 action이 없으면 inactive 상태로 캐시에 남아 있다가 5분 뒤에 메모리에서 사라진다. cacheTime 옵션을 설정해서 이 시간을 조정할 수 있다.
useQuery({
queryKey: ["posts"],
queryFn: fetchPosts,
staleTime: 1000 * 60,
});
// 1분 동안 fresh 상태이며 1분 뒤 stale 상태, default : 0
3. chcheTime (number | infinity)
inactive 상태인 캐시 데이터가 메모리에 남아있는 시간이며, 이 시간이 지나면 캐시데이터는 가비지 컬렉터(GC)에 의해 메모리에서 제거된다.
default : 1000 * 60 * 5 , 5분
useQuery({
queryKey: ["posts"],
queryFn: fetchPosts,
cacheTime: 1000 * 60 * 5,
});
4. retry (boolean | number | (failureCount: number, error: TError) => boolean)
실패한 쿼리를 재시도 하는 옵션이며, true로 설정하면 쿼리 실패시 무한 재시도 하고, false로 설정하면 재시도를 하지 않는다.
default : 3
5. refetchOnWindowFocus (boolean | "always")
데이터가 stale 상태일 경우 윈도우 포커싱 될 때 마다 refetch를 실행하는 옵션이다.
예를 들어, 크롬에서 다른 탭을 눌렀다가 다시 원래 보던 중인 탭을 눌렀을 때도 이 경우에 해당하며, 개발자도구 창을 켜서 네트워크 탭이든, 다른 탭이든 클릭하고 페이지 내부를 클릭했을 때에도 이 경우에 해당한다.
QueryClient defaultOptions 설정으로 refetch 기능들을 다 false로 꺼버렸을 경우에는 refetch 기능이 실행되지 않으며, 그럴 경우엔 refetchOnWindowFocus 옵션이 실행되게끔 true로 설정하며 된다.
default : true
fresh 상태인 동안은 아무리 다른 탭을 왔다갔다해도 fetch 요청을 하지 않는다.
const { data } = useQuery({
queryKey: ["posts"],
queryFn: fetchPosts,
refetchOnWindowFocus: false, // 창 활성화 시 refetch 비활성화
});
6. onSuccess ((data:TDdata) => void) / onError ((error:TError) => void)
쿼리 성공 또는 실패 시 실행될 콜백 함수를 정의하며, 각각의 매개변수 data는 response값, error를 받을 수있다.
const { data } = useQuery({
queryKey: ["posts"],
queryFn: fetchPosts,
onSuccess: (data) => console.log("Data fetched successfully:", data),
onError: (error) => console.log("Error fetching data:", error),
});
useMutation()
useQuery와는 다르게 서버에 데이터를 추가 / 수정 / 삭제 할 때 사용 되며, 작업의 성공, 실패, 진행중 상태를 쉽게 관리할 수 있게 한다.
const mutation = useMutation(mutationFn, {
onMutate: (variables) => {
// 작업이 시작되기 전에 실행되는 콜백
// Optimistic update를 위해 사용될 수 있음
},
onSuccess: (data, variables, context) => {
// 작업이 성공적으로 완료되었을 때 실행되는 콜백
},
onError: (error, variables, context) => {
// 작업이 실패했을 때 실행되는 콜백
},
onSettled: (data, error, variables, context) => {
// 작업이 성공 또는 실패했을 때 실행되는 콜백
// 주로 캐시를 무효화하거나 refetch를 트리거하는 데 사용됨
},
});
mutationFn (variables: TVariables) => Promise<TData>
- Required
- 비동기 작업을 수행하고 프로미스를 반환하는 함수(쉽게 말해 api 요청하는 함수)
- varables는 mutate가 전달하는 객체
onMutate: (variables: TVariables) => Promise<TContext | void> | TContext | void
- onMutate는 mutation함수가 실행되기 전에 실행되고 mutation 함수가 받을 동일한 변수가 전달 됨
- optimistic update 사용 시 유용함
* optimistic update란?
낙관적 업데이트라 부르며 요청을 보내기 전에 UI를 업데이트하는 기능이다.
대표 적인 예로는 인스타그램 좋아요 버튼이 있다.
사용자가 좋아요 버튼을 눌렀을때 UI에서 즉시 좋아요 수가 증가하도록 할 수 있는데, 이 때 서버로 요청을 보내기 전에 UI를 업데이트 하는 것이 낙관적 업데이트며, 이때 어떠한 이유에서든 업데이트가 실패하면 롤백 된다.
onSuccess: (data: TData, variables: TVariables, context?: TContext) => Promise<unknown> | void
- mutation이 성공하고 결과를 전달할 때 실행 됨
onError: (err: TError, variables: TVariables, context?: TContext) => Promise<unknown> | void
- muation이 error를 만났을 때 실행 됨
onSettled: (data: TData, error: TError, variables: TVariables, context?: TContext) => Promise<unknown> | void
- mutation이 성공해서 성공한 데이터 또는 error가 전달 될 때 실행 된다.(성공하든 실패하든)
'항해99 플러스 > React 스터디' 카테고리의 다른 글
[React 스터디] 비동기, REST API (feat: JSON) (0) | 2025.03.04 |
---|---|
[React 스터디] React Router Dom (0) | 2025.03.04 |
[React 스터디] Redux (0) | 2025.02.24 |
[React 스터디] 2주차 숙련 단계 (0) | 2025.02.17 |
[React 스터디] 2주차 입문 단계 (1) | 2025.02.16 |