💻 Frontend/React

React Query 기본 개념 정리

hjinn0813 2024. 10. 8. 12:52
728x90

기존에는 React로 프로젝트를 진행하며 데이터를 불러와서 다뤄야하는 경우, 컴포넌트나 페이지에서 fetch()를 사용하거나 Redux를 사용했다. 근데 회사에서 실제 고객들에게 서비스하는 '제품'을 만들 때에도 이런 방식을 사용하는건 유지/보수 측면에서 불편할 것 같다고 생각했고, 그런 와중에 React Query를 알게 되었다.

사실 지난 여름에 뉴딜 과정 수업을 들으면서도 다른 동기분들을 통해서 React Query의 존재를 알고 있었지만, 이번에 React Query의 특징과 장단점을 확실하게 알게 되었다. 앞으로 회사에서 진행될 프로젝트도 마침 React를 사용할 예정이라, 미리 착실하게 공부해뒀다가 응용해보면 좋을 것 같아서 기록해본다.✍


React Query

데이터를 서버에서 가져오고, 캐싱하고, 동기화하며, 업데이트하는 로직을 간편하게 관리할 수 있도록 도와주는 라이브러리.

복잡한 비동기 데이터 처리와 상태 관리 문제를 쉽게 해결해주기 때문에 서버에서 데이터를 가져와 사용하는 React 애플리케이션에서 매우 유용하게 사용된다.

React Query의 특징

1. 서버에서 데이터 가져오기:

보통의 React에서 서버의 데이터를 가져오기 위해 fetch 함수나 axios와 같은 HTTP 클라이언트를 사용한다. 하지만 데이터를 가져올 때마다 로딩 상태를 처리하거나, 에러가 발생했을 때 다시 시도하는 로직을 작성하는 것은 매우 귀찮고 복잡하다. React Query는 비동기 데이터 처리에 필요한 로직들을 자동으로 처리해준다. 예를 들어, 데이터 로딩 상태를 관리해주고 (isLoading), 에러가 발생하면 알아서 처리해주고 (isError, error), 데이터를 성공적으로 가져오면 화면에 보여주도록 해준다 (data).

2. 캐싱(Cache):

한 번 서버에서 데이터를 가져오면, 그 데이터를 캐싱하여 다시 요청하지 않아도 되도록 도와준다.

예를 들어, 동일한 데이터를 여러 컴포넌트에서 사용하거나 페이지를 이동했다가 돌아왔을 때, 다시 요청할 필요 없이 캐시된 데이터를 바로 사용할 수 있다. 덕분에 불필요한 네트워크 요청이 줄어들고 앱이 더 빠르게 작동한다.

3. 자동으로 최신 데이터 업데이트 (동기화):

데이터를 가져온 후에도, 백엔드 서버에서 데이터가 변경되었을 경우를 대비해 자동으로 데이터가 갱신되도록 할 수 있다.

예를 들어: 페이지를 다시 열거나, 일정 시간이 지나면 데이터를 새로 불러와 최신 상태를 유지한다. 혹은 수동으로 데이터를 다시 가져오도록 요청할 수도 있다 (수동 리패칭).

4. Mutations (데이터 변경):

데이터를 가져오는 것뿐만 아니라 서버에 데이터를 업데이트하거나 삭제하는 작업도 쉽게 관리할 수 있다. 보통 데이터 변경 작업에는 서버 요청을 보내고 성공 여부에 따라 UI를 업데이트하게 되는데, React Query는 데이터 변경이 성공하면 자동으로 화면을 갱신하고 변경된 데이터를 반영할 수 있게 해준다.

(Query는 CRUD 중에 READ에 해당하고, Mutation은 create, update, delete 등에 해당한다.)

5. 장점 요약:

  • 로딩, 에러, 데이터 상태를 간단하게 관리: 로딩 중이거나 에러가 발생했을 때 별도의 로직을 작성하지 않아도 됨.
  • 캐싱 기능: 한 번 가져온 데이터는 캐싱되어 재사용됨.
  • 자동으로 최신 데이터 동기화: 데이터가 변경되면 자동으로 업데이트 가능.
  • 성능 최적화: 불필요한 네트워크 요청을 줄여주고 데이터가 최신 상태인지 자동으로 확인.
  • 데이터 변경 처리: 데이터 추가, 삭제, 수정과 같은 작업을 간단하게 처리.

6. 코드 예시

import { useQuery } from 'react-query';

export default function Todos() {
  // useQuery 훅을 사용해 서버에서 데이터를 가져옴
  const { isLoading, error, data } = useQuery('todos', () =>
    fetch('/todos').then(res => res.json())
  );

  if (isLoading) return 'Loading...';
  if (error) return 'An error has occurred: ' + error.message;

  // 성공적으로 데이터를 가져오면 렌더링
  return (
    <div>
        {data.map(todo => (
          <div key={todo.id}>{todo.title}</div>
        ))}
    </div>
  );
}

사용하려면 useNavigate처럼 라이브러리를 설치하여 import 하고, 함수 컴포넌트 내부 return문 상단에서 해당 Hook을 정의하여 사용한다. 코드의 내용을 설명하자면, 앞부분에서는 데이터를 성공적으로 가져왔을 경우에 useQuery로 어떻게 처리할지를 설정했고, 뒷부분은 로딩/에러가 발생했을 경우의 처리방법을 설정했다.

useQuery는 비동기적으로 데이터 요청을 관리하는 Hook이고, 여기서 'todos'는 캐시 키. 이런 이름으로 데이터를 가져오겠다는 의미이다. '캐시 키'는 데이터의 특징이나 목적에 맞게 작명하면 된다. 그래서 전체 코드를 풀이하자면,

useQuery를 사용해서 데이터 패치 성공, 에러, 로딩 시의 상황을 비동기적으로 관리하겠다.
fetch()로 'todos' 데이터를 서버에서 가져와서 데이터 패치에 성공하면 해당 데이터를 json으로 변환해서 컴포넌트(프론트엔드) 측에서 사용할거다. 그리고 이 데이터는 자동으로 캐싱하고, 필요할 때 다시 가져와서 사용할 수 있게 관리될 것이다.
만약에 에러, 로딩이 발행하면 각각 상황에 맞는 메시지를 화면에 띄워주겠다. 로딩 중이면 'Loading...'이라는 메시지를 화면에 띄울 것이고, 에러가 발생하면 'An error has occurred:'라고 안내 메시지와 함께 구체적으로 어떤 에러가 발생했는지 알려줄 것이다.

라는 의미가 된다.

구글링하다보니 카카오페이 테크 블로그에 React Query와 관련해서 설명이 아주 잘 되어있어서 링크를 가져왔다.

https://tech.kakaopay.com/post/react-query-1/

 

카카오페이 프론트엔드 개발자들이 React Query를 선택한 이유 | 카카오페이 기술 블로그

카카오페이 프론트엔드 개발자들이 React Query를 선택한 이유에 대해 설명합니다. 이 글은 연작 중 1편에 해당합니다. 1편: 카카오페이 프론트엔드 개발자들이 React Query를 선택한 이유, 2편: React Que

tech.kakaopay.com

추가적으로 궁금해서 chatGPT한테 최근 2년 안에 React 데이터 패칭 라이브러리 사용 순위를 찾아봐달라고 했더니, 아래 사진과 같은 결과들을 보여줬다. 물어볼 때마다 결과가 달라지긴 했어도, 어쨌든 Axios랑 React Query 사용량이 높은 것 같으니까 둘 다 제대로 익혀둬야겠다.👍


데이터 불러오는 방법의 차이와 장/단점 비교

내가 기존에 공부를 하면서 사용했던 React + fetch() 방식과 useQuery, Next.js 모두 서버에서 데이터를 가져오는 방식인데 특징이나 장/단점에 차이가 있는 것 같아서 chatGPT에게 비교를 부탁했다.😆

각각의 방식은 어떻게 데이터를 가져오고 관리하고 최적화하는지에 차이가 있다.

1. React + fetch()

fetch()는 JavaScript의 기본 API로, 데이터를 가져올 때 주로 사용하는 함수.

React에서 데이터를 가져오기 위해서는 useEffect() 훅을 사용해서 fetch()로 데이터를 가져오고, 그 데이터를 상태로 관리한다.

장점 - 간단하고 가벼운 방식.
- 특별한 라이브러리 없이도 기본적인 데이터 패칭이 가능하다.
단점 - 로딩 상태, 에러 처리, 데이터 갱신 등 비동기 처리에 필요한 부가적인 로직을 직접 작성해야 함.
- 캐싱 기능이 없어서, 같은 데이터를 여러 번 요청할 때도 매번 새로 데이터를 가져와야 함.
- 데이터 변경 시에 자동 새로고침 기능이 없어서, 데이터 다시 가져오는 로직(리패칭)을 수동으로 구현해야 함.

2. React Query

React Query는 비동기 데이터 처리와 캐싱을 자동으로 관리해주는 라이브러리.

데이터 요청, 로딩 상태, 에러 처리, 캐싱, 데이터 동기화 같은 복잡한 로직을 쉽게 구현할 수 있다.

장점 - 자동으로 캐싱: 한 번 불러온 데이터를 캐시해서, 다시 같은 데이터를 불러올 때 불필요한 요청을 줄임.
- 자동으로 로딩/에러 상태 관리: 로딩, 에러 처리를 자동으로 해준다.
- 데이터 동기화: 데이터가 변경되면 알아서 새로 불러옴(자동 리패칭).
- 성능 최적화: 백그라운드에서 데이터 갱신을 처리해 사용자 경험을 향상시킨다.
단점 - 추가적인 라이브러리를 설치해야 한다.
- 복잡한 기능이 필요하지 않은 단순한 프로젝트에서는 라이브러리가 과도할 수 있다.

3. Next.js

Next.js는 React 프레임워크로, 서버 사이드 렌더링(SSR)과 정적 사이트 생성(SSG) 등을 통해 서버에서 데이터를 가져오고 캐싱하는 기능을 제공한다. 서버에서 데이터를 미리 가져와 HTML을 생성하여 클라이언트에 전달하고, 필요 시 클라이언트 측에서 데이터를 추가로 불러온다.

※ Next.js 개념정리 - https://hjinn0813.tistory.com/105

 

Next.js 개념 정리, 자동 설치, 실행, 샘플 앱 세탁

Next.js 란 무엇인가?React 기반의 프레임워크.웹 애플리케이션과 웹사이트를 더 쉽고 빠르게 개발할 수 있게 도와준다. 참고로 React는 반응형 UI 구축에 도움이 되는 JS 오픈 소스 라이브러리이다.※

hjinn0813.tistory.com

장점 - SEO 최적화: 서버에서 데이터를 가져와 페이지를 렌더링하므로, 검색 엔진 최적화에 유리하다.
- 빠른 초기 로딩: 서버에서 미리 생성된 페이지를 캐시하고 제공하여 초기 페이지 로딩이 빠르다.
- SSR/SSG/ISR 기능을 통해 서버에서 캐싱과 데이터 갱신을 효율적으로 처리할 수 있음.
- 내장 API 라우트로 서버와 클라이언트 간의 데이터 연동이 쉬움.
단점 - 복잡한 설정과 서버 렌더링 구조가 필요할 수 있음.
- SEO가 중요하지 않은 프로젝트에서는 필요 이상의 기능일 수 있음.
- SSR/SSG를 잘 활용하지 않으면 불필요한 오버헤드가 발생할 수 있음.

4. 결론

  • React + fetch(): 아주 간단한 데이터 요청이나 소규모 프로젝트에서는 적합하지만, 수동으로 처리해야 하는 부분이 많다.
  • React + React Query: 데이터 패칭과 상태 관리가 중요한 프로젝트에서 유용, 캐싱과 자동 동기화 기능으로 생산성 향상.
  • Next.js: SEO가 중요하거나, 서버에서의 데이터 관리와 최적화가 필요한 프로젝트에서 강력한 선택. 초기 로딩 속도와 성능을 최적화하려면 좋음.
/ React + fetch() React Query Next.js
데이터
패칭
클라이언트에서 fetch()로 직접 서버에 요청 클라이언트에서 useQuery()로 비동기 데이터 패칭 및 상태 관리 서버에서 데이터 요청
(SSR/SSG/ISR)
로딩/에러 수동 처리
(useState, useEffect)
자동 처리
(isLoading, error)
자동으로 로딩/에러 처리
캐싱
(Cache)
없음 자동 캐싱, 백그라운드에서 데이터 갱신 서버에서 페이지나 API 캐싱
(정적 페이지 캐싱)
SEO 없음 없음 최적화 가능
(서버에서 렌더링한 페이지 제공)
데이터
갱신
수동으로 리패칭하는 로직 필요 자동 동기화 및 재검증
(자동으로 최신 데이터 유지)
ISR(점진적 정적 재생성)로 서버에서 데이터 자동 갱신 가능
복잡도 낮음
(직접 로직 작성해야해서 비효율적)
중간
(간단하게 비동기 데이터 관리 및 최적화 가능)
높음
(SSR과 라우팅 설정 필요)

ISR, 점진적 정적 재생성

Incremental Static Regeneration.

Next.js에서 제공하는 기능으로, 정적 사이트 생성(SSG)의 장점과 실시간 데이터 갱신을 결합한 방식.

쉽게 말하면, 정적 페이지를 생성하면서도 최신 데이터를 일정 주기로 반영할 수 있는 방법.

ISR의 작동방식:

1. 초기 페이지 생성: 사용자가 페이지에 처음 접근할 때, Next.js는 정적 페이지를 만들어서 사용자에게 보여준다다. 이 정적 페이지는 한 번 생성되면 캐싱되기 때문에, 이후에도 빠르게 로드된다.

2. 백그라운드에서 데이터 갱신: Next.js는 이 페이지를 일정 시간 후에 백그라운드에서 다시 생성한다. 즉, 사용자가 페이지를 볼 때는 이전에 만들어진 페이지가 표시되고, 그동안 서버에서는 최신 데이터를 사용해 페이지를 다시 생성하는 것이다.

3. 페이지 자동 갱신: 새로운 사용자가 그 페이지를 다시 요청하면, 백그라운드에서 갱신된 최신 페이지가 제공된다. 이렇게 하면 서버가 계속해서 페이지를 재생성하면서도, 최신 상태를 유지할 수 있다.

→ 쉽게 비유하자면: 마치 블로그 글을 쓴 후, 한 번 정적 HTML 파일로 변환해놓고, 일정 시간마다 새로운 내용으로 자동으로 업데이트하는 것과 비슷하다. 페이지는 빠르게 제공되면서도, 데이터가 주기적으로 새롭게 갱신된다.

ISR의 장점:

  • 빠른 초기 로딩: 정적 페이지처럼 빠르게 로드된다.
  • 최신 데이터 반영: 백그라운드에서 최신 데이터를 가져와 자동으로 갱신한다.
  • 서버 부하 감소: 매 요청마다 서버에서 페이지를 다시 만들 필요 없이, 주기적으로만 갱신하니 서버 부하가 줄어든다.

React Query + Axios

만약 React Query만 사용한다면, useQuery 내부에 fetch()를 바로 사용해서 API 주소를 넣는다. 이렇게 하면 추가 라이브러리 없이 코드를 간결하게 작성할 수 있다. 하지만 fetch()는 기본적인 기능만 제공하기 때문에 타임아웃, 헤더 등 추가적인 요청을 수동으로 처리해야 한다. 반면에 React Query와 Axios를 함께 이용한다면, Axios에 기본적으로 내장된 기능을 통해 복잡한 API 요청도 가능해서, 코드의 가독성이 훨씬 좋아질 수 있다.

아래는 앞서 작성했던 React Query 예시 코드에 Axios를 사용한 예시이다.

import React from 'react';
import { useQuery } from 'react-query';
import axios from 'axios';

export default function Todos() {
  const { isLoading, error, data } = useQuery('todos', () =>
    axios.get('/todos').then(res => res.data) // Axios로 API 요청
  );

  if (isLoading) return 'Loading...';
  if (error) return 'An error has occurred: ' + error.message;

  return (
    <div>
      {data.map(todo => (
        <div key={todo.id}>{todo.title}</div>
      ))}
    </div>
  );
};
728x90