React - React Query

김서영·2024년 6월 13일
0

CS 스터디 - React

목록 보기
27/28

React Query


리액트에서 서버에 데이터를 요청해서 가져오기, 데이터를 서버에 업데이트, 캐싱 작업 등을 하는데 쉽게 처리할 수 있도록 도와주는 라이브러리

=> 리액트에서 서버 상태를 관리하는데 도움을 주는 라이브러리

React Query의 등장 배경

React Query는 클라이언트 애플리케이션에서 서버 상태를 효율적으로 관리하고, 서버와의 데이터 동기화를 쉽게 하기 위해 등장했다.

1. 복잡한 서버 상태 관리 가능

전통적인 상태 관리 라이브러리(예: Redux, MobX)로 서버 데이터를 관리하는 것은 종종 복잡하고 반복적인 코드 작성을 요구했다.
서버 상태를 관리하는 데 필요한 페칭, 캐싱, 동기화, 에러 핸들링 등의 작업을 수작업으로 처리해야 했다.

2. 비효율적인 네트워크 요청

동일한 데이터를 여러 번 페칭하거나, 불필요한 네트워크 요청이 발생하는 문제가 있었다.
데이터가 갱신될 때마다 컴포넌트가 재렌더링되면서 성능 저하가 발생할 수 있었다.

3. 데이터의 일관성 문제

서버에서 가져온 데이터를 클라이언트에서 관리할 때 일관성을 유지하는 것이 어려웠다.
여러 컴포넌트에서 동일한 데이터를 사용할 때, 데이터의 변경 사항을 각 컴포넌트에 반영하는 것이 복잡했다.

위와 같은 문제들을 해결하기 위해 React Query가 등장하게 되었는데,

React Query의 장점

1. 간편한 데이터 페칭과 캐싱

useQuery 훅을 사용하여 데이터 페칭, 캐싱, 동기화를 간편하게 처리할 수 있다.
동일한 쿼리에 대해 여러 번 요청할 경우, 자동으로 캐싱된 데이터를 사용하여 네트워크 요청을 최소화한다.

2. 자동화된 상태 관리

로딩 상태, 에러 상태, 데이터 상태를 자동으로 관리하여, 복잡한 상태 관리를 간소화한다.
데이터가 갱신되면 자동으로 컴포넌트를 리렌더링하여 최신 데이터를 표시한다.

3. 백그라운드 데이터 갱신

백그라운드에서 데이터를 자동으로 갱신하여, 사용자가 항상 최신 데이터를 볼 수 있게 한다.
폴링, 리페치, 쿼리 무효화 등의 기능을 제공하여 데이터의 일관성을 유지한다.

4. 효율적인 에러 핸들링

데이터 페칭 중 발생하는 에러를 쉽게 처리하고, 사용자에게 적절한 피드백을 제공할 수 있다.

5. 개발자 경험 향상

선언적인 API를 제공하여, 코드의 가독성을 높이고 유지보수성을 향상시킨다.
DevTools를 통해 쿼리 상태를 시각적으로 확인하고 디버깅할 수 있다.

6. 서버 상태와 클라이언트 상태의 분리

서버 상태를 클라이언트 상태와 분리하여, 각각의 상태를 독립적으로 관리할 수 있다.
서버 상태에만 집중할 수 있도록 설계되어, 클라이언트 상태 관리 라이브러리와도 잘 통합된다.

React Query의 구성 요소

1. QueryClient

쿼리를 관리하는 중심 객체로, 쿼리 캐시와 관련된 설정을 담당한다.

import { QueryClient, QueryClientProvider } from 'react-query';

const queryClient = new QueryClient();

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <ExampleComponent />
    </QueryClientProvider>
  );
}

2. useQuery

데이터를 가져오기 위해 사용하는 훅으로, 쿼리 키와 fetch 함수를 인자로 받아 쿼리 키에 해당하는 데이터를 가져오고 캐시한다.

import { useQuery } from 'react-query';

function ExampleComponent() {
  const { data, error, isLoading } = useQuery('repoData', fetchRepoData);

  async function fetchRepoData() {
    const response = await fetch('https://api.github.com/repos/tannerlinsley/react-query');
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    return response.json();
  }

  if (isLoading) return 'Loading...';

  if (error) return `An error has occurred: ${error.message}`;

  return (
    <div>
      <h1>{data.name}</h1>
      <p>{data.description}</p>
    </div>
  );
}

4. useQueries

여러 개의 쿼리를 동시에 처리할 때 사용하는 React Query 훅이다.
각 쿼리에 대한 설정을 배열로 전달하며, 각 쿼리의 결과는 배열로 반환된다. 이를 통해 여러 데이터를 병렬로 가져오고, 각각의 상태와 결과를 관리할 수 있다.

import { useQueries } from 'react-query';

function ExampleComponent() {
  const results = useQueries([
    {
      queryKey: ['repoData1'],
      queryFn: () => fetchRepoData('https://api.github.com/repos/tannerlinsley/react-query'),
    },
    {
      queryKey: ['repoData2'],
      queryFn: () => fetchRepoData('https://api.github.com/repos/facebook/react'),
    }
  ]);

  async function fetchRepoData(url) {
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    return response.json();
  }

  return (
    <div>
      {results.map((result, index) => {
        if (result.isLoading) return <p key={index}>Loading...</p>;
        if (result.error) return <p key={index}>An error has occurred: {result.error.message}</p>;
        return (
          <div key={index}>
            <h1>{result.data.name}</h1>
            <p>{result.data.description}</p>
          </div>
        );
      })}
    </div>
  );
}

4. useMutation

데이터를 생성, 업데이트, 삭제할 때 사용하는 훅이다.
useMutation은 mutate 함수를 반환하며, 이를 사용하여 변이 작업을 수행할 수 있다.

import { useMutation, useQueryClient } from 'react-query';

function ExampleComponent() {
  const queryClient = useQueryClient();

  const mutation = useMutation(newTodo => fetch('/api/data', {
    method: 'POST',
    body: JSON.stringify(newTodo),
  }), {
    onSuccess: () => {
      // 쿼리 무효화
      queryClient.invalidateQueries('todos');
    },
  });

  function handleAddTodo() {
    mutation.mutate({ title: 'New Todo' });
  }

  return (
    <div>
      <button onClick={handleAddTodo}>Add Todo</button>
    </div>
  );
}

5. QueryCache

쿼리의 결과를 캐싱하는 객체로, 캐싱된 데이터에 접근하거나 업데이트할 수 있다.
QueryClient의 인스턴스를 생성할 때 내부적으로 사용되며, 개발자가 직접 사용할 일은 많지 않지만 필요에 따라 커스터마이징이 가능하다.

import { QueryCache, QueryClient } from 'react-query';

const queryCache = new QueryCache({
  onError: (error, query) => {
    console.log(`Query ${query.queryKey} failed: ${error.message}`);
  },
  onSuccess: (data, query) => {
    console.log(`Query ${query.queryKey} succeeded: ${data}`);
  },
});

const queryClient = new QueryClient({ queryCache });

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <ExampleComponent />
    </QueryClientProvider>
  );
}

6. QueryClientProvider

QueryClient 인스턴스를 리액트 컴포넌트 트리에 제공하는 역할을 한다.

import { QueryClient, QueryClientProvider } from 'react-query';

const queryClient = new QueryClient();

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <ExampleComponent />
    </QueryClientProvider>
  );
}
profile
개발과 지식의 성장을 즐기는 개발자

0개의 댓글