React Query & Recoil & asynchronous Recoil 알아보기

학미새🐥·2023년 11월 21일
1
post-thumbnail

0. React의 상태 관리

▶️ state(상태)란?

컴포넌트 간에 이동되는 props와 달리 컴포넌트 내부에서 관리되는 자바스크립트 객체

  • 어플리케이션의 화면 렌더링에 영향을 끼친다
  • 사용자의 인터랙션을 통해 동적으로 변하는 데이터

서로 다른 컴포넌트에서 동일한 데이터를 다룰 땐, 해당 데이터의 출처가 동일해야 한다. 그래야 서로의 변화에 동적으로 반응할 수 있기 때문이다. 즉 상태의 일관성 (데이터의 무결성)을 잘 지켜야한다.
Single Source of Truth이라는 방법론이 이러한 상태의 일관성을 유지하기 위해 등장한 방법론인데, 말그대로 신뢰할 수 있는 단일 출처를 말한다.
이 방법론이 React가 택한 방법론이다. 따라서 우리는 React에서 데이터의 무결성, 즉 서로 다른 컴포넌트에 분포되어있는 공통된 출처의 데이터의 일관성을 유지할 수 있도록 상태 관리를 해주어야 한다.

▶️ Client State vs Server State

Client StateServer State
위치클라이언트에 저장된 데이터서버나 외부 데이터 소스에 저장된 데이터
접근성클라이언트만 접근 가능접근 권한이 있는 모든 클라이언트 접근 가능
데이터 관리클라이언트에서 관리
(Redux, Recoil 등 상태관리 라이브러리 사용)
서버에서 관리
(데이터베이스 사용)
지속성세션 간에 필연적으로 지속되진 않음일반적으로 세션 간에 지속성 유지
네트워크 요청데이터를 가져오거나 업데이트하려면 네트워크 요청이 필요할 수 있음데이터 접근 또는 업데이트를 위해 네트워크 요청이 필요할 수 있음
보안클라이언트가 액세스할 수 있고 보안 수준이 낮음인증 및 암호화로 보호할 수 있으므로 더 안전
성능server state에 비해 속도 빠름client state에 비해 속도 느림
확장성클라이언트 기기 용량이 제한적이어서 확장성이 다소 떨어짐전용 서버나 데이터 소스로 관리되기 때문에 용량이 커서 확장성이 비교적 높음
예시컴포넌트 state, Redux state, 브라우저 쿠키DB 레코드, API 응답, 서버 세션 데이터

본 아티클에서는 Server State 라이브러리의 대표 React Query와, Client State 라이브러리의 대표 Recoil을 알아보고자 한다.



1. React Query

1-1. React Query란?

React에서 데이터 fetching캐싱 프로세스를 간소화해주는 라이브러리

  • 외부(API 등)로부터 데이터를 fetching 및 업데이트 과정 간소화
  • API 요청의 로딩 및 오류 상태 관리
  • 캐싱 자동 관리

Client에서 API와 통신을 하기 위해서는
추가적으로 useEffect, useState를 함께 쓰면서 데이터를 fetching 해왔다.

하지만 React Query를 사용하면 이러한 Hook들의 필요성이 사라진다.


1-2. React Query 주요 개념

Query

  • API 엔드포인트, DB 등의 원격 데이터 소스로부터 데이터 요청
  • useQuery 훅 사용

Mutation

  • 서버에 데이터를 추가하거나 업데이트
  • useMutation 훅 사용

Query Caching

  • Query 결과를 메모리에 저장
    Query Invalidation
  • 쿼리를 오래된 상태로 여겨 무효화

1-3. Hooks

1️⃣ useQuery 훅

데이터 Fetching 용

API 엔드포인트나 DB에서 데이터를 비동기적으로 가져오도록 서버에 요청하는 것


사용법

const query = useQuery( { queryKey: [ ‘key’ ],   queryFn: callback })

queryKey

  • 쿼리의 고유 키
  • React Query 최신 버전부터는 배열 표기법을 사용해서 키 지정

queryFn

  • 훅 호출 시 실행되는 Promise 반환하는 함수
  • 해당 callback함수에서 데이터 fetching
// 예시
const getProducts = () => fetch( 'https://jsonplaceholder.typicode.com/users')
                          .then( res  => res.json() )

const query = useQuery( { queryKey: [‘users’],   queryFn: getTodos })

반환값 쿼리의 상태 정보를 담고 있는 객체

const { isLoading, isError, data, error, refetch, remove } = useQuery( { queryKey: [‘todos’], queryFn: getTodos });

대표적인 프로퍼티

  • isLoading 쿼리가 현재 로딩 중인지 여부 (boolean)
  • isError 쿼리 결과가 오류인지 여부 (boolean)
  • data 쿼리를 통해 성공적으로 fetching된 데이터
  • error 쿼리 실행 중 발생한 모든 에러
  • refetch 쿼리 데이터를 수동으로 refetch 하는 트리거 메소드
  • remove 캐시에서 특정 쿼리 제거하는 메소드

데이터 refetching

useQuery는 기본적으로

  • 컴포넌트 최초 mount 시에 데이터를 fetching해오고,
  • 이후의 데이터 업데이트에 대해서는 자동 실행되지 않는다. (데이터 refetching 안해줌)

[ useQuery 옵션 ]

옵션역할기본값
refetchOnWindowFocus데이터가 오래된(stale) 경우 브라우저 창이 포커스 되면 리페치true
refetchOnMount데이터가 오래된 경우 마운트 시 리페치true
refetchOnReconnect데이터가 오래된 경우 재연결 시 리페치true

하지만 브라우저가 idle 상태를 유지할 때엔, 데이터 업데이트를 감지하여 자동으로 리페치해주지 않는다.

이를 보완하기 위해서 refetchIntervel 옵션을 사용할 수 있다.

  • 설정한 밀리초 단위로 데이터를 계속 refetching 해옴

2️⃣ useMutation 훅

API 엔드포인트나 DB에 데이터를 새로 생성, 수정, 삭제

사용법

const mutation = useMutation({ mutationFn: mutationFunction });

mutationFn

  • 필수 옵션.
  • 실행하고자 하는 함수를 반환하는 함수
// 예시
const AddUser = useMutation({
  mutationFn: ( user ) => {

    return fetch('https://jsonplaceholder.typicode.com/users',
                 {
      method:'post',
      headers: {
        "Content-Type": "application/json",
      },
      body:JSON.stringify( user )
    }).then( res =>  res.json() )
  })

반환값 뮤테이션 실행 결과와 관련된 정보를 담은 객체

대표적인 프로퍼티

  • isLoading 뮤테이션이 현재 로딩 중인지 여부 (boolean)
  • isSuccess 뮤테이션 결과가 성공인지 여부 (boolean)
  • isError 뮤테이션 결과가 오류인지 여부 (boolean)
  • data 뮤테이션 실행 후 반환된 데이터 (있을 때만)
  • mutate 해당 뮤테이션 실행을 위해 호출할 메소드. (인자에 담을 변수를 객체로 전달)
  • reset 뮤테이션 초기 상태로 리셋하는 메소드
  • onSuccess 뮤테이션 성공시 호출할 콜백함수
  • onError 뮤테이션 실패시 호출할 콜백함수

3️⃣ Query Caching

원격 서버와 통신하는데에 걸리는 시간을 단축하고자,

여러번 불러올 데이터는 캐시에 저장하여 반복해서 필요로 할 경우 원격 서버가 아닌 캐시에서 빠르게 가져올 수 있는 방식

useQuery 훅 사용 시, 지정했던 고유 키 밑에 반환된 데이터가 캐싱된다.

기본적으로는 캐시 데이터는 오래된(stale) 상태로 기록된다.


[ Query Caching과 관련된 useQuery 옵션 ]

const query = useQuery({
    queryKey: [‘users’],
    queryFn: getUsers,
    staleTime: 5000,
    cacheTime: 60000
  })

staleTime

  • xx밀리초 후에 데이터는 stale 상태로 처리된다.

cacheTime

  • xx밀리초 후에 캐시 데이터는 가비지 컬렉션으로 간다. (버려짐)

4️⃣ Query Invalidation

지정해준 staleTime 과 무관하게 즉시 데이터를 stale 상태로 처리해야하는 경우가 있다.

예를 들어, API에 POST 요청을 보내 데이터 값을 업데이트할 때엔, API 엔드포인트에 있는 데이터의 값이 캐시에 저장되어있는 값보다 더 최신 상태가 되고, 캐시에 저장되어있는 데이터는 곧바로 outdated한 값이 되어버린다. 이럴 때엔 즉시 캐시 데이터를 stale 상태로 처리해줘야 한다.

React Query의 QueryClient 객체invalidateQueries 메소드 활용

  • 모든 쿼리, 혹은 고유 키를 통해 특정 쿼리를 stale 상태로 처리해주는 역할

사용법

  • useQueryClient 훅을 통해 queryClient 객체 생성
  • 객체 내장 메소드 invalidateQueries 활용하기
import { useQueryClient } from "@tanstack/react-query";

// useQueryClient 훅을 사용하여 queryClient 객체 생성
const queryClient = useQueryClient();

// 캐시의 모든 쿼리 무효화
queryClient.invalidateQueries();

// 'users'로 시작하는 키가 있는 모든 쿼리 무효화
queryClient.invalidateQueries({ queryKey: ["users"] });

📌 React Query 요약

  • React 훅을 사용해서 데이터를 간편하게 Fetch/Update
  • 스마트 캐싱 메커니즘

→ 코드 간소화 & 성능 최적화

React Query는 강력한 Server State 관리 라이브러리이다!


2. Recoil

2-1. Recoil 이란?

  • 비교적 최근에 나온 새로운 상태 관리 라이브러리
  • Facebook사에서 개발

😭 조심스레 한계점을 먼저 알아보고 가자

위 사진은 내장 API인 Context API를 제외한 대표적인 Client State 관리 라이브러리들의 npm 설치 수 비교 그래프이다. (근 1년 기준)
한눈에 알 수 있듯이 Recoil보다 Redux가 설치 수에 있어 훨씬 더 우세하다는 것을 확인할 수 있다.

이 그림에서 그 한계점을 바로 확인할 수 있는데, 바로 최신 라이브러리인 만큼 개발자 커뮤니티가 매우 빈약하기 때문에 어떠한 이슈가 발생하여도 집단지성의 힘을 빌려 해결하기 어려운 상황이 발생할 수 있으며, API 혹은 공식문서에 대한 신뢰도도 비교적 낮다고 할 수 있다.

그러나!

  • 가장 React스러운 상태관리라는 철학을 갖고있음
  • 일반적인 state와 같은 get, set 인터페이스를 사용하기 때문에 간단한 구조를 가지고, Boiler Plate도 적어 러닝커브가 상대적으로 낮음.

위와 같은 면에서는 뚜렷한 강점을 가지고 있기 때문에 React 라이브러리를 사용하는 프론트엔드 개발자라면 충분히 적극적으로 활용해보면 좋을 라이브러리라고 생각한다.

2-2. Recoil 주요 개념

▶️ atom & selector

atom : 상태의 단위

  • 업데이트(set)와 구독(get)이 가능
  • atom이 업데이트 될 때마다, atom을 구독하고 있는 컴포넌트들이 리렌더링된다.

selector : atom의 상태 값을 동기/비동기 방식으로 업데이트

  • atom이 최소한의 상태의 단위였다면, selector는 그곳에서 파생되는 모든 데이터를 관리하는 곳이라고 할 수 있다.
  • selector의 상위 atom 혹은 selector가 업데이트될 경우, 이들과 종속된 하위 selector도 함께 재실행된다.
  • selector도 구독이 가능하여, atom과 동일하게 selector가 업데이트될 때마다 이를 구독하는 컴포넌트들이 리렌더링된다.

▶️ selector의 get

const mySelector = selector({
  key: 'MySelector',
  get: ({get}) => get(myAtom) * 100,
});

get : 콜백함수의 인자에 담겨있는 get함수를 통해 다른 atom/selector를 구독할 수 있다.


위의 예시를 기반으로 atom과 selector의 작동방식을 설명하자면,
myAtom이라는 별도의 atom에서 파생된 데이터를 관리하고자 mySelector라는 selector를 생성한 것이고,
mySelector가 하는 일은 myAtom 상태의 값을 읽어와 100을 곱해준 뒤 반환하는 함수이다.
이 때, mySelector 셀렉터는 myAtom 아톰에 대해 종속된다/의존한다/구독한다 라고 할 수 있으며,
이럴 경우 myAtom의 값이 업데이트될 때마다 mySelector재실행되게 되어 동적으로 값을 관리할 수 있게 된다.

selector에 get 함수만 정의될 경우, RecoilValueReadOnly 객체를 반환하여, 함수 반환 값을 읽는 것만 가능해진다.

반면, 쓰는 것도 가능한 selector의 set함수도 있으나, 본 아티클의 주제에 직결되는 내용만 다루기 위해 생략한다. 관련 내용은 문서에서 참고하자!


▶️ Recoil Hooks

useState와 매우 유사한 형태이므로, 쉽게 사용할 수 있다.

  • useRecoilState : state 값과 setState 모두 사용할 때
    const [변수명, setter명] = useRecoilState(atom명);
  • useRecoilValue : state 값만 사용할 때
    const 변수명 = useRecoilValue(atom명);
  • useSetRecoilState : setState만 사용할 때
    const setter명 = useSetRecoilState(atom명);

2-3. Recoil 사용법

0️⃣ RecoilRoot로 감싸주기

ReactDOM.createRoot(document.getElementById("root")!).render(
  <>
    <RecoilRoot>
      <ThemeProvider theme={theme}>
        <GlobalStyle />
        <App />
      </ThemeProvider>
    </RecoilRoot>
  </>
);

Recoil을 사용하고자 하는 컴포넌트의 상위에서 RecoilRoot를 import 해와서 감싸준다.

일반적인 경우, Recoil을 전역적으로 사용하기 때문에 App.tsx / main.tsx 에서 사용한다.

1️⃣ atom 생성하기

atom.ts 파일을 생성하여 원하는 atom을 생성한다.

// recoil/atom.ts
export const followState: RecoilState<boolean> = atom({
  key: "followState",
  default: false,
});
  • key : 모든 atom은 반드시 전역적으로 고유한 key를 가진다.

2️⃣ recoil Hook 호출하기

  • 상태의 값을 사용하는 컴포넌트 내
  const follow = useRecoilValue(followState);
  • 상태를 업데이트시켜주는 함수를 실행하는 컴포넌트 내
  const setFollow = useSetRecoilState(followState);

📌 Recoil 요약

  • atom : 전역 상태의 최소 단위
  • selector : atom에서 파생된 데이터를 관리하는 곳

Recoil은 강력한 Client State 관리 라이브러리이다!



지금까지 Client State를 관리할 수 있는 Recoil에 대해 알아보았다.
그렇다면 Recoil은 Server State Fetching과는 전혀 무관한 라이브러리일까?
아니다. Recoil을 사용하여 비동기적으로 데이터를 처리하는 것도 가능한데, 지금부터 그 방법인 Recoil Asynchronous Data Query 에 대해 알아보자.

3. Asynchronous Recoil

3-1. Asynchronous Data Query

위에서 본 selector의 get은 을 반환해주는 콜백함수를 가졌었다.
그러나 get 함수는 일반적인 값 뿐만 아니라 Promise 객체를 리턴해줄 수 있다.

대표적인 예시로, Server State를 불러오는 Data Fetching의 경우를 보자.
기존에 우리가 async await 를 사용하여 Data Fetching을 해온 방식은 아래와 같다.

const getContent = async () => {
  const response = await axios.get(url);
  return response.data;
};
// try catch 등은 생략한 가장 기본적인 코드이다

이렇게 비동기적인 API 호출을 실행하여 Promise를 반환하는 함수를 selector의 get으로 넣어보자.

const contentQuery = selector({
  key: 'contentQuery',
  get: async ({get}) => {
    const response = await axios.get(url);
    return response.data;
  },
});

이제 응답받은 데이터를 사용하고 싶을 땐 selector의 값을 가져오는 Hook이었던 useRecoilValue를 똑같이 사용하면 된다.

const 호출컴포넌트 = () => {
  const content = useRecoilValue(contentQuery);
  return (<div>{content}</div>);
}

3-2. Pending 처리

그런데, 만약 Promise가 pending 상태일때, 즉 resolve가 호출되기 전에는 호출컴포넌트는 무엇을 렌더링해야 할까?

이러한 경우를 처리할 수 있는 것이 바로 React의 Suspense이다.

return (
  <RecoilRoot>
    <React.Suspense fallback={<div>Loading...</div>}>
      <호출컴포넌트명 />
    </React.Suspense>
  </RecoilRoot>
);

위와 같이 fallback에 Pending 시 렌더링할 콘텐츠를 담은 React.Suspense비동기 selector를 호출하는 컴포넌트를 감싸주면 된다.

3-3. Error 처리

그렇다면 만약 Promise의 실행 결과, resolve가 아닌 reject가 호출되어 에러가 발생했을 경우엔 어떻게 처리해주어야 할까?

이러한 경우엔 React의 ErrorBoundary를 활용해줄 수 있다.

return (
  <RecoilRoot>
    <ErrorBoundary>
      <React.Suspense fallback={<div>Loading...</div>}>
        <호출컴포넌트명 />
      </React.Suspense>
    </ErrorBoundary>
  </RecoilRoot>
);

위와 같이 <ErrorBoundary> 태그로 비동기 selector를 호출하는 컴포넌트를 감싸주면 된다.

이렇게 Recoil은 React의 Suspense와 ErrorBoundary를 활용할 수 있도록 설계되어있기 때문에 React와의 호환성 및 활용성이 우수한 라이브러리라고 할 수 있다.

📌 Asynchronous Data Query 요약

  • selector의 get함수에서 Promise를 반환해주는 경우
  • resolve -> get함수의 반환값 렌더링
  • pending -> 감싸준 React의 Suspense의 fallback 값 렌더링
  • reject -> 감싸준 React의 ErrorBoundary 컴포넌트 활용

Recoil로도 쉽게 Server State를 관리해줄 수 있다!


참고문헌

Recoil 공식문서
React Query 공식문서

profile
뭐든 다해보려는 공대생입니다

10개의 댓글

comment-user-thumbnail
2023년 11월 23일

useQuery가 최초 mount에만 실행되고 이후에는 자동 실행이 되지 않는다는 점을 이번 아티클을 통해 처음 알게되었습니다!

useQuery가 있는데 useMutation은 왜 사용을 하는지 차이점은 뭔지 궁금해져서 찾아봤는데,
useQuery는 get method를 사용할 때, useMutation은 post, delete, patch(put) method를 사용할 때 사용된다고 하네요!
(참고 자료
https://velog.io/@citron03/React-Query에서-Post-useMutation-hook-사용하기
https://stackoverflow.com/questions/64700944/react-query-whats-the-difference-between-usequery-and-usemutation-hook
)
찾고 나니 너무나도 기본적인 이유였어가지고 초큼 당황스러웠습니다;

recoil을 한 번도 사용해 본 적 없는데, 쉽게 처음부터 설명을 해주셔서 따로 자료 안 찾아봐도 바로 코드에 적용시킬 수 있을 거 같다는 생각이 들었습니다!
recoil은 들어는 봤었지만 asynchronous recoil은 아예 처음 들어보는 거라 생소했지만 이 또한 잘 이해할 수 있었습니다!!

이번 아티클에선 정말 많은 걸 배워가는 거 같아요!!
Suspense와 ErrorBoundary도 진짜 처음 보는 개념인데 완전 신세곕니다;
react-query에 isLoading 보고 state로 관리 안 해줘도 되겠다 싱글벙글 하고 있었는데 Suspense 보고 머리 띵 치고 갑니다;

이에 대해 좀 더 공부하고 싶어서 찾아봤는데요

<Suspense fallback={<Loading />}>
  <Biography />
  <Panel>
    <Albums />
  </Panel>
</Suspense>

이와 같이 여러 개의 자식 컴포넌트가 있는 경우, 하나만 준비되었다고 해서 렌더링 되는 것이 아니라 자식들 모두 준비되었을 때 렌더링이 된다고 합니다!!

<Suspense fallback={<BigSpinner />}>
  <Biography />
  <Suspense fallback={<AlbumsGlimmer />}>
    <Panel>
      <Albums />
    </Panel>
  </Suspense>
</Suspense>

만약 이런 식으로 suspense 안에 suspense가 있는 경우,
BigSpinner는 모든 자식들을 다 기다릴 필요 없이, 내부 suspense 안의 컴포넌트를 제외한 biography만 기다린 뒤 바로 렌더링 한다고 해요!
안쪽의 suspsense는 Biography와는 상관없이 Albums가 준비되면 렌더링 합니다!

이번 주엔 합세랑 솝커톤 때문에 일정이 뽝뽝해서 요 정도까지만 공부를 했지만 나중에 더 공부를 해보고 싶어요!! (ㄹㅇ)
(참고 자료 : https://react.dev/reference/react/Suspense)

아티클 작성하시느라 너무나도 고생하셨습니다 :)

답글 달기
comment-user-thumbnail
2023년 11월 23일

React Query 진짜,,,, 은근 까다롭고 애증(?)의 관계인데 이렇게 잘 정리해주셔서 감사해요! 만약 data 패칭 시에, useMutation을 사용해 데이터를 생성, 삭제, 업뎃 등의 변화를 주면 그 변화와 동시에 데이터 변화를 클라 화면에서 보여주어야 할 때 queryClient.invalidateQueries 를 사용할 수 있더라구요!

저는 앱잼에서 react query를 처음 사용해 보았는데,
대체로 당시에 찾아보면 react query를 사용함에도 불구하고, useEffect, useState와 같은 훅을 사용하여 데이터 패칭을 하는 경우를 살펴 볼 수 있었습니다. (구글링할 때 예시 코드)
그런데, 이러한 훅의 도움을 받지 않고 사용하려고 하는게 react query라서요!
그래서 react query를 사용해서 data fetching을 할 때 커스텀 훅을 이용하는 방법을 앱잼에서 사용해 봤는데, 저는 꽤 좋았던 것 같습니다!

Query Invalidation에도 인자로 조건을 넘겨줄 수 있는데, 구체적인 키를 전달해서 특정 변수가 있는 쿼리를 무효화할 수도 있고,

queryClient.invalidateQueries({
  queryKey: ['todos', { type: 'done' },
})

//무효화o
queryClient.infalidateQueries({
queryKey: ['todos', {type: done'}],
queryFn: fetchTodoList,
}

//무효화x
const todoListQuery = useQuery({
  queryKey: ['todos'],
  queryFn: fetchTodoList,
})

변수나 하위 키가 없는 쿼리만 무효화하려는 경우에는 exact:true 옵션을 메소드에 전달해서 각 쿼리를 구분해서 무효화할 수도 있습니다. 만약 상단에 exact:true를 옵션으로 넣어준다면, 상단 예시에서의 무효화 결과는 반대가 될 거라고 하네요!

또, predicate 함수를 전달하면, 해당 쿼리를 무효화할 지 여부에 대한 boolean값을 반환한다고 합니다.

queryClient.invalidateQueries({
  predicate: query =>
    query.queryKey[0] === 'todos' && query.queryKey[1]?.version >= 10,
})

위와 같은 경우에는 version이 10이상일 경우에만 무효화를 시킬 수 있습니다.

꽤 재미있는 옵션들 같아서 공유하고자 합니다! predicate는 저도 이번에 첨 알았네용,,,

그리고 recoil 맨날 atom만 주구장창^^ 사용했지 selector나 recoil로 비동기처리를 할 수 있는 건 경험해볼 기회가 없었는데,,, 너무 주옥같은 글이었습니다! 감사해요!

답글 달기
comment-user-thumbnail
2023년 11월 23일

저는 전역 상태관리 라이브러리를 아직 사용해본 적이 없는데 이 글을 보고 전역상태 관리 라이브러리에 대한 개념을 이해했어요!! 구독이라는 개념이 뭔가 신선하게 다가오네요!

useQuerystale이라는 개념을 보면서 동작원리에 대해 찾아보았는데요, 현 시점부터 stale 상태가 될 때까지 남은 시간을 계산하여 timeout에 저장하고 setTimeout을 이용해 timeoutms 만큼의 시간이 지난 후에 currentResult가 stale 상태가 아니라면, fresh에서 stale로 상태를 바꿔주는 updateResult()를 호출하고 이를 통해 fresh 상태에서 설정한 staleTimems 만큼의 시간이 지나면 stale 상태로 바뀌게 된다고 합니다. 상상했던 방식으로 작동한다는 것을 알 수 있었습니다~!!

아 그리구 주의할 점은 stale 상태가 되었다고 해서 refetching이 곧바로 일어나는 것이 아니라, stale 상태가 되고 특정조건을 만족해야 refetching이 일어난다고 합니다!

답글 달기
comment-user-thumbnail
2023년 11월 23일

오홍 리액트 쿼리가 server State 라이브러리의 대표!! 라는 점 처음 알게 되었습니다. 굉장히 유익한 아티클 감사합니다..특히 앞에 state 개념도 상기시켜주셔서 더더욱 더 짚고 가는 느낌으로 좋았어요!
게다가 저는 react 하면 useState, useEffect를 거의 한몸으로 알고 있었는데 리액트 쿼리를 사용하면 이런 hook들의 필요성이 사라지다니!! 굉장히 흥미로웠습니다. 그래서 추가적으로 찾아보니

리액트 쿼리를 사용하면 useEffect나 useState가 필요한 상황이 줄어들 수 있지만, 이 라이브러리가 완전히 이들을 대체하는 것은 아닙니다. 리액트 쿼리는 특정 상황에서 훨씬 편리하게 데이터를 다룰 수 있게 도와주지만, 여전히 useEffect와 useState는 특별한 경우에 필요할 수 있습니다.

아하 그래서 특정 로컬 상태를 관리해야 하는 경우나 데이터를 가져오는 방식을 더 세밀하게 제어해야 하는 경우 등이 그런 상황에 해당합니다. 따라서 리액트 쿼리를 사용할 때에도 프로젝트의 특정 요구사항을 고려하여 결정하는 것이 중요하다는 생각이 듭니당!!
또한 recoil이 server state fetching과 전혀 무관하지 않은것도 아니다면서 이제 Recoil Asynchronous Data Query 를 알려주셨는데 이 부분도 굉장히 유익했습니다.

답글 달기
comment-user-thumbnail
2023년 11월 23일

저 분명 React Query랑 Recoli을 이 글을 통해서 처음 접해보았는데요.. 글을 다 읽고 나니까 약간 마스터한 기분이 들어요ㅎㅎ 글 대로만 한다면 완벽하게 해낼 수 있을 것만 같은 느낌이랄까요..? 그만큼 진짜 술술 읽히고 글의 흐름을 따라가니까 이해하기 쉬웠던거 같아요!

React Query에서는 서버상태 관리에 도움이 되는 다양한 도구들도 지원한다고 하는데요. 몇가지 말해보자면 사용자를 위해 데이터의 페이지네이션 혹은 무한스크롤의 상황에서 필요한 경우, 데이터를 가져올 수 있도록 하는 Lazy Loading 도구를 지원한다고 합니다! 또한 각 쿼리들은 key로 식별되기 때문에 페이지 내 여러 컴포넌트가 동일한 데이터를 요쳥하는 경우, 한번만 요청하도록 중복을 제거할 수도 있다고 합니다. Query Caching과는 조금 다른 느낌인거 같기도 하네요.. React Query의 다양한 기능을 활용해볼 수 있도록 좀 더 공부를 해보고 싶어졌어요!

제 React Query와 Recoil의 첫 시작을 도와준 너무나도 유익한 아티클 감사드려요♡

답글 달기
comment-user-thumbnail
2023년 11월 23일

아티클 처음부터 state의 개념이 무엇인지 client state와 server state의 개념으로 나누어서 설명해주셔서 state의 개념에 대해 더욱 확실히 알 수 있어서 너무 좋았어요!!!

저는 이번 아티클에 언급된 라이브러리를 한번도 사용해본 적이 없는데요. 이번 아티클만 보고도 바로 적용할 수 있을 만큼 사용 방법이 자세히 나와있어서 읽기 너무 편했어요!!!

recoil로 server state를 관리할 수 있다는 점을 보고, redux에서 비동기 처리를 어떻게 하는지 궁금해서 찾아봤는데, redux 자체적으로는 지원하지 않고, redux-thunk, redux-saga같은 미들웨어에서 해당 기능을 제공한다고 하네요!! 방법을 살펴봤는데 굉장히 복잡해서,,,,, react-query를 애용해야겠어요 ㅎㅎ 참고용 링크 남깁니다!!

https://velog.io/@ysg81/React-react-redux-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%AC

다뤄보지 않은 라이브러리를 알기 쉽고 친절하게 설명해주셔서 정말 유익했던 아티클이었어요!!! 아티클 작성하느라 고생하셨습니다!!!!

답글 달기
comment-user-thumbnail
2023년 11월 23일

유용한 아티클 공유 감사합니다!
항상 client state, server state에 대해 헷갈렸었는데 이 아티클에서 잘 정리해주셔서 덕분에 평소 모호하게 알고 있던 부분을 짚고 넘어갈수 있었습니다!
또 server state 관리 라이브러리의 예시로 react query, client state 관리 라이브러리의 예시로 recoil에 대해 자세히 설명해주셔서 이해가 쏙쏙 잘 되었습니다~

recoil로 client state 전역 관리는 사용해 본 경험이 있는데, asynchronous recoil 즉, recoil의 selector로 promise를 반환하도록 하는 형식의 data fetching은 사용해 본 경험이 없어 아티클을 읽으면서 더 흥미로웠어요! react-query 역시 심화적으로 다뤄본적은 없어 이번 실습 내용이 더 기대 됩니다!!

전에 react-query 관련하여 서치하다가, Optimistic Update라는 흥미로운 개념을 발견했어서 공유해봅니다!
optimistic Update란 말 그대로 낙관적 업데이트로, 서버 업데이트시 UI에서도 어차피 업데이트 할 것이라는 (낙관적인) 가정으로 미리 UI를 업데이트 시켜주고,
그 후 서버를 통해 검증 받고 업데이트 or 롤백하는 방식을 말합니다!

좋아요와 같은, 사용자 경험을 위해 빠른 UI 업데이트가 필요한 경우 사용되는 방식이라고 해요!
react-query를 사용하면 onMutate, onError, onSettled와 같은 콜백 함수를 이용해 이 optimistic update를 쉽게 구현할수 있다는 장점이 있다고 해요!

아티클 쓰느라 고생 많으셨습니다 Ṑṑ

답글 달기
comment-user-thumbnail
2023년 11월 23일

리액트 쿼리에 대한 사용법을 상세하고 쉽게 설명해주셔서 이해에 큰 도움이 됐습니다.

저는 리액트 쿼리를 사용하면서 고려해볼 점들을 알아보았는데요
React Query Hooks을 custom hook으로 감싸는 것

React 구성 요소에서 useQuery 및 useMutation을 직접 호출하는 대신 고유한 custom hooks로 감싸는 방법을 선택할 수 있다고합니다. 이렇게 하면 데이터 계층과 프레젠테이션 계층이 분리되어 관심사를 명확히 구분할 수 있고 API fetch function을 React 컴포넌트에서 사용할 필요가 없습니다 (관심사 분리, 단일 책임 원칙)

필요한 곳에 상태를 가까이하는 것
상태를 관리하는 것에서 props drilling은 항상 문제가 되었습니다. 필요한 상태를 가까이 배치하고 페칭 로직을 가까이 배치하는 것이 이 문제를 해결하는데 도움을 줄 수 있다고 합니다.

stale한 것이 무엇인지와 오래된 쿼리와 자동으로 다시가져오는 경우에 대해서도 알아보았는데 다음과 같다고 합니다.

  • 쿼리 마운트의 인스턴스가 생성될 때
  • window가 다시 focus 될 때
  • 네트워크가 다시 연결될 때 쿼리는 re-fetch 간격과 함께 선택적으로 구성된다고합니다
답글 달기
comment-user-thumbnail
2023년 11월 23일

리액트 쿼리에 대한 사용법을 상세하고 쉽게 설명해주셔서 이해에 큰 도움이 됐습니다.

저는 리액트 쿼리를 사용하면서 고려해볼 점들을 알아보았는데요
React Query Hooks을 custom hook으로 감싸는 것

React 구성 요소에서 useQuery 및 useMutation을 직접 호출하는 대신 고유한 custom hooks로 감싸는 방법을 선택할 수 있다고합니다. 이렇게 하면 데이터 계층과 프레젠테이션 계층이 분리되어 관심사를 명확히 구분할 수 있고 API fetch function을 React 컴포넌트에서 사용할 필요가 없습니다 (관심사 분리, 단일 책임 원칙)

필요한 곳에 상태를 가까이하는 것
상태를 관리하는 것에서 props drilling은 항상 문제가 되었습니다. 필요한 상태를 가까이 배치하고 페칭 로직을 가까이 배치하는 것이 이 문제를 해결하는데 도움을 줄 수 있다고 합니다.

stale한 것이 무엇인지와 오래된 쿼리와 자동으로 다시가져오는 경우에 대해서도 알아보았는데 다음과 같다고 합니다.

  • 쿼리 마운트의 인스턴스가 생성될 때
  • window가 다시 focus 될 때
  • 네트워크가 다시 연결될 때 쿼리는 re-fetch 간격과 함께 선택적으로 구성된다고합니다
답글 달기
comment-user-thumbnail
2023년 11월 23일

좋은 아티클 감사합니닷!! ♥
우선 저는 매번 프로젝트를 할때마다, 클라쪽 데이터, 서버쪽에서 넘겨주는 데이터 이런식으로 표현해왔는데 ,, 이렇게 명확히 client state/server state로 구분할 수 있다는 것을 처음 알게 되었어욧 ,,
그리고 recoil 설명 중 mySelector 셀렉터는 myAtom 아톰에 대해 종속된다/의존한다/구독한다 라는 표현이 있는데, 저런 관계를 종속한다, 의존한다 이런식으로 표현하는건 알고있었지만 구독한다라고도 표현한다는건 이번에 처음 알게 되었습니다!! 사실 개발도 결국 영어 번역체가 많아서 이런 용어 하나조차도 아예 모르는 상태면 인지하지 못한채 넘어가기 쉬운데, 덕분에 알게 되어서 넘 좋았어요!
한가지 궁금했던건 recoil과 redux를 비교하는 서론에서, recoil이 명확한 단점이 있으면서도 가장 React스러운 상태관리라는 철학이 있다고 하셨는데, 이게 어떤걸 의미하고 왜인지 궁금했습니다! 더 찾아보니, 애초에 Recoil을 소개하는 공식 문서 자체에서 recoil을 작고 React스럽다라고 언급했더라구요! 그리고 그 이유로, 'recoil은 react처럼 작동한다. 앱에 추가하여 빠르고 유연하게 공유되는 상태를 사용할 수 있다(Recoil works and thinks like React. Add some to your app and get fast and flexible shared state.)'라고 언급되었었습니다. 공식 문서에도 더 구체적인거는 안나와서, 이번 실습때 직접 작성해보며 경험해보고싶어요!!
좋은 아티클 넘 감사합니닷!!

답글 달기