목록으로

TanStack Query로 API 호출 컴포넌트 테스트하기

TanStack Query란?

TanStack Query(이전 명칭: React Query)는 서버 상태(Server State) 관리를 쉽게 해주는 라이브러리입니다. 클라이언트에서 API로 호출해 데이터를 가져오고, 이를 UI에 표시하고, 데이터가 변경되는 시점에 맞춰서 다시 불러오고, 에러 상황을 처리하고, 로딩 상태를 보여주는 등 비동기 로직을 체계적으로 관리할 수 있도록 도와줍니다.
클라이언트 상태는 UI 요소가 클릭되거나, 사용자 입력이 들어오거나, 특정 이벤트가 일어나는 등 브라우저 내부(클라이언트 측)에서 발생하는 상태입니다. 주로 useState, useReducer, Context API 등으로 관리합니다.
서버 상태는 API 호출 결과로 서버에서 내려주는 데이터입니다. 이 데이터는 서버 상황에 따라 달라지므로, 업데이트 주기나 캐싱, 불필요한 호출 방지 등 특별한 관리가 필요합니다.
TanStack Query는 서버 상태를 캐싱하고, 필요할 때 자동으로 업데이트해 주며, 에러나 로딩 등도 알아서 처리할 수 있도록 해주어 클라이언트 상태에 적용할 수 있게 도와줍니다.
이번 편은 TanStack Query 사용하는 방법을 안다는 전제로 서술합니다.

컴포넌트 예시

아래는 외부 API를 호출해 사용자 정보를 보여주는 간단한 컴포넌트입니다. TanStack Query의 useQuery 훅을 사용하여 데이터를 가져오고, 로딩 중/에러 발생/데이터 준비 상태를 구분하여 표시합니다.
UserProfile 컴포넌트는 Props로 userId 를 숫자값을 받아서 JSONPlaceholder로부터 가짜 데이터를 받아오는 동작을 합니다.
첫 번째 인자인 useQuery(['user', userId], ...)는 Query Key로써, 캐싱에 사용됩니다. ['user', userId]userId가 변경될 때마다 캐시 상태가 달라지도록 합니다. API 호출 결과 fetchUserData가 성공하면 data에 담겨서 user라는 이름으로 사용됩니다. 실패하면 isErrortrue가 됩니다.
실무에서는 다양한 에러 상황(네트워크 에러, 서버 응답이 4xx 혹은 5xx 등)에 따른 분기 처리나, 대기 시간(Loading)이 길어질 때 로딩 스피너 표시, 재시도 횟수 설정, 캐싱 만료 시간(ttl) 설정 등이 필요합니다.

Vitest + React Testing Library 환경에서의 테스트 전략

외부 의존성과 부수 효과(side effect)를 다루는 방법

TanStack Query는 내부적으로 API 요청(fetch)을 수행하며, 이는 네트워크 호출이라는 부수 효과입니다. 이를 테스트할 때 중요한 점이 몇 가지 있습니다.
API 호출 자체는 대개 의존성 주입이나 모킹 처리합니다. 실제로 API를 호출하지 않고, 테스트 시에는 가짜 응답을 반환해야 합니다. 그렇지 않으면 네트워크 상태나 외부 서버 상태에 따라 테스트가 실패하거나, 테스트가 느려질 수 있습니다.
실제 API 요청 시, 완료될 때까지는 시간이 걸립니다. 따라서 테스트 코드는 비동기 작업이 완료될 때까지 기다렸다가 검증해야 합니다.

QueryClientProvider 설정

테스트 환경에서도 TanStack Query가 동작하려면 QueryClientProvider가 필요합니다. 일반적으로 테스트 유틸 함수를 만들어두고, 해당 유틸 함수가 컴포넌트를 render할 때 QueryClientProvider로 감싸는 방식을 사용합니다.
종종 테스트마다 새로운 QueryClient 인스턴스를 생성하는 것이 안전할 수 있습니다. 한 테스트에서 캐시가 남아 다른 테스트에 영향을 줄 수 있기 때문입니다. 테스트 케이스가 복잡해지면 다음과 같이 매번 새 인스턴스를 만들 수도 있습니다.

테스트 코드 작성 방법 및 예시

1) global.fetch Mocking

테스트에서 API 호출을 모킹 처리한다면, API 호출에 사용되는 global.fetch를 가짜 함수(vi.fn())로 교체하고, 필요할 때마다 원하는 응답을 지정합니다.
맨 윗 줄에 있는 /* global global */는 ESLint가 global 객체가 global 객체라는 사실을 모르기 때문에 사람이 코드로 알려준 것입니다. 이게 없으면 global이라는 변수가 없다는 ESLint 오류가 발생합니다.
TanStack Query 테스트 시 주의할 점은 TanStack Query는 기본적으로 3회 재시도를 한다는 점입니다. 테스트 중 불필요한 재시도를 막으려면 retry: false 설정을 하거나, mock에서 바로 실패 응답을 주면 됩니다.

MSW(Mock Service Worker) 사용 예시

global.fetch를 매번 직접 mock하는 대신, 네트워크 수준에서 API 요청을 가로채 처리하는 MSW 라이브러리를 사용할 수도 있습니다.
MSW는 실제 API 통신 흐름과 동일한 방식으로 테스트하므로 테스트 검증에 있어서 신뢰도가 상승합니다. 실제 API 통신 흐름과 동일하므로 시간초과, 특정 HTTP 상태값, 네트워크 지연 상황을 더 쉽고 정확하게 시뮬레이션할 수 있습니다.
다음은 예시입니다.
이러한 핸들러를 설정하고, setupTests.js에서 setupServer를 통해 MSW 서버를 구동하면, 테스트에서 실제 fetch 호출이 일어나더라도 MSW가 이를 가로채서 지정한 응답을 반환합니다.
토이스토리 2기 모집 중!
푸딩캠프 뉴스레터를 구독하면 학습과 성장, 기술에 관해 요약된 컨텐츠를 매주 편하게 받아보실 수 있습니다.
목차