- 비동기 서버 상태(fetching data)를 위한 React 전용 라이브러리
- 캐싱, 리페칭, 에러 처리, 상태 관리까지 통합적으로 처리하는 서버 상태 관리 도구
1. 서버 상태와 클라이언트 상태의 분리
| 구분 | 클라이언트 상태 | 서버 상태 |
| 예시 | 모달 열림 여부, 폼 입력 값 | 유저 정보, 게시글 목록 |
| 소스 | 사용자의 UI 상의 임시 값 | 외부 API 또는 DB |
| 책임 | UI 상태 조작 | 외부와 동기화 유지 |
| 추천 도구 | React의 useState, useReducer, Recoil 등 | React Query |
분리하는 이유
- 클라이언트 상태는 메모리 상에서만 관리되므로 즉시 변경 가능
- 서버 상태는 fetching, error, loading 상태 등을 항상 고려해야 함
- Redux 등으로 서버 상태를 관리하면 불필요한 보일러플레이트가 발생하고 유지가 어려워짐
2. 데이터 페칭 및 캐싱 자동화
React Query가 자동으로 해주는 일
- useQuery()로 API 요청 -> 응답값을 캐시에 저장
- 같은 key로 요청이 다시 들어오면 캐시에서 불러오기
- 설정에 따라 자동으로 refetch, background에서 데이터 최신화
const { data, isLoading } = useQuery(['posts'], fetchPosts);
['posts']: Query Key
- 캐시의 고유 식별자
- posts라는 이름으로 데이터를 캐싱하고 추후 동일한 키를 사용해서 데이터를 재사용하거나 리페칭
- 배열 형태로 쓰는 이유는 동적 파라미터(상황에 따라 바뀌는 값)를 함께 넣기 위함
useQuery(['posts', userId], () => fetchUserPosts(userId));
-> userId마다 별도로 캐시됨
fetchPosts: Query Function
- 데이터를 가져오는 비동기 함수
async function fetchPosts() {
const res = await fetch(url);
return res.json();
}
- 이 함수를 자동으로 호출해서 데이터를 가져오고, 그 결과를 캐싱함
반환값 구조
const { data, isLoading, error } = useQuery(...);
- data : fetchPosts() 함수 결과
- isLoading : 요청 중이면 true
- error : 요청 실패 시 오류 정보
자동 refetch 조건 예시
- 컴포넌트 리마운트
- 브라우저 포커스
- 시간 기반 refetch (5분마다)
Redux에서는 직접 구현해야 하지만 React Query는 기본값으로 제공함
3. 복잡한 비동기 로직의 간소화
기존 Redux 방식
const FETCH_POSTS = 'FETCH_POSTS';
const FETCH_POSTS_SUCCESS = 'FETCH_POSTS_SUCCESS';
React Query 방식
const { data, isLoading } = useQuery(['posts'], fetchPosts);
- 데이터 요청 상태(loading, error, data) 자동 관리
- 로딩 스피너, 에러 처리도 간단히 연동 가능
- 콜백이나 Thunk, Saga 등의 미들웨어 설정 없이도 간단하게 비동기 처리 가능
4. 옵티미스틱 업데이트와 실시간 처리
옵티미스틱 업데이트란?
- 서버 응답이 오기 전에 UI를 먼저 바꿔주는 전략
useMutation(updateLike, {
onMutate: newData => {
queryClient.setQueryData(['likes'], old => old + 1); // 먼저 UI 업데이트
},
onError: () => {
queryClient.invalidateQueries(['likes']); // 실패 시 롤백
}
});
- useMutation을 사용해 좋아요를 업데이트하는 비동기 작업을 수행
- UI를 Optimistic Update 방식으로 빠르게 반응시키는 구조
useMutation(updateLike, ...)
- updateLike 함수는 서버에 좋아요를 업데이트하는 비동기 함수
- useMutation은 서버에 변경을 요청하고, 그 과정에서 UI도 제어할 수 있게 해주는 훅
onMutate
- 서버에 요청을 보내기 바로 직전에 실행됨
- Optimistic Update를 위해 사용함
- 실제 서버 응답이 오기 전에 UI를 미리 바꿈
- 사용자 입장에선 빠르게 반응하는 UI를 보게 됨
queryClient.setQueryData(['likes'], old => old + 1);
- ['likes'] 캐시 값을 수동으로 업데이트 함
- 기존 값(old)에 1을 더해 화면에 바로 반영되게 함
onError
- 서버 요청이 실패했을 때 실행됨
- 앞서 반영한 Optimistic Update가 틀렸을 수 있으니,
- invalidateQueries로 ['likes'] 쿼리를 무효화하고,
- 서버에서 다시 데이터를 다시 가져오게 함 (롤백)
실시간 업데이트 방법
- Polling : 일정 주기로 데이터 다시 요청
- WebSocket : 서버에서 직접 푸시 (React Query 자체는 안 하지만, 통합 사용 가능)
5. 개발자 도구 지원
- 어떤 쿼리가 loading 중인지
- 어떤 데이터가 캐시돼 있는지
- 쿼리마다 status / state / refetch 여부
- 즉시 캐시 무효화 / 수동 refetch 가능
npm install @tanstack/react-query-devtools
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
<QueryClientProvider client={queryClient}>
<App />
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
QueryClientProvider
- React Query의 context provider
- 내부에서 useQuery, useMutation 등을 사용할 수 있게 전역으로 설정함
- client={queryClient}는 QueryClient 인스턴스를 전달함. 이 인스턴스는 쿼리 캐시, 설정 등을 관리함
const queryClient = new QueryClient();
- <App />이 QueryClientProvider 안에 있어야 React Query 기능이 동작함
ReactQueryDevtools
- React Query 상태를 시각적으로 보여주는 개발 도구
- 브라우저에서 열리는 Devtools 탭처럼, 현재 캐시된 데이터, 쿼리 상태, 에러 상태를 볼 수 있음
- initialIsOpen={false}는 처음엔 닫힌 상태로 시작하겠다는 의미
6. 커뮤니티 및 생태계
- GitHub Star 수, 다운로드 수 모두 전역 상태 관리 라이브러리 중 최상위
- TanStack Query라는 이름으로 Vue, Svelte, Solid까지 확장됨

7. 결론
- 서버에서 가져온 데이터를 자동으로 캐싱, 리페칭, 동기화
- 복잡한 상태 로직 없이 간단한 API로 사용 가능
- 실제 프로덕션에서 개발 속도와 안정성을 크게 향상시켜줌
- 클라이언트 상태는 useState, Recoil
- 서버 상태는 React Query
'카테캠 > 2단계' 카테고리의 다른 글
| [7/22] React Query - useMutation (1) | 2025.07.22 |
|---|---|
| [7/22] React Query - useQuery (0) | 2025.07.22 |
| [7/22] 무한 스크롤 옵저버 (0) | 2025.07.22 |
| [7/21] Intersection Observer (1) | 2025.07.22 |
| [7/18] Fetch API Wrapping (0) | 2025.07.18 |