dolog
그동안 오해해서 미안하다! React Query 본문
서두
React Query가 정확히 어떤 라이브러리인지, 어떤 상황에서 사용해야 하는지 등 잘 모르고 잘못 사용했던 경험으로 인해 '굳이 사용해야 할까?' 라는 의문을 가지고 있었습니다.
하지만 현재 개인 프로젝트를 하면서 왜 React Query를 사용해야 하는지, 또 기존에 어떻게 잘못 사용했는지와 비효율적으로 비동기 상태를 관리했는지에 대해 개선한 경험을 적어보려합니다.
본문
먼저 React Query가 관여하는 부분은 queryFn의 실행 결과로 fulfilled 혹은 rejected 상태인 promise 객체가 반환된 이후입니다.
즉 비동기 요청을 관리해주는 라이브러리가 아닌 비동기 상태를 관리해주는 라이브러리인거죠.
저는 주로 프로필 데이터 등 넓은 영역에서 사용하는 상태를 전역 상태로 두어 사용했었는데 상태를 지역과 전역으로만 나누다 보니 상태가 어디서 사용되는지에만 초점이 맞춰져있었습니다. 그러나 React Query는 상태가 어디서 사용되는지가 아닌 어떤 종류의 상태인지가 핵심입니다.
또한 자주 서버 데이터와 동기화해줘야 하는 상황에서 데이터 자동 갱신으로 인한 사용자 경험을 개선 할 수 있었고, 로딩과 에러 상태에 관한 비동기적인 상태값까지 직관적이고, 편리하게 관리할 수 있어 개발자 경험까지 개선할 수 있었습니다.
① 잘못 사용한 예 : 단일 소스 공급원(Single Source Of Truth) 원칙 위반 + 불필요한 상태 관리가 이루어진 나쁜 코드입니다.
// useUserInfoQuery.ts
import { useEffect } from 'react';
import { useQuery } from '@tanstack/react-query';
import { getUserInfo } from '@/api/history';
import useHistoryStore from '@/stores/historyStore';
const useUserInfoQuery = (id: string) => {
const { setHistoryUser, profileImageUrl, displayName, grade, point } =
useHistoryStore();
const { data } = useQuery(['userEmail', id], () => getUserInfo(id));
useEffect(() => {
if (data) return setHistoryUser(data.data);
}, [data]);
return { profileImageUrl, displayName, grade, point };
};
export default useUserInfoQuery;
① 개선한 코드
// useUserInfoQuery.ts
import { useQuery } from '@tanstack/react-query';
import { getUserInfo } from '@/api/history';
const useUserInfoQuery = () => {
const { data:userInfo, isError, isLoading } = useQuery({
queryKey: ['userEmail'],
queryFn: () => getUserInfo()
staleTime: Infinity,
});
return { userInfo, isError, isLoading };
};
export default useUserInfoQuery;
코드 길이도 줄어들고 훨씬 직관적입니다. 또한 프로필 데이터은 한 번 받아오고 그 이후 다시 받아올 일이 거의 없으므로 staleTime을 Infinity로 해놓고 업데이트가 될 때만 상태를 갱신해줍니다.
② 잘못 사용한 예 : Watchlist 컴포넌트는 Home과 Market 컴포넌트에서 사용하기 때문에 두 번 호출이 됩니다. 하지만 주기적으로 갱신된 데이터를 받아와야 하는 상황이라면 비효율적인 코드입니다.
// ...
export default function Watchlist() {
const [market, setMarket] = useState<Market[]>();
useEffect(() => {
const getMarketInfo = async () => {
const marketDetail = await getMarketDetail();
setMarket(marketDetail);
};
getMarketInfo();
}, []);
// ...
② 개선한 코드
// useMarketDetailQuery.ts
import { useQuery } from '@tanstack/react-query';
import { getMarketDetail } from '../../_api/market';
const useMarketDetailQuery = () => {
const {
data: market,
isError,
isLoading,
} = useQuery({
queryKey: ['marketDetail'],
queryFn: () => getMarketDetail(),
refetchIntervalInBackground: true,
});
return { market, isError, isLoading };
};
export default useMarketDetailQuery;
// Watchlist.tsx
export default function Watchlist() {
const { market, isError, isLoading } = useMarketDetailQuery();
if (isError) return <p>Error!</p>;
if (isLoading) return <p>Loading...</p>;
return (
// ...
useQuery와 refetchIntervalInBackground를 사용하여 지속적인 데이터 갱신과 더불어 브라우저를 벗어난 환경에서도 리패칭이 될 수 있게 코드를 작성했습니다.
결미
React Query는 클라이언트 상태와 서버 상태를 분리하게 해주고, 서버 상태를 효율적으로 관리할 수 있게 도와주는 라이브러리입니다.
본문에 나온 것처럼 필요한 상황에서 알맞게 사용해야겠습니다.
아래 블로그를 참고하여 많은 인사이트를 받았습니다. :)
'톺아보기' 카테고리의 다른 글
병목 현상 해결 : 데이터 베이스 연결 주의하기 (0) | 2024.05.20 |
---|---|
크롬(Chrome)은 들어봤는데 크론(Cron)이요? (0) | 2024.05.17 |
비동기 처리 : callback에서 async, await으로 (0) | 2024.05.07 |
연로한 맥북과 MySQL (0) | 2024.04.30 |
AWS EC2에 대해 알아보기 Part 3 (0) | 2024.04.29 |