[번역] #13: 오프라인 리액트 쿼리

2022 react 00

Thanks for @TkDodo

해당 컨텐츠는 원글을 번역한 것입니다. 오타, 오역 지적은 환영이에요!


#13: Offline React Query

전 계속 말하고 있어요. React Query는 async state manager라고요. Promise만 있으면 resolved 혹은 rejected되므로 라이브러리를 쉽게 쓸 수 있습니다. Promise가 어디에 존재하는지는 중요하지 않아요.

Promise를 생성하는 방법은 여러 가지가 있지만, 가장 많은 use-case는 data fetching입니다. 대부분 네트워크 연결이 활성화되어야 합니다. 그러나 종종, 특히 네트워크 연결을 신뢰할 수 없는 모바일 장치에서는 네트워크가 없어도 없어도 앱이 작동해야 합니다.

Issues in v3

React Query는 오프라인 시나리오를 다룰 수 있도록 좋은 장비를 많이 갖추고 있습니다. 왜냐하면 캐시 레이어를 제공하면서 캐시가 채워져 있다면 네트워크 연결이 되지 않아도 계속 동작할 수 있기 때문이에요. 이제 저희의 예상과 달리 v3가 동작하지 않는 edge-case를 몇 개 설명하겠습니다. 전 우리의 기본적인 post list와 detail post에 대한 예제를 사용할 겁니다.

1) no data in the cache

말씀드렸듯이 v3에서는 캐시가 채워져 있는 한 모든 것이 잘 동작합니다. 뭔가 이상한 edge-case는 다음과 같아요.

  • 네트워크 연결이 양호한 상태에서 list view로 이동합니다.
  • 연결이 끊기고 게시물을 클릭합니다.

연결을 다시 시작할 때까지 쿼리가 loading 상태로 유지됩니다. 또한 브라우저의 devtools에서 실패한 네트워크 요청을 볼 수 있죠. 이는 React Query가 항상 첫 번째 요청을 실패하면 네트워크 연결이 없을 때 재시도를 일시중지하기 때문입니다.

또한 React Query Dev Tools는 쿼리를 fetching 함을 보여주지만 꼭 그런 것은 아닙니다. 쿼리는 실제로 일시 중지되었지만 해당 상태를 나타낼 수 있는 개념이 없습니다. 이게 숨겨져 있는 세부 구현 내용입니다.

2) no retries

마찬가지로 위의 시나리오에서 재시도를 모두 해제한 경우 쿼리는 즉시 오류 상태로 전환되며 이를 멈출 방법은 없습니다.

https://tkdodo.eu/blog/network-error.gif

네트워크에 연결되지 않았는데 쿼리를 일시 중지하기 위해 재시도해야 하는 이유는 무엇인가요 🤷?

3) queries that don’t need the network

네트워크 연결이 필요하지 않은 쿼리(예: web worker에서 비용이 높은 비동기 처리를 수행하므로)는 어떤 문제에 의해 실패하는 경우, 네트워크 연결을 다시 시작할 때까지 일시 중지됩니다. 또한 네트워크 연결이 없는 경우 해당 기능이 완전히 비활성화되므로 이러한 쿼리는 window 포커스에서 실행되지 않습니다.


요약하자면, 크게 두 가지 문제가 있습니다. 경우에 따라 React Query는 네트워크 연결이 필요할 수도 있고(case 3) 그렇지 않을 수도 있지만(case 1과 2) React Query는 쿼리를 다시 시작한다는 거죠.

The new NetworkMode

v4에서는 새로운 networkMode 설정을 사용하여 해당 문제를 해결하려고 했습니다. 이는 전역 또는 쿼리별로 useQueryuseMutation에 대한 옵션을 설정하고, online 쿼리와 offline 쿼리를 명확하게 구분할 수 있음을 의미합니다. 결국 네트워크 연결이 필요한 쿼리와 그렇지 않은 쿼리가 있을 수 있는 거죠.

online

v4에 추가된 기본 모드입니다. 대부분의 사용자가 React Query를 data fecthing에 사용할 테니까요. 간단히 말하자면 쿼리는 네트워크 연결이 활성화된 경우에만 실행될 수 있다고 가정하는 것입니다.

그러면 네트워크 연결이 없을 때 쿼리를 실행하려는 경우 어떻게 될까요? 당연히 쿼리는 새로운 일시중지(paused) 상태로 전환됩니다. 일시중지 상태는 네트워크 연결이 언제든지 끊길 수 있으므로 쿼리가 idle, loadingsuccess 또는 error 상태에 있을 수 있다고 알리는 보조적인 수단입니다.

즉, 여러분은 성공 상태이자 일시중지 상태일 수 있습니다. 그 예로 단 한 번 데이터가 성공적으로 fetch 되었으나 background refetch는 일시중지되어 버리는 경우가 있겠죠.

또는 처음 쿼리가 마운트되면 로드 상태에 있다가 일시 중지될 수 있습니다.

fetchStatus

우리는 쿼리가 실행 중임을 나타내는 isFetching 플래그를 항상 가지고 있었습니다. 새로운 paused 상태는 쿼리가 success하는 fetching 이거나 error가 발생하는 fetching과 비슷합니다. Background refetches는 (👋  상태 기계)에 있을 수 있는 많은 상태를 제공합니다.

이제 서로 배타적이던 fetchingpaused는 useQuery에서 반환되는 새 fetchStatus로 통합했습니다.

  • fetching: 실제로 쿼리가 수행되어 요청이 진행 중입니다.
  • paused: 쿼리가 실행되고 있지 않습니다. 연결을 다시 시작할 때까지 일시중지됩니다.
  • idle: 현재 쿼리가 실행되고 있지 않습니다.

경험상 쿼리 상태는 데이터에 대한 정보를 제공합니다. 성공이란 항상 데이터가 있음을 의미하며, 로딩은 데이터가 아직 없음을 의미합니다. loading 상태를 pending 중으로 변경하는 것을 고려했지만, 이것은 아마 “너무 깨는” 것 같아요 😅

반면에 fetchStatusqueryFn의 실행 여부에 대한 정보를 제공합니다. isFetching 및 isPaused 플래그는 해당 상태에서 파생돼죠.


위에서 본 case 1이 v4에서 어떻게 보일 수 있는지 살펴보죠. React Query devtools의 새로운 네트워크 모드 토글 버튼에 주목하십시오. 네트워크를 실제로 끄지 않기 때문에 너무 멋지죠. 단순히 테스트 목적으로 React Query에게 네트워크가 없다고 믿게 만들 뿐입니다. 맞아요, 저는 이게 너무 자랑스러요 😊

https://tkdodo.eu/blog/paused.gif

우리는 새로운 보라색 상태 배지(paused)로 인해 쿼리의 상태를 명확하게 볼 수 있죠. 또한 네트워크를 다시 키는 경우 첫 번째 네트워크 요청이 이루어집니다.

always

이 모드에서는 React Query가 네트워크 연결에 전혀 영향을 주지 않습니다. 항상 쿼리가 실행되며 일시중지되지 않죠. 이 기능은 data fetch 이외의 작업에 React Query를 사용할 때 유용합니다.

offlineFirst

이 모드는 v3에서 React Query가 작동하는 방식과 매우 유사합니다. 첫 번째 요청은 항상 수행되며, 이 요청이 실패할 경우 재시도가 일시 중지됩니다. 이 모드는 React Query 위에 브라우저 캐시와 같은 추가 캐싱 계층을 사용하는 경우 유용합니다.

GitHub repo API를 예로 들어볼까요? 아래의 응답 헤더를 전송합니다.

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

즉, 다음 60초 동안 해당 리소스를 다시 요청하면 브라우저 캐시에서 응답이 반환됩니다. 이게 깔끔한 이유는 오프라인 상태에서도 작동한다는 것이죠! 최초의 오프라인 PWA와 같은 Service workers는 네트워크 요청을 가로채고, 이용 가능한 경우 캐시된 응답을 전달하는 것과 유사한 방식으로 동작합니다.

기본적인 online 모드처럼 네트워크 연결이 없기 때문에 React Query가 요청을 실행하지 않기로 결정하면 이러한 작업이 수행되지 않습니다. 이 작업을 수행해야 fetching 요청을 가로챌 수 있죠. 추가 캐시 계층이 있는 경우 offlineFirst networkMode를 사용해야 합니다.

첫 번째 요청이 나가고 캐시를 누르면 쿼리가 성공 상태로 전환되고 해당 데이터가 표시됩니다. 캐시 누락이 있는 경우 네트워크 오류가 발생할 수 있으며, 이 오류가 발생하면 React Query가 재시도를 멈추고 쿼리를 일시중지 상태로 전환합니다. 둘 다 최고에요 🙌

What does all of this mean for me, exactly?

당신이 원하지 않으면, 새로운 fetchStatus를 무시하고 React Query를 이전과 동일하게 isLoading으로 동작하도록 결정할 수 있습니다(case 2에서는 네트워크 오류를 볼 수 없기 때문에 더 잘 동작해요).

그러나 앱을 견고하게 만드는 것이 우선이라면, 이제 네트워크 연결이 없는 상황에서도 노출된 fetchStatus에 반응하여 행동할 수 있는 옵션을 사용해주세요.

새로운 기능으로 무엇을 할지는 당신에게 달려 있습니다. 전 사람들이 이것들 위에 어떤 UX를 만들지 기대돼요 🚀


Written by@InSeong-So
꿈을 향하는 개발자입니다. 생산성, 효율성, 자동화, 깊이 있는 공부에 관심이 많습니다.

GitHub