React Query 작성 중

먼지·2022년 9월 30일
0

React Query

목록 보기
1/4
post-thumbnail

React Query란?

리액트 쿼리는 종종 React의 missing data-fetching 라이브러리로 설명되지만 보다 기술적으로 React 애플리케이션에서 서버 상태를 가져오기, 캐싱, 동기화 및 업데이트를 쉽게 수행할 수 있게 해주는 리액트 라이브러리.

Motivation

리액트 컴포넌트에서 데이터를 가져오거나 업데이트하는 명확한 방법이 없어서 보통 React hooks나 범용적인 상태관리 라이브러리를 사용해 앱 전체에 비동기 데이터를 저장하고 제공함. 대부분의 상태 관리 라이브러리는 client state 조작에는 적합하지만 비동기 또는 server state 작업에는 좋지 않음.

우선 상태란 언제든 변경될 수 있는 것으로, 애플리케이션의 상태는 데이터에 대한 제어, 소유 여부에 따라 server stateclient state로 분류할 수 있음

Client state

  • 클라이언트가 제어, 소유하는 데이터
  • 브라우저에서 관리되는 동기적인 상태
  • 로컬에서만 사용자가 변경할 수 있음

local client state, global client state 두 가지로 구분할 수 있음

local

  • 모달 상태, form input values, active nav link 등

global

  • theme, locale, 현재 인증된 사용자 (앱에서 여러 위치에서 재사용됨)

Server state

  • 서버로부터 불러오는 데이터로 비동기적인 상태
  • client가 제어하거나 소유하지 않은 위치에서 원격으로 유지됨
  • 비동기 요청을 통해 fechingupdating이 가능함
  • 소유권을 공유. 사용자 모르게 다른 사람이 변경할 수 있음
  • 주의하지 않으면 애플리케이션에서 잠재적으로 "오래된" 상태가 될 수 있음

앱의 서버 상태의 특성을 파악하면 다음과 같이 많은 문제가 발생함

  • 캐싱... (아마도 프로그래밍에서 가장 어려운 일)
  • 백그라운드에서 "오래된" 데이터를 업데이트
  • 가능한 한 신속하게 데이터 업데이트 반영
  • 서버 상태의 메모리 및 garbage collection 관리

React Query는 서버 상태를 관리하기 위한 최고의 라이브러리 중 하나! 구성 없이 즉시 사용 가능하고 여러 장점이 있음

  • 클라이언트에서 서버 데이터 캐싱을 효율적으로 관리해 줌
  • pagination과 infinite scroll(Infinite Querie)도 쉽게 처리
  • error가 발생할 경우 자동으로 재시도, 데이터가 오래됐다 판단되면 다시 get

Queries

https://react-query-v3.tanstack.com/guides/queries
https://react-query-v3.tanstack.com/reference/useQuery#_top

  • 서버에서 데이터를 패칭하기 위한 api. 사용 중인 메서드가 서버의 데이터를 수정하는 경우 (post, update) Mutations 을 사용하는 것이 좋음
  • unique key는 앱 전체에서 쿼리를 다시 가져오고, 캐싱하고, 공유하기 위해 사용되며 문자열과 배열을 받음.

주요 status

  • isLoading or status === 'loading' - 쿼리에 데이터가 없고 현재 데이터를 요청 중
  • isError or status === 'error' - 쿼리에서 에러가 발생한 경우
    - error - 해당 property로 에러 메세지를 확인
  • isSuccess or status === 'success' - 쿼리 요청이 성공했고 데이터를 사용할 수 있음
    - data - 쿼리가 success 상태인 경우 이 속성으로 데이터를 확인할 수 있음
  • isIdle or status === 'idle' - 이 쿼리는 현재 비활성화됨. 사용할 수 없을 때
  • isFetching - 모든 상태에서, 쿼리가 언제든지 가져오는 경우 (background refetching 포함) isFetching은 항상 true를 리턴함
 import { useQuery } from 'react-query'
 
 function App() {
   const info = useQuery('todos', fetchTodoList)
 }

예시

boolean 타입으로 체크

function Todos() {
   const { isLoading, isError, data, error } = useQuery('todos', fetchTodoList)
 
   if (isLoading) {
     return <span>Loading...</span>
   }
 
   if (isError) {
     return <span>Error: {error.message}</span>
   }
 
   // We can assume by this point that `isSuccess === true`
   return (
     <ul>
       {data.map(todo => (
         <li key={todo.id}>{todo.title}</li>
       ))}
     </ul>
   )
 }

status로 한 번에 처리 가능

function Todos() {
   const { status, data, error } = useQuery('todos', fetchTodoList)
 
   if (status === 'loading') {
     return <span>Loading...</span>
   }
 
   if (status === 'error') {
     return <span>Error: {error.message}</span>
   }
 
   // also status === 'success', but "else" logic works, too
   return (
     <ul>
       {data.map(todo => (
         <li key={todo.id}>{todo.title}</li>
       ))}
     </ul>
   )
 }

Query Keys

  • 기본적으로 React Qeury는 query key를 기반으로 쿼리 캐싱(query caching)을 관리함
  • 쿼리 키는 간단한 문자열이어도 되고, 중첩된 객체나 배열처럼 복잡해도 됨
  • query key를 순차적 진행을 보장하는 직렬화 기법으로, 쿼리의 데이터는 고유

String-Only Query Keys

key의 가장 간단한 형식은 배열이 아니라 개별 문자열임. string query key가 전달되면 해당 문자열이 쿼리 키의 유일한 항목으로 내부적으로 배열로 변환됨. 이 형식은 다음 경우에 유용함

  • Generic List/Index resources
  • Non-hierarchical resources 비계층적 자원?
// A list of todos
 useQuery('todos', ...) // queryKey === ['todos']
 
 // Something else, whatever!
 useQuery('somethingSpecial', ...) // queryKey === ['somethingSpecial']

Array Keys

쿼리에 데이터를 고유하게 설명하기 위해 추가 정보가 필요한 경우 문자열 및 임의의 수의 직렬화 가능한 객체가 있는 배열를 사용하여 해당 데이터를 설명할 수 있음

 // An individual todo
 useQuery(['todo', 5], ...)
 // queryKey === ['todo', 5]
 
 // An individual todo in a "preview" format
 useQuery(['todo', 5, { preview: true }], ...)
 // queryKey === ['todo', 5, { preview: true }]
 
 // A list of todos that are "done"
 useQuery(['todos', { type: 'done' }], ...)
 // queryKey === ['todos', { type: 'done' }]
Query Keys are hashed determin

Query Keys are hashed deterministically!

즉, 객체의 키 순서에 관계없이 다음 쿼리는 모두 동일한 것으로 간주됨

 useQuery(['todos', { status, page }], ...)
 useQuery(['todos', { page, status }], ...)
 useQuery(['todos', { page, status, other: undefined }], ...)

그러나 다음 쿼리 키는 같지 않음. 배열 항목 순서 중요

useQuery(['todos', status, page], ...)
useQuery(['todos', page, status], ...)
useQuery(['todos', undefined, page, status], ...)

If your query function depends on a variable, include it in your query key

Query Key는 가져올 데이터를 고유하게 설명하므로 쿼리 함수에 사용하는 모든 변수를 포함해야 함. 파라미터를 사용할 수 있고 파라미터가 unique 한 값이라면 인자로 받아 아래와 같이 사용할 수 있음

function Todos({ todoId }) {
   const result = useQuery(['todos', todoId], () => fetchTodoById(todoId))
 }

Mutations

https://react-query-v3.tanstack.com/guides/mutations
데이터를 create/update/delete 하거나 server side-effects를 수행할 때 사용됨. 이를 위해 React Query는 useMutation hook을 내보냄

다음은 서버에 새로운 todo를 추가하는 예시

 function App() {
   const mutation = useMutation(newTodo => {
     return axios.post('/todos', newTodo)
   })
 
   return (
     <div>
       {mutation.isLoading ? (
         'Adding todo...'
       ) : (
         <>
           {mutation.isError ? (
             <div>An error occurred: {mutation.error.message}</div>
           ) : null}
 
           {mutation.isSuccess ? <div>Todo added!</div> : null}
 
           <button
             onClick={() => {
               mutation.mutate({ id: new Date(), title: 'Do Laundry' })
             }}
           >
             Create Todo
           </button>
         </>
       )}
     </div>
   )
 }

mutation은 주어진 순간에 다음 상태 중 하나만 있을 수 있음

  • isIdle또는 status === 'idle'- 돌연변이가 현재 idle 상태이거나 fresh/reset된 상태
  • isLoading또는 status === 'loading'- mutation가 현재 실행 중
  • isError또는 status === 'error'- mutation에 오류가 발생
  • isSuccess또는 status === 'success'- mutation 성공했고 돌연변이 데이터를 사용할 수 있음
profile
꾸준히 자유롭게 즐겁게

0개의 댓글