(번역) Offline React Query

content-logo

Table of Contents

본문

TkDodoOffline React Query를 번역한 문서입니다.

여러번 이야기 했듯이, React Query는 비동기 상태 관리자 입니다. 프로미스를 제공하기만 하면, 이것이 해결되었든 거부되었든, 라이브러리는 만족합니다. 그 프로미스가 어디서 왔는지는 중요하지 않습니다.

프로미스를 사용하는 사례는 많지만, 가장 큰 사례는 데이터를 불러오는 것입니다. 이는 종종 활성화된 네트워크 연결을 요구합니다. 하지만 때로는, 특히 네트워크 연결이 불안정할 수 있는 모바일 기기에서는, 네트워크 연결이 없어도 앱이 작동해야 할 필요가 있습니다.

v3에서의 이슈 (Issues in v3)

React Query는 오프라인 시나리오를 처리하는 데 매우 잘 갖추어져 있습니다. 캐싱 계층을 제공하기 때문에, 캐시가 채워져 있는 한, 네트워크 연결이 없어도 계속 작업할 수 있습니다. v3에서 예상대로 작동하지 않을 수 있는 몇 가지 극단적인 시나리오를 살펴보겠습니다. 설명을 위해 문서에 있는 기본적인 게시글 목록/게시글 상세 예제를 사용할 것입니다:

1) 캐시에 데이터가 없는 경우 (no data in the cache)

말했듯이 v3에서는 캐시가 채워져 있으면 잘 작동합니다. 다음과 같은 극단적인 시나리오에서는 동작이 이상해질 수 있습니다.

  • 네트워크 연결이 좋은 상태에서 목록 화면으로 이동합니다.
  • 연결이 끊어진 상태에서 게시글을 클릭합니다.
img-loading-forever

사진의 현상은 네트워크가 다시 연결될 때가지 쿼리가 loading 상태에 머무르게 되는 상황입니다. 또한, 브라우저 개발자 도구에서 실패한 네트워크 요청을 볼 수 있습니다. 이는 React Query가 항상 첫 번째 요청을 발생시키고, 그 요청이 실패하면 네트워크 연결이 없는 경우 재시도를 일시 중지하기 때문입니다.

또한, React Query Devtools는 쿼리가 fetching 상태라고 표시하지만, 이는 전혀 사실이 아닙니다. 쿼리는 실제로 paused 상태이지만, 이 상태를 나타낼 개념이 없어서 그렇게 표시했습니다. - 이는 숨겨진 구현 세부 사항입니다.

2) 재시도를 하지 않습니다. (no retries)

마찬가지로, 위 시나리오에서 재시도를 완전히 비활성화 했다면, 쿼리는 즉시 오류 상태로 전환되며, 이를 막을 방법은 없습니다.

img-network-error

네트워크 연결이 없는데 왜 pause를 위해 재시도 (retries)를 해야하나요 🤷‍♂️?

3) 네트워크가 필요없는 쿼리 (queries that don't need the network)

네트워크 연결이 필요없는 쿼리 (예를 들어, 웹 워커에서 값비싼 비동기 프로세싱을 진행하는 경우) 는 다른 이유로 실패했을 경우 네트워크가 다시 연결되기 전까지 일시정지 할 것입니다. 또한 해당 쿼리는 윈도우 포커스 시 실행되지 않을 것입니다. 왜냐하면 그 기능은 네트워크 연결이 없다면 완전히 비활성되기 때문입니다.


요약하자면, 두 가지 주요 문제가 있습니다: React Query가 네트워크 연결이 필요하지 않을 때도 필요하다고 가정하는 경우 (사례 3), React Query가 실행되지 않아야 할 쿼리를 실행하는 경우 (사례 1 및 2) 입니다.

새로운 네트워크모드 (The new NetworkMode)

v4에서 우리는 이 문제를 포괄적으로 해결하기 위해 networkMode 세팅을 새로 도입했습니다. 이를 통해 온라인 쿼리와 오프라인 쿼리를 명확하게 구분할 수 있습니다. 이는 useQueryuseMutation의 옵션으로, 전역적으로 또는 쿼리 별로 설정할 수 있습니다. 결국 네트워크 연결이 필요한 쿼리도 있고 그렇지 않은 쿼리도 있기 때문입니다.

온라인 (online)

이는 v4에서 등장한 새로운 기본 모드입니다. 우리는 대부분의 사용자들이 React Query를 데이터 불러오기와 결합하여 사용할 것으로 예상하고 있습니다. 간단히 말해서, 이 설정에서는 네트워크 연결이 활성화된 경우에만 쿼리가 실행될 수 있다고 가정합니다.

그렇다면 네트워크 연결이 필요한 쿼리를 네트워크 연결이 없는 상황에서 사용한다면 어떻게 될까요? 쿼리는 새로운 paused 상태로 진입할 것입니다. paused 상태는 쿼리가 가질 수 있는 주 상태인 loading, success 또는 error 상태의 부차적인 상태입니다. 왜냐하면 네트워크 연결은 언제나 끊길 수 있기 때문입니다.

예를 들어, 데이터를 성공적으로 가져왔지만 백그라운드에서 다시 불러오기가 일시 중지된 경우 success 상태와 paused 상태가 동시에 있을 수 있습니다.

또는, 쿼리가 처음 마운트될 때 loading 상태와 paused 상태가 동시에 있을 수 있습니다.

데이터 불러오기 상태 (fetchStatus)

우리는 항상 쿼리가 실행 중임을 나타내는 isFetching 플래그를 가지고 있었습니다. 새로운 paused 상태와 유사하게, 쿼리는 successfetching 상태가 동시에 될 수 있으며, errorfetching 상태가 동시에 될 수도 있습니다. 백그라운드에서 데이터 다시 불러오기로 인해 다양한 상태가 발생할 수 있습니다 (👋 상태 관리 도구).

fetchingpaused는 상호 배타적이기 때문에, 이들을 새로운 fetchStatus로 통합하여 이제 useQuery에서 반환됩니다:

  • fetching: 쿼리가 실제로 실행 중인 상태 - 요청이 진행 중입니다.
  • paused: 쿼리가 실행되지 않은 상태 - 연결이 회복될 때까지 일시 중지됩니다.
  • idle: 쿼리가 현재 실행중이지 않은 상태

일반적인 규칙으로, 쿼리의 status는 데이터에 대한 정보를 제공합니다: success는 항상 데이터가 있음을 의미하고, loading은 아직 데이터가 없음을 의미합니다.

반면에, fetchStatusqueryFn에 대한 정보를 제공합니다: 실행 중인지 아닌지 여부. isFetchingisPaused 플래그는 해당 상태에서 파생됩니다.


이제 위에서 언급한 사례 1이 v4에서 어떻게 보일 수 있는지 살펴보겠습니다. React Query 데브툴에 새로 추가된 네트워크 모드 토글 버튼에 주목해 주세요. 이 버튼은 실제로 네트워크를 끄지 않고, 테스트 목적으로 React Query가 네트워크가 없다고 믿게 만듭니다. 네, 저는 이 기능이 매우 자랑스럽습니다. 😊

img-network-paused

새로운 보라색 상태 배지 덕분에 쿼리가 있는 상태 (paused)를 명확하게 볼 수 있습니다. 또한, 네트워크를 다시 켜면 첫 번째 네트워크 요청이 이루어집니다.

항상 (always)

이 모드에서는, React Query는 여러분의 네트워크 연결을 전혀 신경쓰지 않습니다. 쿼리는 항상 실행될 것이며 절대로 일시정지하지 않을 것입니다. 이는 React Query를 데이터 불러오기 이외의 용도로 사용할 때 유용합니다.

오프라인 우선 (offlineFirst)

GitHub 저장소 API를 예로 들어 보겠습니다. 이 API는 다음과 같은 응답 헤더를 전송합니다:

cache-control: public, max-age=60, s-maxage=60

이는 다음 60초 동안 해당 리소스를 다시 요청하면 응답이 브라우저 캐시로부터 제공됨을 의미합니다. 멋진 점은 오프라인 상태에서도 이 기능이 작동한다는 것입니다! 예를 들어, 오프라인 우선 PWA를 위한 서비스 워커는 네트워크 요청을 가로채고 캐시된 응답이 있을 경우 이를 제공하는 방식으로 유사하게 작동합니다.

이제 React Query가 네트워크 연결이 없다고 판단하여 요청을 실행하지 않으면 이러한 것들이 작동하지 않을 것입니다. 기본적인 online 모드가 그러하듯이 말이죠. 데이터 불러오기 요청을 가로채려면 요청이 발생해야 합니다 :) 따라서 이러한 추가 캐시 레이어가 있는 경우, networkMode: 'offlineFirst'를 사용하도록 하세요.

첫 번째 요청이 나가고 캐시를 히트하면, 쿼리는 success 상태로 전환되고 해당 데이터를 받게 됩니다. 만약 캐시 미스가 발생하면 네트워크 오류가 발생할 가능성이 있으며, 그 후 React Query는 재시도를 일시 중지하여 쿼리를 paused 상태로 전환합니다. 이는 두 가지 장점을 모두 활용할 수 있는 최상의 방식입니다. 🙌

이 모든 것이 여러분에게 어떤 의미가 있을까요? (What does all of this mean for me, exactly?)

사실, 원하지 않으면 아무런 영향이 없습니다. 여전히 새로운 fetchStatus를 신경쓰지 않고 isLoading만 확인할 수 있습니다. 이 경우 React Query는 이전과 똑같이 동작할 것입니다 (사실, 위의 사례 2는 더 잘 작동할 것이며 네트워크 오류를 보지 않게 될 것입니다).

그러나 네트워크 연결이 없는 상황에서도 앱을 견고하게 만드는 것이 우선이라면, 이제는 공개된 fetchStatus를 사용하여 적절히 대응할 수 있는 옵션이 생겼습니다.

이 새로운 상태를 어떻게 활용할지는 여러분에게 달려 있습니다. 이 기능을 바탕으로 사람들이 어떤 UX를 만들어낼지 매우 기대됩니다. 🚀