[React-Query] on/offline 네트워크 상태 변경시마다 토스트UI 발생시키기

전세영·2024년 12월 29일
4


react query를 사용할 때 단순히 온라인일 것이다라고 단정짓고 개발을 해온 적이 많지만, 오프라인 상태일때에는 이를 안내하는 메시지를 띄운다든지, 오프라인동안의 요청은 나중에 온라인이 되었을때 처리한다든지 하는 기능까지 제공해야 더 안정감 있는 서비스를 할 수 있다고 생각합니다.

react-query는 네트워크 on/off 상태를 어떻게 관리하고 있을까요?

어떻게 사용하면 아래 서비스 요구사항을 만족시킬 수 있을까요?

요구사항 정의

리액트 쿼리를 이용하여 아래의 요구사항을 달성해보고자 합니다.

  1. 네트워크 중단시 발생한 요청은 보관해두었다가 online이 되었을 때 발생시키기

  2. 네트워크 on/off 상태 변경시마다 토스트UI 발생시키기

어떤 전략을 사용해야할까요?

시도 1. networkMode로 대략적인 동작을 선택할수있다.

리액트쿼리에서는 networkMode라는 변수를 useMutation이나 useQuery의 옵션으로 넘길 수 있습니다.
이 networkMode를 통해 온오프라인시 기본동작방식을 결정할수있습니다.

기본적으로 리액트쿼리는 오프라인상황에서는 모든 querymutation을 무시하는 전략을 사용합니다.

Network Mode : online(Default) | always | offlineFirst

online (Default): 오프라인상황에서 모든 querymutation을 무시한다.
always : 네트워크 상태가 오프라인이어도, 모든 querymutation을 항상 발생시킨다.
offlineFirst : 둘 사이의 중간입니다. 오프라인이어도 일단은 한번은 queryFn을 실행하고, retry를 수행하지 않습니다.

Default: 오프라인 시에 요청은 멈춰있다가, 온라인이 되면 요청이 발생한다.

online (Default)전략에서는 네트워크상태가 offline 상태일 때,
fetchStatuspaused가 되어 잠시 요청이 멈춰있다가,
온라인이 되면 요청이 다시 발생합니다.
따라서 리액트쿼리에서 디폴트옵션만으로 이미 1번 요구사항에 맞는 처리가 되고있습니다.

그럼 남은 건 네트워크 on/off 될 때 사용자에게 토스트 UI를 띄우는 것만 해줘도 충분하겠네요 !

나머지 옵션값들은 어떤상황에 쓸모가 있을까요?

공식문서에서는 아래와같이 설명해주고있습니다.

always : 실제 네트워크 연결이 필요하지않은 요청일때 의미가있습니다. 예를들면 AsyncStorage, ()=>Promise.resolve(5) 와 같은 것을 queryFn을 통해서 쓰고있는 경우.

offlineFirst : serviceWorker가 요청을 intercept해서 캐싱하고있는상황이나, offline-first PWA, 혹은 Cache-Control header로 HTTP 캐싱을 하고있는 경우.

따라서 이 옵션들은 그저 특수한 실행환경일 때에도, 정상적 동작을 하도록 보조해주기위한 의미가 있을 뿐

보편적인 offline시에 토스트 UI를 띄운다거나, 요청을 캐싱해뒀다 온라인시 발생시킨다거나 하는 요구사항을 충족시키는 것과는 목적이 달라 보입니다.

시도 2. onlineManager 사용하기

오프라인/온라인 전환 때 toast를 띄우고자한다면 react-query의 onlineManager를 이용할 수 있습니다.

onlineManager.subscribe((isOnline)=>{/* 원하는 동작*/});

방식으로 사용하며 이를 이용해 useNetwork라는 훅을 만들어줄 수 있습니다.

import { useSyncExternalStore } from "react";
function useNetwork() {
  const isOnline = useSyncExternalStore(onlineManager.subscribe, () => onlineManager.isOnline());
  return isOnline;
}
  return isOnline;
}
function Component() {
  const isOnline = useNetwork();
  console.log("isOnline", isOnline);
  return <div />;
}

네트워크 요청을 끊을때, 연결할때마다 정상적으로 console.log가 발생합니다.

react 입장에서 react-query의 onLineManager은 외부스토어이기 때문에, 외부스토어를 구독하기위해 제공되는 훅 useExternalStore를 사용해주었습니다.
useState와 useEffect의 조합으로도 같은 역할을 해줄 수는 있으나, SSR을 지원하고자할때, 혹은 동시성렌더링(startTransition)을 사용할때 티어링이 발생하는 문제가 있는 등. 엣지케이스에서 useExternalStore가 더 안정적이기때문에 가급적 외부스토어 구독시 useExternalStore를 사용해줍시다.

만약 console대신 토스트를 띄워주고자 한다면

function Component() {
  const isOnline = useNetwork();
  const {showToast} = useToast();
  useEffect(()=>{
  showToast(<div>{`네트워크가 ${isOnline} 상태입니다.`}   </div>);
  },[isOnline])
  return <div />;
}

useToast가 이미 구현되어있다고 할 때
이렇게 사용하면 네트워크 상태에 따라 토스트를 보여주는 로직을 만들 수 있겠습니다.

useNetwork()와 같은 훅을 구현할 때, 이전엔 navigator.onLine으로 구현하는 경우도 많았습니다만

react-query 공식문서에 따르면 navigator.onLine에는 chromium based browsers환경에서 수많은 false positive 이슈가 존재한다고 합니다. (실제론 offline 인데, online으로 인식하게 됨)
따라서 위와 같이 onlineManager를 이용해 구현하는 것이 좋아보입니다.

리액트쿼리의 onlineManager 또한 이전엔 내부적으로 navigator.onLine을 구독하였으나 이 문제로인해 방식이 바뀌었습니다.

결론

리액트 쿼리를 이용한 오프라인 요구사항을 충족시키는 법에대해 이야기해보았습니다.

  1. 네트워크 중단시 발생한 요청은 보관해두었다가 online이 되었을 때 발생시키기

이 요구사항은 이미 networkMode의 디폴트옵션인 online에서 자동으로 되고 있는 기능입니다.
만약 특정 Mutation에서는 이 기능을 끄고싶다면, 해당 뮤테이션에만 networkMode를 offlineFirst로 바꾸어서 이 기능을 꺼버릴 수도 있겠습니다.

  1. 네트워크 on/off 상태 변경시마다 토스트UI 발생시키기
    OnlineManager를 이용해 const isOnline = useNetwork()훅을 만들어 isOnline이 바뀔때마다 showToast()를 해주는 방법으로 구현할 수 있었습니다.
profile
가치를 빠르고 안전하게 전달하는 개발을 하고 싶습니다.

0개의 댓글

관련 채용 정보