Notice
Recent Posts
Recent Comments
관리 메뉴

즐겁게, 코드

[번역] : React Query - 왜, 그리고 어떻게 사용할 수 있을까? 본문

🇺🇸 번역

[번역] : React Query - 왜, 그리고 어떻게 사용할 수 있을까?

Chamming2 2021. 4. 3. 01:58

이 글은 Nathan Sebhastian 님의 How and Why You Should Use React Query 를 번역한 글입니다.

요즘은 axios 와 더불어 react query 역시 많이 쓴다는 말을 들어서... 큰 개념만 정리해보고자 번역을 진행해 봤습니다.

 

시작하기 전에 이 글은 react query 버전 2 를 기준으로 작성된 글로, 21년 4월 기준 가장 최근에 출시된 버전 3에서는 꽤나 굵직한 변경점들이 있으니 최신 버전의 사용 방법은 공식 문서와 유튜브 튜토리얼을 참조하는 것을 추천드립니다.

 

📝 요약 : 기존의 데이터 페칭에는 로딩 상태 관리 및 페칭한 데이터 관리 등을 위해 여러 훅을 사용해야 했으나, 리액트 쿼리를 사용하면 훨씬 간결하게 페칭 로직을 작성할 수 있다.

  // ❌ : 기존의 데이터 페칭 로직 시 사용하는 훅들
  const [isLoading, setLoading] = useState(false)
  const [isError, setError] = useState(false)
  const [data, setData] = useState({});
// ✅ : react-query를 사용하면 하나의 훅으로 모든 페칭에 연관된 상태를 한번에 제어할 수 있다.
const { status, data, error, isFetching } = useQuery(() => fetch(URL));

리액트 어플리케이션을 제작할 때 겪는 어려움 중 하나는 바로 서버에서 데이터를 불러오는 방식을 정하는 것입니다.

지금까지 리액트에서 데이터를 불러오는 가장 흔한 방식은 요청 과정에서 일어나는 일들을 전역 상태(state)로써 관리하는 방식이었는데요, 한번 스타워즈 API를 활용해 데이터를 불러오는 예시를 들어 보겠습니다.

import React, {useState, useEffect} from 'react';
import axios from 'axios';
// axios를 활용한 일반적인 데이터 페칭(fetch)방식
function App() {
  const [isLoading, setLoading] = useState(false)
  const [isError, setError] = useState(false)
  const [data, setData] = useState({});

  useEffect(() => {
    const fetchData = async () => {
      setError(false);
      setLoading(true);

      try {
        const response = await axios('http://swapi.dev/api/people/1/');

        setData(response.data);
      } catch (error) {
        setError(true);
      }
      setLoading(false);
    };
    fetchData()
  }, []);

return (
    <div className="App">
      <h1>스타워즈 API를 활용한 React Query 예제</h1>
      {isError && <div>뭔가 문제가 생겼군요 ...</div>}

      {isLoading ? (
        <div>불러오는 중 ...</div>
      ) : (
        <pre>{JSON.stringify(data, null, 2)}
        </pre>
      )}
    </div>
  );
}
export default App;

위 코드에서는 API로부터 불러온 데이터와 데이터 로딩 여부, 아니면 에러를 수신했는지의 3가지 상태를 관리하기 위해 useStateuseEffect 훅을 모두 사용하고 있는데요, 이는 어플리케이션에서 데이터를 불러올 때 수 차례 반복되는 패턴입니다.

 

이뿐만 아니라 리액트에서 데이터를 불러오는 과정에서는 종종 다음 문제들을 겪기도 합니다.

  • 데이터가 모든 어플리케이션 인스턴스에서 공유되며 다른 사용자에 의해 임의로 변경될 수 있습니다.
  • 데이터의 최신 여부를 보장할 수 없으며 항상 갱신이 필요합니다.
  • 최적화된 요청 작업을 위해 기존의 캐시를 무효화하는 등 캐시를 조작할 수 있어야 합니다.

마지막으로, 테마나 사이드바 설정같은 사용자 설정을 관리하는 로컬 상태와 API로부터 불러온 데이터를 관리하는 원격 상태를 함께 사용할 때에도 문제가 생깁니다.

// 원격 데이터 상태와 로컬 데이터 상태가 뒤섞인 전역 상태의 모습
const state = {
  // 로컬 앱 상태
  theme: "light",
  sidebar: "off",
  // 원격 데이터 상태
  followers: [],
  following: [],
  userProfile: {},
  messages:[],
  todos:[],
}

만약 원격 상태와 로컬 상태를 분리할 수 있다면 더 좋지 않을까요?

또 데이터를 불러오기 위해 3개의 상태를 관리하는 대신, 보다 간결한 코드를 짤 수 있다면 어떨까요?

 

이를 위한 첫 번째 방법은 데이터를 불러오고 관리하기 위한 커스텀 훅을 만드는 것입니다.

이는 매우 유효한 방법으로, Bit(Github)과 같은 컴포넌트 허브(옮긴이 : 컴포넌트 허브는 재사용 가능한 컴포넌트들을 업로드해 공유하는 사이트를 의미합니다.) 에서 커스텀 훅을 찾거나 공유해 사용할 수도 있습니다. (이 방법은 어떤 프로젝트에서도 활용할 수 있는 좋은 방법입니다.)

예시) Bit.dev에서 공유된 리액트 컴포넌트들.

또다른 방법이자 이번 글에서 깊이있게 다룰 내용은 바로 React Query 입니다. 이 라이브러리는 데이터 페칭, 동기화, 업데이트, 캐싱을 도와주면서 두 개의 훅과 하나의 유틸리티 함수로 작성할 코드 수를 줄여줍니다.

 

글을 보다 잘 이해하기 위해, 저자로부터 수정한 아래의 샘플 저장소를 다운로드해주시길 바랍니다.

이 간단한 예제는 axios를 사용해 API로부터 문자열이 담긴 배열을 받아오고, 주어진 폼을 통해 새로운 문자열을 배열에 추가할 수도 있습니다. 또 열려있는 React Query DevTools을 활용해 캐시된 데이터를 실시간으로 확인할 수도 있습니다.

(옮긴이 : React Query 버전 2에서는 DevTool이 제대로 열리지 않는 문제가 있는 듯 합니다. -ㅅ-)

useQuery 훅

useQuery 훅은 데이터를 불러오는 코드를 React Query 라이브러리에 등록하기 위해 사용합니다.

useQuery는 키와 데이터를 불러오는 비동기 함수를 인자로 받아 어플리케이션의 현재 상태를 나타내는 다양한 값을 반환합니다.

예를 들기 위해 위의 스타워즈 API 예제를 다시 작성해 보겠습니다.

import React from 'react';
import axios from 'axios';
import {useQuery} from 'react-query';
// axios를 사용해 react-query 로 데이터 불러오기
function App() {
  const { isLoading, error, data } = useQuery('fetchLuke', () =>
  axios('http://swapi.dev/api/people/1/'))
return (
    <div className="App">
      <h1>스타워즈 API와 함께하는 React Query 예제</h1>
      {error && <div>뭔가 잘못됐군요 ...</div>}

      {isLoading ? (
        <div>루크 스카이워커의 데이터를 불러옵니다 ...</div>
      ) : (
        <pre>{JSON.stringify(data, null, 2)}
        </pre>
      )}
    </div>
  );
}
export default App;

늘 사용하던 useStateuseEffect 훅을 사용하지 않는다는 점을 눈치채셨나요?

왜냐 하면 useQuery에는 어플리케이션에서 활용할 수 있는 다양한 값(로딩 상태, 에러 응답, 반환된 데이터 등)들이 이미 포함되어 있기 때문입니다.

 

이번에는 예제 저장소로 돌아가, /pages/index.js 에서 useQuery 함수를 활용해 api로부터 데이터를 불러온 방법을 확인해 보세요.

const { status, data, error, isFetching } = useQuery('todos', async () => {
  const { data } = await axios.get('/api/data')
  return data
})

기존 방식과의 또다른 차이점은 isFetching을 활용해 쿼리가 데이터를 다시 불러오고 있는지 아닌지를 검사할 수 있는 것인데요, 이는 곧 알아보도록 하고 지금은 원격 데이터를 수정하는 방법에 대해 알아봅시다.

useMutation 훅

useMutation 훅은 원격 데이터의 생성 / 업데이트 / 삭제 (옮긴이 : 이를 뮤테이션이라고 부릅니다!) 에 주로 사용됩니다.

useMutation 훅은 데이터를 업데이트하기 위한 비동기 함수를 인자로 갖고 뮤테이션을 실행하기 위한 뮤테이트 함수를 반환합니다.

const [mutate] = useMutation(
  text => axios.post('/api/data', { text }),
)
mutate("Learn about React Query")

뮤테이트 함수가 특정 결과를 반환할 때만 실행되는 옵션 함수를 추가할 수도 있는데요, 예제에서 폼을 제출할 때 API로 새로운 데이터를 전송하는 뮤테이트 함수를 사용했음을 확인할 수 있습니다.

 

또, POST 요청이 성공적으로 끝나면 입력 칸이 공백으로 바뀌는 것도 확인할 수 있습니다.

const [mutatePostTodo] = useMutation(
  text => axios.post('/api/data', { text }),
  {
    onSuccess: () => {
      // 쿼리 무효화(Query Invalidations)
      // queryCache.invalidateQueries('todos')
      setText('')
    },
  }
)

그러나 예제 어플리케이션에서 새로운 텍스트를 추가해도 할일 목록이 즉시 갱신되지는 않습니다.

React Query가 할일 목록을 다시 페칭하게 하려면, setText 함수 위의 코드의 주석을 해제해 보세요.

{
  onSuccess: () => {
    // 쿼리 무효화
    queryCache.invalidateQueries('todos')
    setText('')
  },
}

queryCache.invalidateQueries 는 todo 키를 갖는 캐시를 무효화시키며, React Query가 데이터를 다시 불러오도록 합니다.

queryCache 유틸리티

예제에서 보았듯, React Query는 불러온 데이터를 todo 라는 키에 캐싱합니다.

그러나 이렇게 불러온 데이터는 staleTime 옵션을 설정하지 않았다면 저절로 최신의 상태를 잃게 됩니다.

 

queryCache는 쿼리의 추가적인 조작을 위한 다양한 함수를 포함하고 있는 유틸리티 객체로, 예제 코드에서 새로운 요청을 보내 할일 목록을 불러오기 위해 queryCache.invalidateQueries 함수를 사용했던 것처럼 이곳에서 queryCache로 할 수 있는 모든 동작을 확인할 수 있습니다.

결론

React Query는 원격 데이터를 전역 상태로 관리해야만 하던 문제를 해결할 수 있는 완벽한 훅 라이브러리입니다.

사용자가 데이터를 불러올 곳을 라이브러리에 알려주기만 하면, 캐싱, 백그라운드 업데이트, 오래된 데이터 교체까지 어떠한 추가 코드나 설정도 없이 해결합니다.

 

React Query는 또 useStateuseEffect 훅을 몇 줄의 React Query만의 방식으로 대체합니다.

장기적으로 보면 이는 어플리케이션을 빠르고 유지보수하기 쉽게 하는 데 도움을 주게 됩니다.

만약 더 많은 내용이 궁금하다면, React Query 공식 문서를 참고하는 것을 잊지 마세요!

반응형
Comments
소소한 팁 : 광고를 눌러주시면, 제가 뮤지컬을 마음껏 보러다닐 수 있어요!
와!! 바로 눌러야겠네요! 😆