[React] React Query 10분만에 뿌수기

olwooz·2022년 12월 22일
0

React

목록 보기
2/8

React Query란?

redux, recoil, mobx, zustand와 같은 라이브러리들이 클라이언트 상태 관리 라이브러리라면 React Query는 서버 상태 관리 라이브러리, 즉 API를 호출해 서버에서 비동기로 가져오는 데이터들을 관리하기 쉽게 만들어주는 라이브러리다.

사용하는 이유

클라이언트 상태 관리 라이브러리나 ReactuseState를 사용해 서버 데이터를 관리하면 비동기 처리 등의 로직이 번거롭고 복잡하다. 간단한 예시로 서버에서 데이터를 받아오기 전까지 로딩 컴포넌트를 띄워주는 코드를 작성해보겠다.

  1. React Query 사용 X
// without React Query
import React, { useState, useEffect } from 'react';

function MyComponent() {
  const [data, setData] = useState(null);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    async function fetchData() {
      setIsLoading(true);
      try {
        const response = await fetch('https://api.example.com/users');
        const data = await response.json();
        setData(data);
      } catch (error) {
        setError(error);
      }
      setIsLoading(false);
    }
    fetchData();
  }, []);

  if (isLoading) {
    return <p>Loading...</p>;
  }

  if (error) {
    return <p>Error: {error.message}</p>;
  }

  return (
    <ul>
      {data.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}
  1. React Query 사용 O
// with React Query
import { useQuery } from 'react-query';

function MyComponent() {
  const { data, isLoading, error } = useQuery('users', () => fetch('https://api.example.com/users'));

  if (isLoading) {
    return <p>Loading...</p>;
  }

  if (error) {
    return <p>Error: {error.message}</p>;
  }

  return (
    <ul>
      {data.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

코드가 훨씬 간결하고 직관적으로 변한 모습이다.
이 외에도 캐싱, 메모리 성능 최적화 등 많은 장점이 있다.
이 글에선 React Query에서 가장 자주 사용되는 두 가지 기능을 간략하게 소개할 것이다.

1. useQuery

데이터를 READ 할 때 사용한다.

import { useQuery } from 'react-query';

function MyComponent() {
  const { data, isLoading, error } = useQuery('users', () => fetch('https://api.example.com/users'));

  if (isLoading) {
    return <p>Loading...</p>;
  }

  if (error) {
    return <p>Error: {error.message}</p>;
  }

  return (
    <ul>
      {data.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

상단의 예시에서 useQuery'users' string을 첫 번째 인자로, () => fecth() 함수를 두 번째 인자로 받는다.

첫 번째 인자는 queryKey라는 값인데, query마다 유니크한 값을 지정해 React Query로 하여금 query의 상태와 결과 등을 추적할 수 있게 해준다.

두 번째 인자는 Promise를 반환하는 비동기 함수인 queryFn인데, query가 데이터를 요청할 때 사용하게 될 함수이다.

만약 첫 번째 인자인 queryKey에 string이 아니라 배열을 넘겨주게 된다면
(e.g. ['users', userId])
배열의 첫 번째 요소는 queryKey가 되고 그 뒤 요소들은 queryFn의 인자로 쓸 수 있게 된다.

import { useQuery } from 'react-query';

function MyComponent({ userId }) {
  const { data, isLoading, error } = useQuery(['user', userId], () => fetch(`https://api.example.com/users/${userId}`));

  if (isLoading) {
    return <p>Loading...</p>;
  }

  if (error) {
    return <p>Error: {error.message}</p>;
  }

  return <p>{data.name}</p>;
}

useQuery의 결과값을 { data, isLoading, error }에 저장하고 있다.

data는 말 그대로 useQuery를 통해 얻은 데이터이다.
isLoading은 query가 아직 데이터를 받고 있는 중인지를 나타내는 boolean 값이다.
error 또한 말 그대로 쿼리에서 발생한 에러이다.

가장 간단하게 쓰이는 건 이 정도지만 이 외에도 공식 문서 (https://react-query-v3.tanstack.com/reference/useQuery) 를 보면 아주 다양한 useQuery의 옵션과 리턴값들을 볼 수 있다.

2. useMutation

데이터를 CREATE, UPDATE, DELETE 할 때 사용한다.
아래는 간단한 회원가입 form 예시이다.

import { useMutation } from 'react-query';

function MyComponent() {
  const { mutate, isLoading, error } = useMutation(async (newUser) => {
    const response = await fetch('https://api.example.com/users', {
      method: 'POST',
      body: JSON.stringify(newUser),
      headers: {
        'Content-Type': 'application/json'
      }
    });
    return response.json();
  });

  async function handleSubmit(event) {
    event.preventDefault();
    const formData = new FormData(event.target);
    const newUser = {
      name: formData.get('name'),
      email: formData.get('email')
    };
    await mutate(newUser);
    // do something
  }

  return (
    <form onSubmit={handleSubmit}>
      <label htmlFor="name">Name:</label>
      <input type="text" name="name" />
      <br />
      <label htmlFor="email">Email:</label>
      <input type="email" name="email" />
      <br />
      <button type="submit" disabled={isLoading}>
        Create user
      </button>
      {error && <p>Error: {error.message}</p>}
    </form>
  );
}

useMutation은 Promise를 반환하는 비동기 함수 mutationFn을 인수로 받고, { mutate, isLoading, error... } 등을 반환한다.
useQuery를 호출하면 queryFn이 실행되어 data를 받아오지만,
useMutation을 호출하면 mutate라는 함수를 만들어서 반환해주고, 그 후 다른 곳에서 mutate 함수가 호출돼야 비로소 mutateFn이 실행된다.

마찬가지로 공식 문서 (https://react-query-v3.tanstack.com/reference/useMutation) 에 더 많은 옵션과 리턴값을 볼 수 있다.

세 줄 요약

  1. React Query를 사용해 서버 데이터를 간결하고 간편하게 관리할 수 있다.
  2. READ할 땐 useQuery를 사용한다.
  3. CREATE, UPDATE, DELETE할 땐 useMutation을 사용한다.

0개의 댓글