
제가 처음 React를 접했을 때 가장 낯설던 개념은 상태(state)였습니다.
저는 개념이 이해되지 않아 상태 관련된 코드를 직접 짜보며 억지로 이해했는데요..
이 글을 읽는 분들은 저와 같은 가시밭길을 걷지 않길 바라며 자세하게 작성해봤습니다..!
우리 모두 서버 상태관리의 마스터가 되어봐요~!😋

상태(State)란, "컴포넌트가 기억하는 값"이다.
버튼을 "클릭 했는지", 모달이 "열려있는지", 입력창에 "뭘 썼는지" 등 이런 "값"들을 관리하는 것이 상태 관리이다.
상태는 크게 두 종류로 나뉜다.
위에서 언급한 것 처럼 UI와 관련된 상태가 있고, 그와 별개로 서버에서 가져오는 데이터가 있다.
그렇다면, 서버 데이터의 상태란 뭘까?
게시글 목록, 유저 프로필, 상품 정보처럼 API를 호출해서 받아오는 데이터가 바로 서버 상태다.
이 데이터를 '언제 가져오고', '언제 다시 가져오고', '어떻게 저장할지' 관리하는 것이 서버 상태 관리다.
두 환경의 상태 차이를 표로 정리하자면 아래와 같다.

클라이언트(client) 상태는 내가 완전히 제어할 수 있다.
하지만, 서버(server) 상태는 아래와 같다.
정리하자면, 서버(server) 상태는 서버가 언제든 바뀔 수 있고, 내 앱은 그걸 가져다 쓰는 입장이다.
즉, 내가 완전히 제어 "불가능"한 상태이다.
fetch + useState 만으로 서버 데이터를 다룰때, 주로 아래와 같은 코드를 작성했다.

데이터를 하나 가져오는데, state가 3개다. ( 🤢 🤮 )
그리고 이 방식은 아래와 같은 한계들이 있다
위의 한계들을 해결하기 위해 나온 것이 Tanstack Query(React Query)이다.
공식 문서의 표현을 가져오면 아래와 같다.
서버 상태를 fetching, caching, synchronizing, updating하는 라이브러리
참고
Tanstack Query 라이브러리는 "서버 데이터를 가져오고, 캐싱하고, 최신 상태로 유지해주는 도구"이다.
참고로 원래 이름은 React Query였다.
React 외의 다양한 프레임워크(Vue, Angular, Svelte 등)를 지원하면서 Tanstack Query로 이름을 바꾸었다.
React 환경에서 사용시 @tanstack/react-query를 설치하여 사용하면 된다.
참고
취준시 JD에 Tanstack Query과 React Query를 혼용해서 작성되는 경우가 많다.
같은 라이브러리이지만, 혹시 모르니 JD 요구 기술 스택에 맞게 서류를 꼼꼼히 수정하자..!
이제부터 Tanstack Query 사용법을 알아보겠다.
Tanstack Query를 사용하려면, 먼저 앱 전체를 QueryClientProvider로 감싸야한다.

QueryClient캐시를 관리하는 핵심 인스턴스.
모든 쿼리의 데이터가 여기에 저장된다.
QueryClientProviderQueryClient를 Context로 앱 전체에 전달한다.
React의 Context API와 동일한 방식이다.
참고
Context란?
React에서 컴포넌트 간 데이터를 공유하기 위한 객체다.
createContext()로 생성하며,Provider로 감싼 하위 컴포넌트 어디서든 해당 데이터에 접근할 수 있다.
React의 Context API란?
React의 컴포넌트 트리 전체에 데이터를 전달하는 기능이다.
일반적으로props는 부모 → 자식으로만 데이터를 전달할 수 있지만(props drilling 발생),Context를 사용하면 중간 컴포넌트를 거치지 않고 원하는 컴포넌트에 바로 데이터를 전달할 수 있다.
캐시의 기준점
Tanstack Query는 모든 캐시를 Query Key를 기준으로 "식별"한다.
Query Key가 같으면 같은 데이터로 취급한다.

Query Key는 "배열 형태"로 작성한다.
예시 이미지를 보면 키의 형식이 2가지이다.
고정된 값이다. 항상 동일한 데이터를 가져올 때 사용한다.

변하는 값이 포함된 키이다. 특정 데이터를 하나씩 식별할 때 사용한다.

이미지 속 예시 코드인 ['posts', 1]과 ['posts', 2]는 서로 다른 캐시이다.
queryKey는 동일하지만, 배열의 두번째 요소(id)가 다르기 때문이다.
동적인 키가 필요한 이유는 다음과 같다.
['posts'])은 하나지만, ['posts', 1], ['posts', 2])는 id마다 데이터가 다르다.즉, id별로 캐시를 따로 관리해야 하므로, 배열에 유니크한 값인 id를 추가해 키를 구분한다.
React의 useEffect 의존성 배열과 비슷한 개념이라 생각하면 된다.
postId가 바뀌면 Query Key도 바뀌고, 이를 기반으로 Tanstack Query가 자동으로 새 데이터를 요청한다.
Query Key 덕분에 같은 데이터를 여러 컴포넌트에서 요청해도
실제 API 호출은 한 번만 일어난다.
데이터를 얼마나 믿을건지
캐시된 데이터를 언제까지 "신선하다"고 볼건지, "언제 지울 건지"를 제어하는 두가지 옵션이다.
표로 표현하자면 아래와 같다.

참고
gcTime은 v4까지cacheTime이라는 이름이었다. v5에서gcTime으로 변경되었다.

staleTime를 5분으로 설정하면, 같은 쿼리를 5분 안에 다시 호출해도 API 요청 없이 캐시 데이터를 바로 반환한다.
각 모듈의 작동 흐름은 아래와 같다.
queryFn 실행 (API 요청)staleTime 5분 타이머 시작queryFn 재실행 (API 재요청) → 새 데이터로 교체stale 상태여도 빈 화면이 아니라 기존 캐시를 먼저 보여주면서 백그라운드에서 갱신한다.gcTime 만료 → 캐시 메모리에서 삭제queryFn 재실행자 이제 기본 작동 원리는 파악이 됐다.
이제 useQuery에 대해 설명해보겠다.
Tanstack Query에서 데이터를 가져올 때 사용하는 핵심 훅(Hook)
참고
훅(Hook)이란?
- 함수 컴포넌트에서 React의 state, lifecycle 등을 사용할 수 있게 해주는 함수.
- React의 state, lifecycle을 사용하면 클래스 컴포넌트 없이 함수 컴포넌트만으로 구현이 가능하다. 클래스 컴포넌트는 this, constructor 등 보일러플레이트가 많고 코드가 복잡해지는 단점이 있었지만, 훅의 등장으로 함수 컴포넌트로 동일한 기능을 구현하고, 코드가 간결해졌다.
- use로 시작하는 컨벤션이 있다.(useState, useEffect, useQuery ...)

queryKey : 캐시 키.queryFn : 데이터를 가져오는 비동기 함수. fetch, axios 상관 없이 Promise를 반환하면 된다.useQuery가 반환하는 주요 값들을 예시 코드로 작성해보았다.

참고
v5에서 isLoading이 isPending으로 변경되었다.
두 이름이 혼용되는 글들이 많다!
실제로 사용 예정인 '게시글 목록을 불러오는 기능' 예제 코드다.
게시글 목록 : Feed
게시글 : Post
1 Feed : N Post
먼저 feed의 api 메소드 코드 예시이다.

다음은 api 메소드를 queryFn에 넣은 실제 컴포넌트 코드 예시이다.

위에서 fetch + useState 예시(0-4)보다 코드가 훨씬 짧아지고, Tanstack Query가 캐싱과 중복 요청 방지를 알아서 처리해준다.

이번 글에서 다룬 내용을 정리하면 아래와 같다.
QueryClient가 캐시를 관리하고, QueryKey가 캐시를 식별한다.staleTime, gcTime으로 캐시의 신선도와 수명을 제어한다.useQuery로 데이터를 가져오고, data/isLoading/isError로 상태를 다룬다.참고
정합성이란?
서버와 클라이언트(혹은 DB) 간의 데이터가 서로 일치하고 모순이 없는 상태를 의미한다.
이제 Tanstack Query를 제대로 활용하기 위해
데이터를 변경할 때 사용하는 useMutation, 캐시를 직접 건드리는 useQueryClient를 다뤄볼 예정이다.