리액트 쿼리(React Query)란?
React Query는 리액트에서 서버 상태(Server State)를 쉽게 관리할 수 있도록 도와주는 라이브러리이다.
일반적인 상태관리 라이브러리(예: Redux, Zustand)는 클라이언트 상태(UI 상태 등)를 주로 다루는데,
React Query는 서버에서 가져오는 데이터(API 요청 등)를 관리하는 데 특화되어 있다.
[개념 알고 가기 🔍]
1) 서버 상태란?
- 예를 들어 GET /users, GET /posts 같은 API로 받아오는 데이터를 말한다.
이 데이터는 서버에 있고, 클라이언트에서 가져와야 하니까 서버 상태라고 불린다.
리액트 쿼리를 사용하는 이유
- 로딩 상태 관리 (loading, isFetching)
- 에러 상태 처리 (try/catch)
- 데이터 캐싱
- 다시 불러오기 (Refetch)
- 백그라운드 업데이트
- 페이지네이션, 무한 스크롤
- 데이터 동기화
일반적으로 데이터 API를 가져올 때는 위의 내용들이 필요하다.
하지만 리액트 쿼리에서는 위의 내용들을 지원해주므로 편리하게 사용할 수 있다.
기능 | 설명 |
로딩/에러 상태 관리 | isLoading, isError 등 자동으로 제공 |
데이터 캐싱 | 동일한 요청에 대해 중복 호출 방지 |
백그라운드 리패칭 | 오래된 데이터는 자동으로 새로고침 |
쿼리 무효화 | mutation 이후 관련 데이터만 정확히 다시 불러오기 |
윈도우 포커스시 재요청 | 사용자 눈에는 항상 최신 데이터처럼 보이도록 |
페이지네이션/무한 스크롤 | 쉽게 구현 가능 |
SSR & prefetch 지원 | 서버사이드 렌더링도 지원 |
따라서 리액트 쿼리를 사용한다면 다음과 같은 이점을 얻을 수 있다.
1. 코드 간결성
복잡한 상태 관리 로직을 생략할 수 있고, 필요한 건 useQuery, useMutation 두 가지 훅이면 충분하다.
2. 성능 향상
중복 요청 방지(캐시), 사용자 인터랙션 시 빠르게 응답, 백그라운드 업데이트로 UX 향상
3. 유지보수 용이성
비즈니스 로직과 API 관리가 분리되어 깔끔, 새로운 API 추가나 수정 시에도 일관된 방식
리액트 쿼리 쓰는 이유 정리 🔍
1) 복잡한 서버 상태 관리를 간단하게 만들어줌
2) 로딩, 에러, 캐싱, 재요청 등 필수 기능을 자동으로 처리
3) 코드가 간결해지고 버그가 줄어듦
4) 성능과 사용자 경험(UX)이 좋아짐
5) 빠른 개발 속도, 유지보수 쉬움
그렇다면 이제 React-Query를 사용해보자!
설치 방법
npm install @tanstack/react-query
* 예전 이름은 react-query였고, 지금은 @tanstack/react-query로 바뀜
⚠️ 리액트 쿼리를 설치할 때 리액트 버전과 호환 가능한 버전인지 확인해야한다.
React-Query 버전 | 호환 가능 React 버전 | 비고 |
v3.x (react-query) | React 16.8 이상 | 오래된 버전, 이제는 권장되지 않음 |
v4.x (@tanstack/react-query) | React 17, React 18 이상 | 최신 기능 대응, 현재 주로 사용되는 버전 |
v5 (알파/베타) | React 18 이상 | Suspense/Concurrent 대응 강화, 사용 가능하지만 실험적일 수 있음 |
설치 후 세팅
리액트 쿼리를 사용하기 위해서는 QueryClient 만들고, Provider로 감싸줘야한다.
* Provider를 꼭 써야 React Query 기능이 작동
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<MyComponent />
</QueryClientProvider>
);
}
리액트 쿼리 주요 기능
기능 | 설명 |
useQuery | 데이터 조회 (GET) |
useMutation | 데이터 쓰기 (POST, PUT, DELETE 등) |
isLoading, error | 상태 자동 관리 |
refetch() | 수동 재요청 |
invalidateQueries() | 쿼리 무효화 (자동 리패칭) |
캐싱 | 동일한 데이터 요청 방지 |
배경 데이터 갱신 | 사용자 모르게 데이터 업데이트 |
1. useQuery로 데이터 불러오기
- queryKey: 이 쿼리의 고유 키. 데이터를 식별하고 캐시 관리에 사용
- queryFn: 데이터를 실제로 가져오는 함수 (API 호출 등)
- data, isLoading, error: 상태 자동 관리
[예시 코드]
import { useQuery } from '@tanstack/react-query';
import axios from 'axios';
function MyComponent() {
const { data, isLoading, error } = useQuery({
queryKey: ['users'],
queryFn: () => axios.get('/api/users').then(res => res.data)
});
if (isLoading) return <p>로딩 중...</p>;
if (error) return <p>에러 발생!</p>;
return (
<ul>
{data.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
2. useMutaion으로 데이터 쓰기
[예시 코드]
import { useMutation } from '@tanstack/react-query';
import axios from 'axios';
function AddUser() {
const mutation = useMutation({
mutationFn: (newUser) => axios.post('/api/users', newUser),
onSuccess: () => {
// 성공 시 users 쿼리를 다시 불러옴
queryClient.invalidateQueries({ queryKey: ['users'] });
},
});
return (
<button onClick={() => mutation.mutate({ name: '홍길동' })}>
유저 추가
</button>
);
}
3. 리패칭 (refetching)
데이터를 수동으로 다시 불러오고 싶을 때 사용한다.
const { data, refetch } = useQuery(...);
<button onClick={() => refetch()}>다시 불러오기</button>
4. 자동 새로고침 (Refetch Interval)
자동으로 새로고침하고 싶을 때는 useQuery에 refetchInterval 옵션을 설정한다.
useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
refetchInterval: 10000, // 10초마다 자동으로 새로고침
});
5. 캐싱과 상태관리
React Query는 자동으로 데이터 캐싱을 하고, 동일한 queryKey로 요청하면 서버에 다시 요청하지 않고 캐시된 데이터를 반환한다.
- 최초 로딩 시 서버 요청
- 같은 컴포넌트가 다시 렌더링되면 캐시된 데이터 사용
- 설정에 따라 일정 시간이 지나면 자동으로 다시 요청 (stale time)
[개념 알고가기 🔍]
1) staleTime이란? - 신선한 상태 유지 시간
① 동작 방식 (staleTime: 10000으로 설정된 경우)
쿼리가 처음 실행되면 데이터를 받아오고, 그 시점부터 10초 동안은 stale 상태가 아님.
이 기간 동안은 React Query가 자동으로 재요청(refetch) 하지 않음.
10초가 지나면 데이터가 stale(오래됨)으로 간주되고, 다음 조건 중 하나가 일어나면 다시 데이터를 요청(refetch) 해
: 컴포넌트가 다시 mount되거나, 창에 다시 포커스되거나, 네트워크가 다시 연결될 때 등
② 언제 유용할까?
너무 자주 데이터를 새로 불러오지 않아도 되는 경우
(예: 공지사항, 프로필, 상품 목록 등 자주 바뀌지 않는 데이터)
2) cacheTime이란? - 캐시 유지 시간
① 동작 방식 (cacheTime: 300000으로 설정된 경우)
컴포넌트가 unmount되어도 데이터를 바로 삭제하지 않음.
이 cacheTime이 끝날 때까지 메모리 안에 캐시로 유지
다시 같은 쿼리가 mount되면, 서버에 요청하지 않고 캐시된 데이터를 바로 사용함.
② 언제 유용할까?
- 사용자가 같은 페이지로 자주 왔다 갔다 할 때
- 리스트 → 상세 → 다시 리스트 같은 구조에서 유용
- 불필요한 재요청을 줄여 성능 최적화 가능
[간단 요약]
- staleTime이 길면 = 네트워크 요청 줄어듦, 대신 데이터는 오래됨 가능성 ↑
- cacheTime이 길면 = 빠른 화면 전환 가능, 하지만 메모리 사용량 ↑
[추천 예시]
옵션 의미 기본 값 staleTime 데이터를 신선하다고 간주할 시간 (ms) 0 (즉시 stale 처리) cacheTime 데이터가 사용되지 않아도 캐시에 유지되는 시간 (ms) 5분 (300000)
1) 실시간 데이터 (ex. 실시간 주가, 라이브 채팅 등) ➡️ staleTime: 0, refetchInterval 사용해서 실시간 유지
2) 일반 리스트 (ex. 게시글 목록) ➡️ staleTime: 30초~1분, cacheTime: 3~5분
3) 잘 바뀌지 않는 데이터 (ex. 유저 프로필) ➡️ staleTime: Infinity (영원히 fresh), cacheTime: Infinity (계속 보관)
자주 사용하는 옵션들
useQuery({
queryKey: ['posts'],
queryFn: fetchPosts,
staleTime: 5000, // 5초 동안은 신선한 데이터로 간주
cacheTime: 1000 * 60 * 5, // 5분간 캐시 유지
refetchOnWindowFocus: true, // 창 포커스될 때 refetch
});