React Query 사용해보기! - 1

제이제이리·2023년 12월 9일

React

목록 보기
1/1
post-thumbnail
  • 삼성 청년 SW에서 프로젝트를 진행하던 중, React Query에 대한 자료를 작성해서 공유했었다.
  • 새로 사용하게 된 기술 스택이 많아, 이해가 어려워하던 팀원을 위해 관련 내용을 작성했는데,
    나도 공부할 수 있는 계기가 되어 좋았다. 해당 기록을 다시 다듬어보자!

React Query란?

TanStack Query(FKA React Query)는 종종 웹 애플리케이션용 누락된 데이터 가져오기 라이브러리로 설명되지만 좀 더 기술적인 용어로 말하면 웹 애플리케이션에서 서버 상태 가져오기, 캐싱, 동기화 및 업데이트를 매우 쉽게 만듭니다.

  • 공식 문서를 요약하자면, React-query는 서버와의 동기화 및 데이터 업데이트를 쉽게 만들어주는 라이브러리를 의미한다.

React Query를 사용하는 이유

대부분의 기존 상태 관리 라이브러리는 클라이언트 상태 작업에는 적합하지만 비동기 또는 서버 상태 작업에는 그다지 적합하지 않습니다 . 이는 서버 상태가 전혀 다르기 때문입니다 . 우선 서버 상태는 다음과 같습니다.

  • 귀하가 통제하거나 소유하지 않는 위치에 원격으로 지속됩니다.
  • 가져오기 및 업데이트를 위한 비동기 API가 필요합니다.
  • 공유 소유권을 암시하며 본인도 모르게 다른 사람이 변경할 수 있습니다.
  • 주의하지 않으면 애플리케이션이 "구식"이 될 수 있습니다.

애플리케이션에서 서버 상태의 특성을 파악하면 진행하면서 더 많은 문제가 발생하게 됩니다

  • 캐싱... (아마도 프로그래밍에서 가장 어려운 일)
  • 동일한 데이터에 대한 여러 요청을 단일 요청으로 중복 제거
  • 백그라운드에서 "오래된" 데이터 업데이트
  • 데이터가 "오래된" 시기 알기
  • 최대한 빠르게 데이터 업데이트 반영
  • 페이지 매김 및 지연 로딩 데이터와 같은 성능 최적화
  • 서버 상태의 메모리 및 가비지 수집 관리
  • 구조적 공유를 통해 쿼리 결과 메모

👨‍🔧 정리하자면,

서버에서 데이터를 가져와 웹 프레임워크 내에서 비동기 처리를 하는 복잡한 과정을 좀 더 쉽게 처리하기 위함이다. 또한 장점을 정리하면,

  1. 애플리케이션에서 복잡하고 잘못 이해된 여러 줄의 코드를 제거하고 몇 줄의 React Query 로직으로 대체할 수 있도록 도움을 준다.
  2. 새로운 서버 상태 데이터 소스 연결에 대한 걱정 없이 애플리케이션을 더욱 유지/관리하기 쉽게 만들고 새로운 기능을 쉽게 구축할 수 있다.
  3. 애플리케이션이 그 어느 때보다 더 빠르고 반응성이 뛰어나다는 느낌을 주어 최종 사용자에게 직접적인 영향을 미친다.
  4. 잠재적으로 대역폭을 절약하고 메모리 성능을 높이는 데 도움이 된다.

React Query 버전 설정하기

  • 나는 지난 프로젝트에서 React Query의 가장 최신 버전인 5.-을 설치하였으나, 이후 4.-으로 버전을 바꿔 사용하였다.
  • 그 이유는 바로 onSuccess, onError의 중단 때문이다..!

[번역] React Query API의 의도된 중단

  • 기존 사용하던 쿼리에서 오류가 자꾸 나서 관련 내용을 찾던 중 해당 포스팅을 보게 되었다.
  • 위 블로그에서는 TkDodo의 Breaking React Query's API on purpose를 번역한 포스팅을 담고 있는데, useQuery 메소드에서 버그 발생 및 앱 리팩토링/확장 시 에러가 쉽게 발생할 수 있는 상태 동기화가 제공되기 때문에 해당 메소드를 중단했다고 한다.
  • 그러나 당시 짧은 기간 내에 프로젝트를 완성해야 했고, 모든 팀원들은 React Query를 사용해 본적이 없어 예제를 기반으로 한 설명이 절실했다...
  • 기술 예제와 공통 코드를 작성해야 했던 나는 결국 이전에 사용해봤고, 예제가 있는 버전으로 다운그레이드를 진행했다.😢

오늘의 교훈 : 버전 체크를 잘하자, 예전에 쓴 코드 다시 쓸 때는 공식문서 잘 읽자.


use Query로 get요청 진행하기.

  • typeScript 및 React-Native를 사용한 프로젝트 내에서 useQuery를 작성한 예시
  • 먼저, 팀원들과 함께 쓸 api 양식을 만들어준다.
// 헤더 및 공통 api 양식 작성해주기

export const Header = async () => ({
  headers: {
    Authorization: `Bearer ${await getStorage('Accesstoken')}`,
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Credentials': 'true',
  },
});

export const api = {
  get: <T = unknown>(path: string, init?: RequestInit) =>
    request(path, init).then<T>(response => response.json()),

  put: <T = unknown>(path: string, payload?: T, init?: RequestInit) =>
    request(path, {
      headers: init?.headers,
      method: 'PUT',
      body: JSON.stringify(payload),
    }),

  post: <T = unknown>(path: string, payload?: T, init?: RequestInit) =>
    request(path, {
      headers: init?.headers,
      method: 'POST',
      body: JSON.stringify(payload),
    }),

  delete: <T = unknown>(path: string, payload?: T, init?: RequestInit) =>
    request(path, {
      headers: init?.headers,
      method: 'DELETE',
      body: JSON.stringify(payload),
    }),
};

// 위에서 작성한 양식 토대로 api 작성하기
export const fetchMyAnimalListInfo = async () => {

					// 1. 타입    2. api         3. 헤더를 작성
  return await api.get<Animal[]>('/animal/id001', await Header());
};
  • 다음으로 @tanstack/react-query를 설치해주자.
yarn add @tanstack/react-query
  • 이후, 데이터를 fetch해오는 코드를 작성.
import React from 'react';
import {View, FlatList, Text} from 'react-native';
import AnimalCard from './AnimalCard';
import styles from './style';
import {fetchMyAnimalListInfo} from '@/apis/animal';
import {useQuery} from '@tanstack/react-query';

// 위 데이터에 맞게 타입을 수정해주기.

type Animal = {
  animalId: number;
  animalName: string;
  fileUrl: string;
  selected: boolean;
};

const targetNumColumns = 3; // 원하는 열의 수

export default function AnimalCardlist() {
  const {
    data,          // API 응답 결과를 반환하는 값임. state에 저장해도 되지만 여기서는 그냥 불러와서 그대로 씀
    isLoading,     // 데이터 로딩 중일 때 true값을 반환함. 따라서 view에 isLoading? 해서 로딩 페이지 보여줄 수 있음
    isError,      // 1. isError: 쿼리가 오류났다? 예를 들어서 500 에러가 났다? 그러면 이게 true값이 되어서 돌발! 돌발! 하고 알려준다는 뜻임. 
									// 근데 나는 이 페이지에서 쿼리를 세 네개 썼고, 이 값이 중복 되면 안되니까 isError라는 값을 isBookListError라는 이름으로 바꿔주세요~ 하고 써두었음.
    error           // 2. error: 1번에서 오류났는데 그러면 메세지가 있어야 뭔 에런지 알겠죠? 그럼 여기서 isError가 true면 error를 써서 메세지를 받음.
										// 나는 이 페이지에서 쿼리를 세 네개 썼고, 이 값이 중복 되면 안되니까 error를 bookListError로 바꿔서 메세지를 받음.
										// useQuery<Animal[]>로 호출 성공! 이러면 받아오는 타입을 지정해줌.
  } = useQuery<Animal[]>({
    queryKey: ['animalList'],   // 쿼리의 고유 키. 리스트로 넣어주면 오류가 해결됨. 이는 공문에도 리스트로 넣으라고 쓰여있긴 함
    queryFn: fetchMyAnimalListInfo,  // 실제 데이터를 가져오는 함수
  });

  // 로딩 중이면 로딩 메시지를 출력
  if (isLoading) return console.log('로딩');
  // 에러가 발생하면 에러 메시지와 함께 에러의 내용을 출력하는데 hotToast등으로 에러났다고 말하거나 처리하기
  if (isError) return console.log('에러', error);

  // 응답받은 데이터를 Animal 타입 배열로 변환해서 넣어주는데....
//  이건 이렇게 안하고 state에 저장해도 됨. 
// 그러나 나중에 동물 선택하고 리스트 업데이트 해야 하고 그러면 업데이트 로직 생각해볼 것
  const animalsArray: Animal[] = data as Animal[];

  const totalCards = animalsArray.length;
  const numColumns = Math.min(
    targetNumColumns,
    Math.ceil(totalCards / targetNumColumns),
  );
  const missingCards = numColumns - (totalCards % numColumns);

  // 타겟 열 수와 다르다면 hidden card 만드는 예외처리
  if (missingCards !== targetNumColumns) {
    for (let i = 0; i < missingCards; i++) {
      animalsArray.push({
        animalId: i,
        animalName: '',
        fileUrl: '',
        selected: false,
      });
    }
  }

  return (
    <View>
      <FlatList
        key={numColumns}
        horizontal={false} // 수직으로 정렬
        numColumns={numColumns} // 한 줄에 표시할 카드 수 설정
        data={animalsArray}  <----------------------------여기에 fetch 해 온 아이템 다 들어가는 것
        keyExtractor={item => item.animalId.toString()}
        renderItem={({item}) => {
          if (!item.animalName) {
            return <View style={styles.hiddenCard} />;
          }
          return (
            <AnimalCard
              id={item.animalId.toString()}
              title={item.animalName}
              imgurl={item.fileUrl}
            />
          );
        }}
      />
    </View>
  );
}
  • 이렇게 useQuery를 사용하는 방법을 정리해서 노션 문서에 남겨두었다.
  • 팀원들이 댓글 달아준 내용 보면서 뿌듯했었다 ㅎㅎㅎ 역시 배워서 남 주는게 최고다!
  • 다음 포스팅에서는 useMutation을 사용해 post, put, delete 요청 하는 방법을 작성해보겠다!
profile
추상적인 생각을 구체적인 현실로

0개의 댓글