주어진 시간에 대해 시스템을 나타내는 것 → 언제든 변경될 수 있음. 문자열, 배열, 객체 등의 형태로 프로그램에 저장된 데이터이다.
개발자 입장에선 쉽게 생각해 오너십가지고 관리해야하는 데이터라고 할 수 있다.
모던 웹 프론트엔드 개발에서 프로덕 규모가 커지면서 더많은 상태가 생기고 결론적으로 관리해야할 상태가 많아짐
react에서 만약 최상단에 함수, 상태값들을 정의하고 props로 넘겨받으면 동작하는데는 문제가 없지만 props drilling이 발생하고 상태값 또한 많아지면 관리는 더욱 힘들어집니다.
또한 재사용 가능한 컴퍼넌트를 생성하는걸 중요시 생각하는 개발자 입장에서 과도한props로 넘겨받는 인자들은 재사용성을 떨어뜨립니다.
우린 이런 문제 해결을 위해 redux를 사용했었습니다.
리덕스 동작방식은 상태변화가 필요할때 action을 발생시킵니다. 이때 액션은 하나의 객체로 이루어져있습니다.
이때 액션생성함수(action creator)를 통해 액션을 생성하고 단순히 인자를 받아 액션객체형태로 만들어줍니다.
리듀서는 현재 상태와 새롭게 전달받은 액션 두가지를 파라미터로 받고 현재의 상태와 새로운 액션을 참조하여 새로운 상태값을 반환해주는 것을 리듀서라고 합니다.
그리고 이를 다시 store에 저장합니다.이런상황에서 서버상태관리를 위해 서드파티 라이브러리 saga를 도입했습니다. 그런데 아래와 같은 문제가 발생했습니다.
React Query is often described as the missing data-fetching library for React,but in more technical terms, it makes fetching, caching, synchronizing andupdating server state in your React applications a breeze
React 애플리케이션에서 서버 상태를 가져오고, 캐싱하고, 동기화하고 업데이트 하는것을 매우 쉽게 만들어준다는 뜻이다.
아래의 코드는 react-query공식문서의 샘플 코드이다.
import {
QueryClient,
QueryClientProvider,
useQuery,
} from '@tanstack/react-query'
const queryClient = new QueryClient()
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<Example />
</QueryClientProvider>
)
}
function Example() {
const { isLoading, error, data } = useQuery({
queryKey: ['repoData'],
queryFn: () =>
fetch('https://api.github.com/repos/TanStack/query').then((res) =>
res.json(),
),
})
if (isLoading) return 'Loading...'
if (error) return 'An error has occurred: ' + error.message
return (
<div>
<h1>{data.name}</h1>
<p>{data.description}</p>
<strong>👀 {data.subscribers_count}</strong>{' '}
<strong>✨ {data.stargazers_count}</strong>{' '}
<strong>🍴 {data.forks_count}</strong>
</div>
)
}
여기서 repoData는 쿼리 키값이고, key-value매핑 구조를 가진다.
useQuery는 Hook으로 수행되는 Query요청은 HTTP METHOD GET 요청과 같이 서버에 저장되어있는 상태를 불러와 사용할 때 사용합니다.
Mutation은 query와 다르게 데이터 업데이트 시 사용합니다. CRUD에서 useQuer가 Read였다면 나머지 Create,update,delete는 mutation으로 사용합니다. useMutation은 Hook으로 요청시 HTTP METHOD POST,PUT,DELETE요청과 같이 서버에 side Effect를 발생시켜 서버의 상태를 변경할 떄 사용합니다.
// hooks/queries/inquiry.ts
import { useQuery } from 'react-query';
import * as API from 'api';
export const useGetInquiryList = (query: any, options: any) =>
useQuery('useGetInquiryList', () => API.getInquiriesList(qu
};
여기서 CRUD의 read즉 get메서드를 호출하기 위해 useQuery를 활용해 getInqurires란 API를 호출한다.
여기서 key값 useGetInquiryList에 response가 value로 매핑되게 될 것이다.
해당 훅스를 필요한 UI컴퍼넌트에서 호출해보자.
const getInquiryList = useGetInquiryList(query, {
onSuccess: (data: any) => {
console.log(data);
},
onError: (error: any) => {
alert(error.message);
}
});
따로 useEffect를 사용하지 않아도 default로 컴퍼넌트가 mount시 해당 함수가 실행될 것이다.
또한 response에서 성공시, 에러시 핸들링을 간단하게 처리할 수있다.
그럼 하위 컴퍼넌트에서 상태값을 어떻게 꺼내 사용할까?
import { useQueryClient } from 'react-query';
const queryClient = useQueryClient();
const testList = queryClient.getQueryData('useGetInquiryList')
console.log('ui compo level', testList);
이전에 useGetInquiryList라는 키값에 response값을 매핑했으니 해당 키값을 통해 접근해 값을 받아올 수있다.
Context Api에 있습니다. 실제 react-query라이브러리 코드를 보면 queryClient내부적으로 Context를 사용합니다
클라이언트 상태관리를 하는데 있어 redux, zustand, recoil, mobx어떤 클라이언트 상태 관리 라이브러리를 사용해도 클라이언트 상태관리들만 남아 목적에 맞게 심플하다.
서버 상태값들은 react-query를 통해 간단하지만 아주 직관적으로 처리가 가능하다.
무엇보다 서버 상태와 클라이언트 상태 관리를 확실히 분리하여 사용할 수있다는 장점이 있다.
수많은 useQuery의 키값을 어떻게 관리할지 고민이다. ui컴퍼넌트 내부에서 useQuery를 이용해 해당 키값에 밸류를 매핑해서 사용하는건 좋다.
하지만 이렇게 컴퍼넌트 레벨에서 해당 쿼리를 마구 생성하면 점점 프로젝트가 커지면 저 방대한 양의 키값을 어떻게 처리할까...?