🔥 네트워크 연결 없이 동작하는 React Query
강의 목차
React Query는 비동기 상태 관리 도구입니다. Promise만 제공하면 React Query는 그 Promise가 해결되든 거부되든 상관없이 잘 작동합니다. Promise의 출처는 중요하지 않습니다.
Promise를 만드는 방법은 여러 가지가 있지만, 가장 흔한 용도는 데이터 가져오기입니다. 대부분의 경우 이를 위해 네트워크 연결이 필요합니다. 하지만 때로는, 특히 네트워크 연결이 불안정할 수 있는 모바일 기기에서는 네트워크 없이도 앱이 작동해야 합니다.
v3의 문제점
React Query는 오프라인 상황을 잘 다룰 수 있습니다. 캐시 계층을 제공하기 때문에, 캐시가 채워져 있다면 네트워크 연결이 없어도 계속 작업할 수 있습니다. 하지만 v3에서 예상대로 작동하지 않는 몇 가지 특수한 경우를 살펴보겠습니다. 설명을 위해 문서에 있는 기본적인 게시물 목록/상세 보기 예제를 사용하겠습니다.
1) 캐시에 데이터가 없는 경우
앞서 말씀드렸듯이, v3에서는 캐시가 채워져 있으면 잘 작동합니다. 하지만 다음과 같은 특수한 상황에서는 이상하게 동작할 수 있습니다:
- 네트워크 연결이 좋은 상태에서 목록 화면으로 이동합니다.
- 연결이 끊긴 상태에서 게시물을 클릭합니다.
이 경우, 네트워크 연결이 복구될 때까지 쿼리는 '로딩' 상태에 머무릅니다. 또한 브라우저 개발자 도구에서 실패한 네트워크 요청을 볼 수 있습니다. React Query는 항상 첫 번째 요청을 보내고, 실패하면 네트워크 연결이 없을 때 다시 시도하는 것을 일시 중지하기 때문입니다.
더불어, React Query 개발자 도구는 쿼리가 '가져오는 중'이라고 표시하지만, 이는 완전히 정확하지 않습니다. 실제로 쿼리는 '일시 중지' 상태지만, 이 상태를 나타내는 개념이 없어서 숨겨진 구현 세부 사항이 됩니다.
2) 재시도가 없는 경우
마찬가지로, 위 상황에서 재시도를 완전히 끈 경우, 쿼리는 즉시 오류 상태로 전환되며 이를 막을 방법이 없습니다.
네트워크 연결이 없을 때 쿼리를 '일시 중지'하려면 왜 '재시도'가 필요할까요? 이해하기 어려운 부분입니다.
3) 네트워크가 필요 없는 쿼리
네트워크 연결이 필요 없는 쿼리(예: 웹 워커에서 비용이 많이 드는 비동기 처리를 수행하는 경우)도 다른 이유로 실패하면 네트워크 연결이 복구될 때까지 일시 중지됩니다. 또한 네트워크 연결이 없으면 이 기능이 완전히 비활성화되기 때문에 윈도우에 포커스가 갈 때 이러한 쿼리가 실행되지 않습니다.
요약하자면, 두 가지 주요 문제가 있습니다. 어떤 경우에는 React Query가 네트워크 연결이 필요하다고 가정하지만 실제로는 그렇지 않을 수 있고(경우 3), 다른 경우에는 React Query가 아마도 하지 말아야 할 때 쿼리를 실행합니다(경우 1과 2).
새로운 NetworkMode
v4에서는 새로운 networkMode
설정으로 이 문제를 전체적으로 해결하려 했습니다. 이를 통해 '온라인' 쿼리와 '오프라인' 쿼리를 명확히 구분할 수 있습니다. 이 옵션은 useQuery
와 useMutation
모두에 적용할 수 있어, 전역적으로 설정하거나 쿼리별로 설정할 수 있습니다. 결국, 네트워크 연결이 필요한 쿼리와 그렇지 않은 쿼리가 혼재할 수 있기 때문입니다.
online
v4에서 새롭게 도입된 기본 모드입니다. 대부분의 사용자가 React Query를 데이터 가져오기와 함께 사용할 것으로 예상하기 때문입니다. 간단히 말해, 이 설정에서는 쿼리가 활성 네트워크 연결이 있을 때만 실행될 수 있다고 가정합니다.
그렇다면 네트워크 연결이 필요한 쿼리를 연결 없이 실행하려고 하면 어떻게 될까요? 쿼리는 새로운 '일시 중지' 상태로 전환됩니다. 이 '일시 중지' 상태는 쿼리가 가질 수 있는 주요 상태인 '로딩', '성공', '오류'에 부차적인 상태입니다. 언제든지 네트워크 연결을 잃을 수 있기 때문입니다.
즉, '성공' 상태이면서 동시에 '일시 중지' 상태일 수 있습니다. 예를 들어, 한 번 데이터를 성공적으로 가져왔지만 백그라운드 새로고침이 일시 중지된 경우입니다.
또는 쿼리가 처음 마운트될 때 '로딩' 상태이면서 '일시 중지' 상태일 수 있습니다.
fetchStatus
우리는 항상 쿼리가 실행 중임을 나타내는 isFetching
플래그를 가지고 있었습니다. 새로운 '일시 중지' 상태와 마찬가지로, 쿼리는 '성공'이면서 '가져오는 중'일 수 있고, '오류'이면서 '가져오는 중'일 수도 있습니다. 백그라운드 새로고침으로 인해 매우 많은 상태가 가능해집니다(상태 머신이 필요한 시점입니다).
'가져오는 중'과 '일시 중지'는 서로 배타적이므로, 우리는 이들을 새로운 fetchStatus
로 통합했습니다. 이제 useQuery
에서 반환됩니다:
- '가져오는 중': 쿼리가 실제로 실행 중입니다 - 요청이 진행 중입니다.
- '일시 중지': 쿼리가 실행되지 않습니다 - 연결이 복구될 때까지 일시 중지됩니다.
- '대기 중': 쿼리가 현재 실행되고 있지 않습니다.
규칙을 간단히 말하자면, 쿼리의 '상태'는 '데이터'에 대한 정보를 제공합니다: '성공'은 항상 데이터가 있다는 뜻이고, '로딩'은 아직 데이터가 없다는 뜻입니다.
반면에 fetchStatus
는 queryFn
에 대한 정보를 제공합니다: 실행 중인지 아닌지를 알려줍니다. isFetching
과 isPaused
플래그는 이 상태에서 파생됩니다.
위에서 언급한 경우 1이 v4에서 어떻게 보일지 살펴보겠습니다. RQ 개발자 도구에 새로 추가된 네트워크 모드 토글 버튼을 주목해주세요. 실제로 네트워크를 끄는 것이 아니라 React Query가 테스트 목적으로 네트워크가 없다고 '믿게' 만듭니다. 이 기능이 꽤 마음에 듭니다. 😊
새로운 보라색 상태 배지 덕분에 쿼리가 어떤 상태('일시 중지')에 있는지 명확히 볼 수 있습니다. 또한 네트워크를 다시 켜면 첫 번째 네트워크 요청이 이루어집니다.
always
이 모드에서 React Query는 네트워크 연결 상태를 전혀 고려하지 않습니다. 쿼리는 항상 실행되며 절대 일시 중지되지 않습니다. 이는 React Query를 데이터 가져오기 '이외의' 용도로 사용할 때 가장 유용합니다.
offlineFirst
이 모드는 React Query v3의 작동 방식과 매우 유사합니다. 첫 번째 요청은 '항상' 이루어지며, 실패하면 재시도가 일시 중지됩니다. 이 모드는 React Query 위에 브라우저 캐시와 같은 추가 캐싱 계층을 사용할 때 유용합니다.
예를 들어 GitHub 저장소 API를 살펴보겠습니다. 다음과 같은 응답 헤더를 보냅니다:
cache-control: public, max-age=60, s-maxage=60
text
이는 다음 60초 동안 해당 리소스를 다시 요청하면 응답이 브라우저 캐시에서 올 것임을 의미합니다. 이 방식의 좋은 점은 오프라인 상태에서도 작동한다는 것입니다! 서비스 워커(예: 오프라인 우선 PWA)도 비슷한 방식으로 작동하여 네트워크 요청을 가로채고 사용 가능한 캐시된 응답을 제공합니다.
기본 online
모드처럼 React Query가 네트워크 연결이 없다는 이유로 요청을 보내지 '않기로' 결정한다면 이런 기능들은 작동하지 않을 것입니다. 가져오기 요청을 가로채려면 요청이 반드시 일어나야 합니다. :) 따라서 이런 추가 캐시 계층이 있다면 networkMode: 'offlineFirst'
를 사용해야 합니다.
첫 번째 요청이 나가고 캐시에 적중하면 좋습니다. 쿼리는 '성공' 상태가 되고 그 데이터를 얻게 됩니다. 캐시 미스가 발생하면 아마도 네트워크 오류가 발생할 것이고, 그 후 React Query는 재시도를 일시 중지하여 쿼리를 '일시 중지' 상태로 만듭니다. 이는 두 가지 장점을 모두 가진 방식입니다. 🙌
이 모든 것이 정확히 어떤 의미가 있나요?
원하지 않는다면 아무것도 바꿀 필요가 없습니다. 새로운 fetchStatus
를 무시하고 isLoading
만 확인하기로 결정할 수 있습니다 - React Query는 이전과 똑같이 작동할 것입니다(실제로 경우 2는 네트워크 오류가 보이지 않기 때문에 더 잘 작동할 것입니다).
하지만 네트워크 연결이 없는 상황에서도 앱을 견고하게 만드는 것이 중요하다면, 이제 노출된 fetchStatus
에 반응하고 그에 따라 행동할 수 있는 옵션이 있습니다.
이 새로운 상태로 무엇을 할지는 여러분에게 달려 있습니다. 사람들이 이를 바탕으로 어떤 사용자 경험을 만들어낼지 기대됩니다.