TanStack Query

TanStack Query vs Zustand: 서버 상태와 클라이언트 상태 차이

2026.01.28·수정 2026.05.10·약 7분

둘 다 상태를 다루는 도구처럼 보여 API 데이터까지 Zustand에 넣거나, 모달 상태까지 Query로 관리하려는 혼란이 생깁니다.

둘 다 상태를 다루지만 출발점이 다릅니다

TanStack Query vs Zustand: 서버 상태와 클라이언트 상태 차이 첫 번째 설명 다이어그램

TanStack Query와 Zustand는 모두 화면의 값을 다룬다는 점에서 비슷해 보입니다. 하지만 TanStack Query가 주로 다루는 값은 서버에서 온 데이터입니다. 상품 목록, 사용자 정보, 게시글 상세처럼 원본이 서버에 있고 다시 가져오거나 동기화해야 하는 값입니다. Zustand는 모달 열림 여부, 선택한 탭, 임시 필터, 장바구니 UI 상태처럼 클라이언트 안에서 결정되는 값을 다루기에 잘 맞습니다.

서버 상태는 캐시와 동기화가 핵심입니다

서버 상태는 내 앱만 바꾼다고 끝나지 않습니다. 다른 사용자가 데이터를 바꿀 수도 있고, 시간이 지나면 다시 가져와야 할 수도 있습니다. 그래서, refetch 같은 기준이 필요합니다. TanStack Query는 이 문제를 위해 만들어진 도구입니다. API 요청 책임을 나누는 기준은 TanStack Query queryFn 사용법, 캐시 시간은 TanStack Query staleTime gcTime 차이와 이어집니다.

const productsQuery = useQuery({ queryKey: ['products', category], queryFn: () => fetchProducts(category)}); const isFilterOpen = useUiStore((state) => state.isFilterOpen);

클라이언트 상태는 화면 안의 의사결정에 가깝습니다

클라이언트 상태는 서버에 원본이 없거나, 서버와 즉시 동기화할 필요가 없는 값입니다. 로그인 모달이 열렸는지, 사이드바가 접혔는지, 사용자가 선택한 필터 패널이 무엇인지 같은 값입니다. 이런 값까지 Query 캐시에 넣으면 데이터 요청 도구와 UI 제어가 섞입니다. Zustand는 이런 공유 UI 상태를 작고 명확하게 나누는 데 잘 맞습니다. Zustand의 기본 관점은 Zustand란 무엇인가에서 먼저 잡을 수 있습니다.

같이 쓸 때는 소유권을 나눠야 합니다

TanStack Query vs Zustand: 서버 상태와 클라이언트 상태 차이 핵심 기준 정리

상품 목록은 TanStack Query가 가져오고, 사용자가 고른 정렬 옵션이나 필터 패널 열림 상태는 Zustand가 들고 있는 식으로 나눌 수 있습니다. 다만 필터 값이 URL 검색 파라미터와 연결되어야 한다면 또 다른 기준이 필요합니다. 핵심은 값의 원본이 어디인지 묻는 것입니다. 서버가 원본이면 Query, 브라우저 안의 UI 판단이면 Zustand, URL로 공유되어야 하면 search params를 고려합니다.

잘못 섞으면 갱신 기준이 흐려집니다

API 응답을 Zustand에 복사해두면 새 데이터가 들어왔을 때 어떤 값이 최신인지 헷갈립니다. 반대로 모달 상태를 Query에 넣으면 요청과 관계없는 리렌더링 기준이 생깁니다. 두 도구를 같이 쓰는 것 자체가 문제는 아닙니다. 역할이 겹치게 쓰는 것이 문제입니다. 서버 데이터, 클라이언트 UI 상태, URL 상태를 나눠 적어보면 어떤 도구가 맡아야 하는지 대부분 정리됩니다.

상태의 주인을 먼저 정하면 도구 선택이 쉬워집니다

상태를 나눌 때 가장 먼저 볼 질문은 “이 값의 원본이 어디인가”입니다. 서버에 원본이 있는 값은 시간이 지나면 바뀔 수 있고, 다시 가져와야 하며, 실패나 로딩 상태도 함께 다뤄야 합니다. 이런 값은 TanStack Query가 맡는 것이 자연스럽습니다. 반면 브라우저 안에서만 결정되는 값은 Zustand나 가 더 단순합니다.

예를 들어 상품 목록 데이터는 Query가 가져오고 캐싱합니다. 사용자가 필터 패널을 열었는지, 목록 보기 방식을 카드형으로 볼지 리스트형으로 볼지는 클라이언트 상태입니다. 필터 조건 자체가 URL에 들어가야 공유 가능한 페이지가 된다면 search params까지 고려합니다. 이렇게 서버 상태, 클라이언트 상태, URL 상태를 나누면 도구가 겹치지 않습니다.

실무에서는 한 도구로 모든 상태를 처리하려는 순간 문제가 커집니다. API 응답을 Zustand에 복사해두면 최신 데이터 기준이 흐려지고, 모달 열림 여부를 Query로 관리하면 요청과 관계없는 상태까지 캐시처럼 다루게 됩니다. 역할을 좁게 쓰는 것이 오히려 코드가 단순합니다.

마지막에는 독자가 바로 적용할 기준을 남깁니다

TanStack Query vs Zustand: 서버 상태와 클라이언트 상태 차이을 읽는 독자는 단순한 정의보다 “그래서 지금 내 코드에서는 무엇을 먼저 확인해야 하는가”를 알고 싶어 합니다. 그래서 글을 작성할 때는 개념을 설명한 뒤 바로 판단 질문으로 이어지는 흐름이 필요합니다. 이 값은 어디에서 만들어졌는지, 어느 컴포넌트나 페이지가 책임지는지, 배포나 빌드 뒤에도 같은 방식으로 동작하는지처럼 실제 작업에서 바로 확인할 수 있는 질문을 남겨야 합니다.

또 하나 중요한 기준은 기존 글과의 거리입니다. 이미 자세히 다룬 문법이나 오류 해결 절차를 다시 길게 반복하면 새 글의 역할이 흐려집니다. 이 글은 독자가 흩어진 글을 어떤 순서로 읽을지 알려주는 입구이거나, 서로 다른 개념을 연결하는 브릿지여야 합니다. 세부 구현은 관련 글로 넘기고, 현재 글에서는 왜 그 글을 그 시점에 읽어야 하는지 설명하는 것이 더 좋습니다.

실제 작성 과정에서는 예시를 하나 정해 끝까지 밀고 가는 편이 자연스럽습니다. 카드 UI, 블로그 상세 페이지, 장바구니 상태, 코딩테스트 입력 배열처럼 작은 상황 하나를 잡고 설명하면 개념이 공중에 뜨지 않습니다. 독자가 자신의 코드에 대입할 수 있는 기준이 생기면, 글은 단순 요약이 아니라 다음 작업을 결정하는 안내문 역할을 하게 됩니다.

마지막 정리는 “무엇을 외울 것인가”보다 “무엇부터 확인할 것인가”로 끝내는 것이 좋습니다. 처음 볼 파일, 먼저 나눌 상태, 우선 점검할 설정, 다음에 읽을 기존 글을 짧게 안내하면 독자가 바로 움직일 수 있습니다. 이런 마무리가 있어야 허브 글과 브릿지 글이 기존 글을 반복하지 않고도 충분한 가치를 가집니다.

발행 전에는 중복과 연결 흐름을 한 번 더 봅니다

이 글은 단독으로 읽혀도 의미가 있어야 하지만, 동시에 기존 글로 독자를 보내는 안내 역할도 해야 합니다. 그래서 발행 전에는 제목이 기존 글과 너무 비슷하지 않은지, 본문에서 이미 발행된 글의 설명을 그대로 반복하지 않았는지, 내부 링크가 실제 다음 행동으로 이어지는지 확인합니다.

특히 허브 글은 자세한 구현보다 순서와 판단 기준이 더 중요합니다. 독자가 글을 다 읽은 뒤 어떤 기존 글을 먼저 열어야 하는지, 어떤 문제를 만나면 어떤 체크리스트로 이동해야 하는지 알 수 있어야 합니다. 이 기준이 분명하면 비슷한 주제의 글이 많아도 서로 경쟁하지 않고 블로그 안에서 길을 만들어줍니다.

읽는 순서를 정하면 글도 덜 흩어집니다

TanStack Query vs Zustand: 서버 상태와 클라이언트 상태 차이의 핵심은 모든 개념을 한 번에 외우는 것이 아니라, 실제 작업에서 마주치는 순서대로 판단 기준을 세우는 데 있습니다. 먼저 작은 화면이나 문제 하나를 기준으로 시작하고, 필요한 순간에 다음 글로 넘어가면 학습 흐름이 끊기지 않습니다.

이 글이 마음에 드세요?

RSS 피드를 구독하세요!

“TanStack Query vs Zustand: 서버 상태와 클라이언트 상태 차이”에 대한 1개의 생각

댓글 남기기