1. 쿼리 상태와 페치 상태
React Query는 현재 상태를 알려주는 플래그를 제공한다.
내부 상태 머신에서 파생된 여러가지 boolean 플래그 값을 제공하는데,
선언된 타입을 보면 쿼리
상태, 페치
상태가 각각 3가지씩 존재한다.

쿼리
상태 : 데이터(캐시)에 대한 정보success
: 쿼리가 성공했고, 이에 대한data
가 쿼리캐시에 설정된다.error
: 쿼리가 실패했고,error
가 설정된다.pending
: 쿼리캐시에 데이터가 없다.
페치
상태: Fn에 대한 정보
fetching
:queryFn
요청이 실행중이고 데이터를 기다리고 있다.paused
:queryFn
,mutationFn
이 실행중이지 않고 다시 인터넷에 연결될 때 까지 일시중지된 상태이다.(offline)idle
:~Fn
이 실행중이 아니다.
쿼리상태와 페치상태는 서로 독립적이다.
또한 각 상태 내부에서는 하나의 상태만 가질 수 있다.
즉 fetching
이면서 success
일 수 있고, fetching
이면서 error
일 수 있지만,
pending
이면서 success
일 수는 없다!


위는 내부 상태 머신에서 파생된 여러가지 boolean 플래그 값이다.
캐시 상태정보인 쿼리상태와 함수 상태정보인 페치상태를 잘 알고있다면 적절히 써먹을 수 있다.
참고로 isLoading
은 isPending && isFetching
을 의미한다. (캐시가 없고, 요청중)
즉 그 쿼리의 첫번째 fethcing
이라는 의미이다.
2. 표준 예시
const todos = useTodos()
if (todos.isPending) {
return 'Loading...'
}
if (todos.error) {
return 'An error has occurred: ' + todos.error.message
}
return <div>{todos.data.map(renderTodo)}</div>
위 예시는 쿼리가 요청중인지, 에러인지를 순서대로 확인하고 데이터를 렌더링한다.
대부분의 경우 괜찮지만 React Query가 제공하는 백그라운드 페칭(refetchOnMount, refetchOnWindowFocus 그리고 refetchOnReconnect) 때문에 UX가 나빠질 수 있다.
어떤 문제가 발생하는지 알아보자.
3. 백그라운드 에러
많은 상황에서 백그라운드 페칭이 실패하면 조용히 무시될 수 있지만, 위 코드에서는 불가능하다.
일어날 수 있는 상황을 보자.
- 사용자가 페이지를 열고 첫번째 쿼리가 성공한다. 이후 잠시동안 다른 탭에서 작업하다가 페이지로 다시 돌아오면 React Query가 백그라운드 페칭을 한다. 그리고 페칭이 실패한다.
- 사용자가 아이템 목록 페이지에 들어온다. 아이템 하나를 골라 클릭한다. 쿼리가 성공하고 목록 페이지로 돌아온다. 이제 다시 그 아이템 페이지로 들어가면 캐시에 남아있는 데이터를 볼 것이다. 하지만 이때 백그라운드 페칭이 실패한다.
위 두 상황에서 쿼리의 상태는 다음과 같다.
{
"status": "error",
"error": { "message": "Something went wrong" },
"data": [{ ... }]
}
보다시피 사용 가능한 오래된 데이터와 에러가 공존한다.
이는 오래된 캐싱을 사용하면서 유효성을 다시 검사하는 (stale-while-revalidate caching) 메커니즘을 이용하기 때문이다.
즉 오래된 데이터라 할지라도, 존재한다면 보여준다.
이제 다시 표준예시를 살펴보면..
const todos = useTodos()
if (todos.isPending) {
return 'Loading...'
}
// 캐시가 남아있지만 백그라운드 페칭 때 에러가 발생한다.
if (todos.error) {
return 'An error has occurred: ' + todos.error.message
}
return <div>{todos.data.map(renderTodo)}</div>
캐시가 남아있지만 백그라운드 페칭 때 데이터 대신 에러가 렌더링되는 문제가 발생한다.
이제 어떤 것을 우선해서 보여줄지는 우리에게 달려있다.
에러를 보여주는게 중요할까? 오래된 데이터가 있는 경우 데이터만 표시하는 것으로 충분할까?
아니면 둘 다 표시하고 에러는 토스트로 보여주면 어떨까?
구현은 요구사항에 따라 달라질 것이므로 이 질문에 대한 명확한 답변은 없다.
하지만 위 두가지 예시를 고려할때 렌더링 되어있는 캐시 데이터가 갑자기 에러 화면으로 바뀐다면 혼란스러운 사용자 경험을 줄 수 있을 것 같다.
React Query가 실패한 쿼리는 세번까지 다시 시도하므로, 에러 화면으로 대체될 때까지 몇 초가 걸릴 수 있기 때문에 더 문제가 될 수도 있다. 사용자는 그 몇 초동안 아무런 피드백을 받을 수 없기 때문이다.
이러한 이유로 캐시 데이터를 먼저 확인하는 방법이 있다.
const todos = useTodos()
if (todos.data) {
return <div>{todos.data.map(renderTodo)}</div>
}
if (todos.error) {
return 'An error has occurred: ' + todos.error.message
}
return 'Loading...'
다시 말하지만 구현은 요구사항에 따라 달라지기 때문에 "옳은 방법"은 없다.
다만 개발자로서 React Query의 공격적인 리페칭이 가져올 수 있는 결과를 알고있어야한다.
Reference
https://tanstack.com/query/latest/docs/reference/QueryClient#queryclientisfetching
https://tkdodo.eu/blog/status-checks-in-react-query
'React Query' 카테고리의 다른 글
7. Query Options API (0) | 2024.07.31 |
---|---|
6. React Query가 필요한 이유 (0) | 2024.07.21 |
4. 효율적으로 React Query Key 선언하기 (5) | 2024.02.14 |
3. React Query와 에러핸들링 (2) | 2024.02.08 |
2. Form에서 React Query 잘 활용하기 (0) | 2024.01.22 |
1. 쿼리 상태와 페치 상태
React Query는 현재 상태를 알려주는 플래그를 제공한다.
내부 상태 머신에서 파생된 여러가지 boolean 플래그 값을 제공하는데,
선언된 타입을 보면 쿼리
상태, 페치
상태가 각각 3가지씩 존재한다.

쿼리
상태 : 데이터(캐시)에 대한 정보success
: 쿼리가 성공했고, 이에 대한data
가 쿼리캐시에 설정된다.error
: 쿼리가 실패했고,error
가 설정된다.pending
: 쿼리캐시에 데이터가 없다.
페치
상태: Fn에 대한 정보
fetching
:queryFn
요청이 실행중이고 데이터를 기다리고 있다.paused
:queryFn
,mutationFn
이 실행중이지 않고 다시 인터넷에 연결될 때 까지 일시중지된 상태이다.(offline)idle
:~Fn
이 실행중이 아니다.
쿼리상태와 페치상태는 서로 독립적이다.
또한 각 상태 내부에서는 하나의 상태만 가질 수 있다.
즉 fetching
이면서 success
일 수 있고, fetching
이면서 error
일 수 있지만,
pending
이면서 success
일 수는 없다!


위는 내부 상태 머신에서 파생된 여러가지 boolean 플래그 값이다.
캐시 상태정보인 쿼리상태와 함수 상태정보인 페치상태를 잘 알고있다면 적절히 써먹을 수 있다.
참고로 isLoading
은 isPending && isFetching
을 의미한다. (캐시가 없고, 요청중)
즉 그 쿼리의 첫번째 fethcing
이라는 의미이다.
2. 표준 예시
const todos = useTodos()
if (todos.isPending) {
return 'Loading...'
}
if (todos.error) {
return 'An error has occurred: ' + todos.error.message
}
return <div>{todos.data.map(renderTodo)}</div>
위 예시는 쿼리가 요청중인지, 에러인지를 순서대로 확인하고 데이터를 렌더링한다.
대부분의 경우 괜찮지만 React Query가 제공하는 백그라운드 페칭(refetchOnMount, refetchOnWindowFocus 그리고 refetchOnReconnect) 때문에 UX가 나빠질 수 있다.
어떤 문제가 발생하는지 알아보자.
3. 백그라운드 에러
많은 상황에서 백그라운드 페칭이 실패하면 조용히 무시될 수 있지만, 위 코드에서는 불가능하다.
일어날 수 있는 상황을 보자.
- 사용자가 페이지를 열고 첫번째 쿼리가 성공한다. 이후 잠시동안 다른 탭에서 작업하다가 페이지로 다시 돌아오면 React Query가 백그라운드 페칭을 한다. 그리고 페칭이 실패한다.
- 사용자가 아이템 목록 페이지에 들어온다. 아이템 하나를 골라 클릭한다. 쿼리가 성공하고 목록 페이지로 돌아온다. 이제 다시 그 아이템 페이지로 들어가면 캐시에 남아있는 데이터를 볼 것이다. 하지만 이때 백그라운드 페칭이 실패한다.
위 두 상황에서 쿼리의 상태는 다음과 같다.
{
"status": "error",
"error": { "message": "Something went wrong" },
"data": [{ ... }]
}
보다시피 사용 가능한 오래된 데이터와 에러가 공존한다.
이는 오래된 캐싱을 사용하면서 유효성을 다시 검사하는 (stale-while-revalidate caching) 메커니즘을 이용하기 때문이다.
즉 오래된 데이터라 할지라도, 존재한다면 보여준다.
이제 다시 표준예시를 살펴보면..
const todos = useTodos()
if (todos.isPending) {
return 'Loading...'
}
// 캐시가 남아있지만 백그라운드 페칭 때 에러가 발생한다.
if (todos.error) {
return 'An error has occurred: ' + todos.error.message
}
return <div>{todos.data.map(renderTodo)}</div>
캐시가 남아있지만 백그라운드 페칭 때 데이터 대신 에러가 렌더링되는 문제가 발생한다.
이제 어떤 것을 우선해서 보여줄지는 우리에게 달려있다.
에러를 보여주는게 중요할까? 오래된 데이터가 있는 경우 데이터만 표시하는 것으로 충분할까?
아니면 둘 다 표시하고 에러는 토스트로 보여주면 어떨까?
구현은 요구사항에 따라 달라질 것이므로 이 질문에 대한 명확한 답변은 없다.
하지만 위 두가지 예시를 고려할때 렌더링 되어있는 캐시 데이터가 갑자기 에러 화면으로 바뀐다면 혼란스러운 사용자 경험을 줄 수 있을 것 같다.
React Query가 실패한 쿼리는 세번까지 다시 시도하므로, 에러 화면으로 대체될 때까지 몇 초가 걸릴 수 있기 때문에 더 문제가 될 수도 있다. 사용자는 그 몇 초동안 아무런 피드백을 받을 수 없기 때문이다.
이러한 이유로 캐시 데이터를 먼저 확인하는 방법이 있다.
const todos = useTodos()
if (todos.data) {
return <div>{todos.data.map(renderTodo)}</div>
}
if (todos.error) {
return 'An error has occurred: ' + todos.error.message
}
return 'Loading...'
다시 말하지만 구현은 요구사항에 따라 달라지기 때문에 "옳은 방법"은 없다.
다만 개발자로서 React Query의 공격적인 리페칭이 가져올 수 있는 결과를 알고있어야한다.
Reference
https://tanstack.com/query/latest/docs/reference/QueryClient#queryclientisfetching
https://tkdodo.eu/blog/status-checks-in-react-query
'React Query' 카테고리의 다른 글
7. Query Options API (0) | 2024.07.31 |
---|---|
6. React Query가 필요한 이유 (0) | 2024.07.21 |
4. 효율적으로 React Query Key 선언하기 (5) | 2024.02.14 |
3. React Query와 에러핸들링 (2) | 2024.02.08 |
2. Form에서 React Query 잘 활용하기 (0) | 2024.01.22 |