🏝️ React-Query란
웹 애플리케이션에서 서버 상태를 가져오고, 캐싱하고, 동기화하고, 업데이트하는 것을 쉽게 만들어주는 상태 관리 라이브러리입니다.
- TanStack Query
리액트로 애플리케이션을 개발하면, 보통 useState와 useEffect를 사용하여 데이터와 상태를 관리합니다.
이 과정에서 상위 컴포넌트에서 하위 컴포넌트로 props를 깊게 내려줘야 하는 상황('props drilling')이나 공통된 데이터를 여러 컴포넌트에서 사용하는 경우가 존재합니다.
이때 리액트의 컨텍스트(context API)를 사용할 수 있지만, 애플리케이션의 규모가 크거나 구조가 복잡하다면 컨텍스트만으로 상태를 관리하는 것은 한계가 있습니다.
따라서
- 반복적인 데이터 fetching 코드 감소
- 기존 데이터 캐싱
- 비동기 작업의 상태 관리
- 서버와 클라이언트 단의 데이터 동기화
- 데이터 관리 최적화
와 같은 이점을 얻기 위해 react-query(이하 리액트-쿼리)와 같은 상태 관리 라이브러리가 등장했습니다.
🏝️ React-Query를 사용하는 이유
여러 상태 관리 라이브러리 중에서도 리액트-쿼리는 API 통신 및 비동기 데이터 관리에 특화된 라이브러리입니다.
리액트-쿼리는 'useQuery'라는 훅을 사용하여 서버에 필요한 데이터를 비동기적으로 요청합니다.
아래는 리액트-쿼리를 사용하는 예시입니다.
import { useQuery } from '@tanstack/react-query'
function App() {
const info = useQuery({ queryKey: ['todos'], queryFn: fetchTodoList })
}
먼저 queryKey에는 콜백 함수의 결과값으로 반환되는 프로미스 값을 가리킬 고유한 키 값을 지정합니다.
다음으로 queryFn에는 프로미스 값을 반환하는 함수를 작성합니다.
결과적으로 useQuery 훅은 { data, error, isLoading, isError,... }와 같은 프로퍼티 값이 담긴 객체를 반환합니다.
객체 프로퍼티를 더 자세히 알고 싶다면 공식 문서를 참고해주세요
리액트-쿼리를 사용하면 얻게 되는 이점은 다음과 같습니다.
1. 데이터 캐싱 및 재사용
사용자가 여러 컴포넌트에서 동일한 데이터를 사용하는 경우 캐싱된 데이터를 재사용하게 됩니다.
useQuery 훅을 사용하여 데이터를 호출했을 때,
동일한 queryKey로 호출한 기록이 존재한다면 캐시 되어 있던 해당 데이터를 재사용하고,
그렇지 않다면 queryFn을 호출하여 새롭게 데이터를 받아옵니다.
참고로 현재의 데이터가 최신 상태라고 보장하는 시간을 stale time이라고 하는데,
stale time이 지나 해당 데이터가 최신 상태가 아니게 되면, 리액트-쿼리는 다시 네트워크 요청을 보내 새로운 데이터를 받아옵니다.
이때 따로 stale time을 지정하지 않고 디폴트 값인 0(밀리초)을 사용한다면, 매번 새로운 요청을 보내게 되어 결국 캐싱된 데이터를 사용하지 않게 됩니다.
따라서 stale time과 cache time을 잘 설정해야 캐시 된 데이터를 활용할 수 있습니다.
2. 간편한 상태 관리
리액트-쿼리는 useQuery 훅을 통한 비동기 요청의 로딩(isLoading, isPending)/성공(data)/실패(isError, error) 등의 상태를 제공합니다.
따라서 개발자가 별도로 상태 관리 로직을 작성할 필요 없이 반환되는 객체에서 원하는 프로퍼티 값을 사용하면 됩니다.
아래는 비동기 요청 시 반환되는 객체 속 상태를 활용하는 예시입니다.
import { useQuery } from '@tanstack/react-query';
function Todos() {
const { isPending, isError, data, error } = useQuery({
queryKey: ['todos'],
queryFn: fetchTodoList,
});
if (isPending) return <span>Loading...</span>
if (isError) return <span>Error: {error.message}</span>
return (
<ul>
{data.map((todo) => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
);
}
3. 서버 사이드 렌더링(SSR) 지원
렌더링 전에 서버에서 데이터를 미리 받아와 클라이언트에게 초기 데이터로 전달이 가능합니다.
이를 통해 처음에 화면이 로딩되는 시간을 단축하고, 웹 사이트의 SEO(검색 엔진 최적화)를 향상할 수 있습니다.
4. 낙관적인 업데이트(optimistic update)
새로운 데이터 업데이트 시 서버의 응답을 기다리지 않고 UI에 먼저 추가한 뒤에,
서버에게 보낸 요청이 성공한 경우 먼저 추가한 데이터 상태를 유지하고, 실패한 경우 이전 상태로 되돌리게 됩니다.
리액트-쿼리 사용법에 대한 내용은 다음 블로그를 참고해주세요
읽어주셔서 감사합니다:)