본문 바로가기

Frontend/React

[React] Server State&Client State,React-Query

728x90
프로젝트를 진행하면서
비동기로 처리되는 Server State와 Client State의 차이를 체감하고
React-Query를 공부한 기록을 남기려고 한다.

Server State, Client State

배경

  • 내가 Server State와 Client State의 차이를 체감한 것은 좋아요 기능을 구현했을 때이다.
  • 그 이유는 이 간단한 기능을 구현하는데 서버와 클라이언트의 입장을 모두 고려해야 하기 때문이다.
  • 서버
    • 좋아요 개수 증가 요청을 받는다.
    • DB에 좋아요 개수를 증가시키고, 응답을 보낸다.
  • 클라이언트
    • 하트 이모티콘을 누른다.
    • 바로 증가된 개수를 확인한다.
여기서 좋아요 개수는 Server State로 비동기 요청이 이루어지는 데이터다.
때문에 요청이 이루어지면 기존의 값은 더이상 유효하지 않으며,
서버 DB의 값을 변경시키고 변경된 값을 바로 클라이언트 측에 보여줘야 한다.

Server State

  • 위에서 말한 것처럼 Server State는 비동기적으로 처리되는 서버의 데이터와 결부된 State이다.
  • 이는 여러 클라이언트에 의해 수정될 수 있는 값이기도 하다.

Client State

  • Client State는 컴포넌트의 state와 같은 클라이언트가 소유하는 State이다.
  • 여러 클라이언트에 의해 수정될 수 없는 값이다.
지금까지 위 두 State에 대한 구분이 명확하지 않았다.
Server State에서 필요한 작업은 다음과 같다.
- 비동기 요청과 연결되어 있는 상태값
- 해당 상태값이 변할 때마다 이루어지는 비동기 요청
- 변경된 서버 데이터를 상태값에 바로 반영

이 필요한 작업 그 이상을 해주는 것이 바로 React-Query이다.

React-Query

  • 공식문서에서는 React-Query의 핵심 개념이 세 가지 있다고 말한다.
    • Queries
    • Mutation
    • Query Invalidation
  • 이번 기록의 방향성은 React-Query의 필요성 체감, React-Query의 전체적인 그림과 핵심 기능이다.
  • 자세한 사용법과 사용하면서 겪는 이슈에 대한 기록 또한 계속할 예정이다.

Installation

  • 먼저 React-Query를 설치한다.
npm i @tanstack/react-query
yarn add @tanstack/react-query

Start

import Todos from './Todos';
import {QueryClient,QueryClientProvider} from '@tanstack/react-query';

function App() {
  queryClient=new QueryClient();
  return (
    // Provide the client to your App
    <QueryClientProvider client={queryClient}>
      <Todos />
    </QueryClientProvider>
  )
}
  • 먼저 Query를 사용하기 위해서 QueryClient를 생성한다.
  • QueryClientProvider는 자식 컴포넌트들에게 생성한 QueryClient를 제공한다.

Queries

  • 앞서 말한 서버의 데이터와 결부된 State값이 바로 Query이다.
  • query에서 가장 중요한 것은 unique key, QueryFn이다.
    • unique key : query는 특정 key값을 가지는데 이 key를 통해 다른 컴포넌트들에서도 접근이 가능하다.
    • QueryFn : 서버의 데이터와 연결되어 있으므로 해당 데이터를 불러올 함수가 QueryFn이다. QueryFn은 Promise를 반환하는 함수이다.
  • 이렇게만 보면 잘 이해가 가지 않을 수도 있다.
  • 위의 좋아요 기능을 예시로 들면,
    • query : 특정 게시물의 좋아요 개수
    • QueryFn : 서버로부터 좋아요 개수 get 요청을 보내는 함수
query는 앞서 말한 대로 서버의 데이터와 관련 있는 값이다.
따라서 QueryFn은 서버의 데이터를 수정하기보다는 가져오거나(get) 등록하는(post) 함수이다.
그렇다면 서버의 데이터를 수정하는 작업은 어떻게 처리할까?

위에서 언급한 Mutation이 바로 그런 작업을 하는 개념이다.
  • Mutation은 뒤에서 다루도록 하고, Query를 사용하는 방법을 더 보자.

Query

const query = useQuery(['todos'], getTodos)
  • 공식문서에 있는 예시이다.
  • useQuery를 통해 query를 사용할 수 있다.
    • useQuery에는 위에서 말했듯이 unique key와 QueryFn이 필요하다.
    • 먼저 ['todos'] (배열 형태의 unique key)를 볼 수 있다.
      • ['todos',todoId] 형태로 전달할 수도 있다.
    • getTodos는 Promise를 전달하는 서버에 Todos를 요청하는 함수이다.
  • useQuery는 data, isLoading, isSuccess, isError 등 요청을 처리하는 모든 값들을 반환한다.

Mutation

const mutation = useMutation(postTodo, {
    onSuccess: () => {
      // Invalidate and refetch
      queryClient.invalidateQueries(['todos'])
    },
  })
  • 위의 코드 또한 공식 문서에 나온 예시이다.
  • 앞서 말한 대로 Mutation은 서버의 데이터를 수정하는 데 사용되는 개념이다.
  • useMutation을 통해서 Mutation을 사용할 수 있다.
    • 첫 번째 인자로 MutationFn을 전달하는데 이 함수가 바로 서버의 데이터를 수정하는 요청을 보내는 함수이다.      QueryFn과 마찬가지로 Promise를 반환하는 함수이다.
  • 단순히 수정 요청을 보내는 것만으로는 큰 메리트가 없다.
  • onSuccess를 같이 사용했을 때 Mutation은 React-Query의 핵심 개념이 된다.
우리가 원하는 작업은 수정 요청을 보냈을 때 수정된 서버의 데이터를 클라이언트 측에 바로 반영하는 것이다.
그렇다면 Mutation을 통해 수정 요청을 보내는 데에 그치는 것이 아니라 수정된 서버의 데이터를 반영해야 한다.

onSuccess가 그 역할을 한다.
MutationFn 요청이 성공했다는 것은 성공적으로 서버의 데이터를 수정했다는 것이고
onSuccess에서 서버의 데이터를 클라이언트 측에 반영하기만 하면 된다.

Query Invalidation

queryClient.invalidateQueries(['todos'])
  • invalidateQueries 메서드는 query를 refetch 시키는 기능을 한다.
  • 서버의 데이터가 변경되었으므로 기존의 query는 유효하지 않게 되고, 이를 알려줌으로써 query를 최신 상태로 업데이트하는 흐름이다.
  • 메서드에 unique key를 전달하면 해당 Query가 본인의 QueryFn으로 다시 서버의 데이터를 받아오고 최신화되는 것이다.

정리

  • React-Query의 핵심기능 3가지를 내가 이해한 바는 다음과 같다.
1. 서버의 데이터와 연결된 Query (unique key로 구분)
2. 서버의 데이터를 수정하는 Mutation
3. 서버의 데이터가 수정되었음을 인지하는 Mutation의 onSuccess Option
4. 수정된 서버의 데이터를 클라이언트 측에 반영하는 Query Invalidation

 

마치며

  • 새로운 것을 배울 때 그것이 도입된 배경과 그 기능의 철학을 정확히 이해하고, 내가 그 필요성을 체감하는 것이 얼마나 중요한지 느꼈다.
  • React Query에 대한 전체적인 흐름을 이해했을 뿐 세부 사항에 대해서는 아직 많이 부족하다고 생각한다.
  • 새롭게 알아가는 것에 대해서는 추가적으로 계속 기록을 남길 예정이다.

 

728x90