React Query 정리

sooki_m·2021년 12월 25일
0
post-thumbnail

React Query

스스로 공부하고 찾아보며 기술을 적용했기 때문에 분명 틀린 점이 있을 수 있습니다. 틀린 점은 댓글에 지적해주시면 감사하겠습니다.

회사에 입사하고 첫 실무에 들어가며 만난 프레임워크들은 입사 전에는 한 번도 사용해보지 않았던 것들이었다.(Next.js, react-query, tailwind...)

그래서 한 달 간 업무를 진행하며 기존 작성되어 있는 코드와 공식문서와 갓구글에 열심히 검색하며 습득한 지식으로 기능을 개발했다. 하다보니 이 기술들을 완벽하게 다 이해하고 쓰는 것은 아니기에 어설프게나마 내 언어로 정리하는 시간을 가지면 좋을 것 같다고 생각해서 첫 주자로 react query에 대해 글을 쓰게 되었다.
(최근에 tailwindcss 버전 업데이트하면서 엄청나게 삽질하고 파트장님께 틀린 지식도 많이 전파했는데 죄송합니다... 죄송해요... 제 맘 아시죠...? 사랑합니다 웹 파트... 만간에 이 삽질기도 정리해보도록 하겠습니다..)

현재 실무에서는 react query를 도입해 프론트 쪽의 상태 관리를 Context API와 함께 사용하고 있다.
react query의 아이디어는 간단하다.

프론트에서 (긴 시간) 상태 관리 하지 말고 서버에서 최신의 데이터를 자주 가져와서 view에 뿌려줘라.

맞는 말이지만 서버와 네트워크 통신하는 것은 여러모로 비용이 많이 들기 때문에 지금까지 그렇게 하지 않았던 것 아닌가? 뭐지? 하는 생각이 들었는데 막상 사용해보니 좋은 점이 많았다.

일단 react query는 클라이언트용 상태 관리 라이브러리가 아니다. 서버 사이드 상태 관리 라이브러리라고 하는데, 사실 이 용어의 명확한 정의는 잘 모르겠고 내가 생각했을 때 그렇게 지칭하는 이유는 react query를 사용해 API 호출을 했을 때 그 결과를 가져오는 상태에 대해 react query가 알고 있기 때문이다.

무슨 말이냐면 보통 프론트에서 작업을 하면 우리는 다음과 같이 작업을 하게 된다.

(모든 예제는 React를 기준으로 작성하였으며 실제 환경에서 돌려보지 않았기 때문에 에러가 있을 수 있습니다.)

const [state, setState] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useStaet(false);

useEffect(async () => {
   try {
    	const { data } = await axios.get('https://...');
      
      	setState(data);
      	setLoading(false);
    } catch {
    	setError(true);
    }
  
},[]);

return (error 
  	? <div>Error!</div> 
  	: <div>{ loading ? 'loading...' : state }</div>
);

보통 리액트를 사용하면 이런 식으로 컴포넌트 내에서 사용되는 상태를 useState로 관리한다. 서버에서 API 콜을 해서 데이터를 받아와야 하는 상황이면 loading 상태와 error 상태를 나눠서 관리하게 된다. loading이 true이면 보통 loading indicator를 노출하고 데이터를 다 받아 상태가 업데이트 되면 받아온 데이터를 뿌려주는 식이다.

그러나 React Query를 사용하면 이렇게 loading, error 상태를 직접 관리해주지 않아도 된다. 데이터를 받아오는 중인지, 에러가 났는지 이미 react query에서 알고 그 값을 반환해주기 때문이다.

useQuery

const queryFunction = async () => {
	return await axios.get('https://...');
};

const { data, isLoading ,isSuccess, isError } = useQuery(queryKey, queryFunction, option);

if (isLoading) return <div>loading...</div>;

if (isError) return <div>Error!</div>;

return <div>{data}</div>;

useQuery는 보통 http get method로 데이터를 불러올 때 사용하는데 이렇게 상태에 따라서 return 값으로 jsx를 쓸 수 있어서 훨씬 더 깔끔하게 상태에 따라 다른 화면을 노출할 수 있다.

queryKey의 경우에는 데이터 캐싱 때문에 반드시 필요한데 option에 staleTime 내에 같은 키의 데이터를 사용하면 api 콜을 또 하는 대신에 캐싱된 값을 리턴하도록 되어 있다. staleTime은 default가 0이므로 캐싱을 위해서는 꼭 staleTime을 설정해둬야 불필요한 API 콜을 줄일 수 있다.

queryFunction에는 api call을 하는 function을 넣을 수 있다. 현업에서는 바로 api call을 하는 function을 넣지 않고 custom hook을 사용하고 있다.

option에는 다양한 조건들을 넣을 수 있는데 api call이 실패했을 때 다시 시도할 횟수를 설정할 수도 있고(retry: boolean | number | (failureCount: number, error: TError) => boolean), 성공했을 때 onSuccess 콜백을 사용해 데이터 페치에 성공했을 때 콜백 함수를 실행할 수도 있다.
(지금 옵션을 찾아보니 retry 값의 default 값에 대한 내용이 없는데, 기억에 옵션을 주지 않은 query는 실패하면 계속해서 retry를 했던 거 같다. true로 설정되어 있는 것 같기도... 이건 다시 확인해보고 앞으로 사용할 때는 retry 값을 꼭 설정해둬야 할 것 같다.)

참고 문서 - useQuery 공식문서

useMutation

useMutation은 보통 데이터를 조작할 때 사용하는 query다. 즉 post, put, delete method를 사용할 때 같이 사용할 수 있다고 볼 수 있다.

useMutation을 사용할 때 queryKey는 옵션이고 queryFunction만 필수 파라미터로 넘겨야 한다. 이 function이 mutate라는 콜백으로 return 되어 다음과 같이 사용할 수 있다.

const mutationFunction = (data) => {
	return axios.put('https://...', data);
};

const { mutate, data } = useMutation(mutationFunction, option);

const onHandleMutate = async () => {
	await mutate('abc');
};
//mutate == mutationFunction
/*
공식문서 내 mutationFunction의 정의는 다음과 같다.
mutationFn: (variables: TVariables) => Promise<TData>
Required
A function that performs an asynchronous task and returns a promise.
variables is an object that mutate will pass to your mutationFn
*/

참고 문서 - useMutation 공식문서

이외에도 useInfiniteQuery 등 여러 객체를 사용해봤는데 차차 더 정리해보도록 하고 오늘은 여기서 마무리해보록 하겠다.

현재 하고 있는 업무 ESLint & Prettier 설정을 새롭게 하면서 엄청나게 또 삽질 많이 했는데 그것도 또 정리하면 좋을 것 같다.

fin.

profile
머쨍이 개발ing 😎

0개의 댓글