React Query를 사용해보자

우빈·2023년 5월 30일
3
post-thumbnail

API를 더욱 쉽게 핸들링하는 라이브러리인 리액트 쿼리에 대해 알아보자.

리액트 쿼리는 서버의 값을 클라이언트에 가져오거나, 캐싱, 값 업데이트, 에러핸들링 등 비동기 과정을 더욱 편하게 도와주는 라이브러리다.
그렇다면 리액트 쿼리를 사용했을 때의 장점은 무엇일까?

리액트 쿼리의 장점

공식 문서에서 말하는 리액트 쿼리의 장점은 다음과 같다.

UPDATE시 자동으로 GET 수행

API를 업데이트했을 때 자동으로 GET을 수행하여 이미 캐싱되어있던
데이터를 GET을 통해 다시 불러온다.

데이터가 오래 되었다고 판단시 다시 GET 수행

데이터가 오래 되었다고 판단되면 다시 GET을 수행하여
새로운 데이터를 불러온다.

동일 데이터를 여러번 요청할 경우 한 번만 요청

멱등한 데이터를 여러번 요청한다고 판단 시 리액트 쿼리는
알아서 자신이 데이터를 판단하고 한 번만 요청한다.

비동기 과정을 선언적으로 관리 가능

리액트 쿼리에서 제공하는 훅을 변수로 선언하여 사용할 수 있다.

리액트 훅과 비슷한 문법으로 친근한 사용법

이렇게 리액트 쿼리 하나만으로 굉장히 많은 장점이 있다!!
리액트 쿼리를 유용하게 사용한다면 굉장히 깔끔하고 효율적으로
API를 핸들링할 수 있다.

그럼 이제 리액트 쿼리를 본격적으로 세팅해보자!

리액트 쿼리 세팅하기

리액트 쿼리는 리액트의 루트 파일에서 쿼리 클라이언트를 세팅해야 한다.
queryClient를 new 키워드를 사용해 생성하고, 리액트 쿼리에서 지원하는 Provider 컴포넌트로 App을 감싸주면 된다.

queryClient를 생성할 때, 기본적인 옵션도 설정해줄 수 있다.

import { QueryClient, QueryClientProvider } from 'react-query'
import { ReactQueryDevtools } from 'react-query/devtools'

const queryClient = new QueryClient({
    defaultOptions: {
    queries: {
      retry: 0, // 요청 실패했을 경우 재요청 횟수
      suspense: true // React.Suspense를 사용할 때
    }
  }
})

const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement)
root.render(
	<QueryClientProvider client={queryClient}>
		<ReactQueryDevtools initialIsOpen={true} />
		<App />
	</QueryClientProvider>
)

suspense

suspense는 React.Suspense와 함께 사용할 수 있는 옵션이다.
queryClient의 defaultOptions에 suspense를 true로 설정해주면 전처리하여 사용할 수 있다.

const queryClient = new QueryClient({
	defaultOptions: {
		queries: {
			retry: 0,
			suspense: true,
		},
	},
})

또는 함수마다 별개로 suspense를 적용시킬 수 있다.

const { data } = useQurey("user", ()=>axios.get(''), { suspense: true });

return (
  <Suspense fallback={<div>loading</div>}> {/* loading때 보여주는 UI */}
    <ErrorBoundary fallback={<div>error</div>}> {/* error가 났을 때 보여주는 UI */}
      <div>{data}</div> {/* loading 후 보여지는 data */}
    </ErrorBoundary>
  </Supense>
);

QueryCache

쿼리에 대한 성공과 실패를 전처리할 수 있는 기능이 쿼리 캐치이다.
이는 QueryClient를 생성할 때 옵션으로 새로 넣어 설정해줄 수 있다.

const queryClient = new QueryClient({
	queryCache: new QueryCache({
		onError: (error) => {
			console.log(error)
		},
		onSuccess: (data) => {
			console.log(data)
		},
	}),
})

이 외에도 여러가지 유용한 리액트 쿼리의 기본 옵션을 설정할 수 있으니,
공식 문서에서 본인의 서비스에 필요한 옵션들을 찾아보자!

이제 모든 세팅이 끝났으니 리액트 쿼리를 사용해보자.

React Query Hooks

React Query에는 여러가지의 훅들을 제공한다.
이 훅을 사용하여 API를 설정할 수 있다! 바로 알아보자.

useQuery

useQuery는 데이터를 get 하기 위한 api다.
첫번째 파라미터로 고유한 키가 들어가고, 두번째 파라미터로 api 호출 함수가 들어간다.
고유 키를 다른 컴포넌트에서도 사용하여 호출할 수 있기 때문에, 모듈화하여 사용이 가능하다.
비동기로 작동하며, enabled라는 키워드를 통해 동기적으로 작동시킬 수 있다.

const Hello = () => {
	const { isLoading, isError, data, error } = useQuery('hello', () => axios.get(''), {
		refetchOnWindowFocus: false, // 사용자가 해당 창을 재방문 했을 때 재실행 여부
		retry: 0, // 실패시 재호출 횟수
		onSuccess: (data) => {
			console.log(data)
		},
		onError: (e) => {
			console.log(e.message)
		},
	})
}

이런 식으로 따로 state를 사용하지 않고도 값을 핸들링할 수 있는 장점이 있다.

const { status, data, error } = useQuery('todos', () => axios.get(''))

이렇게 따로 isLoading과 isError를 나누지 않고 status로도 처리할 수 있다.

동기적으로 실행하기

다음 코드는 world가 true일 때 useQuery를 실행하는 코드이다.
이런식으로 enabled라는 키워드를 통해 동기 작업 또한 가능하다.

const { status, data, error } = useQuery('hello', () => axios.get(''), {
	enabled: !!world,
})

useQueries

useQueries는 useQuery를 하나로 묶어 처리하는 문법이다.

const res = useQueries([
	{
		queryKey: 'getOne',
		queryFn: () => axios.get(''),
	},
	{
		queryKey: 'getTwo',
		queryFn: () => axios.get(''),
	},
])

res 값에 배열 형태로 값들이 들어오며, 배열 내에는 여러가지 상태와 data가 함께 들어가있다.

useMutation

useMutation은 서버에 데이터 변경 작업을 요청할 때 사용한다.
mutationFn은 프로미스 처리가 이루어지는 함수로, 보통 axios등의 요청 함수를 넣는다.

const savePerson = useMutation({
	mutationFn: () => axios.post('', ''),
})

mutate

mutate를 통해서 이벤트가 발생되었을 때 값을 useMutation에 저장해줄 수 있다.
mutate를 사용하면 작성한 내용이 실행된다. 또한 세 번째 인자를 통해 try catch finally 구문과
같이 데이터를 따로 처리할 수 있다.

const [person, setPerson] = useState({
	id: 1,
	name: '',
	nickname: '',
	isLogin: false,
})

const users = useMutation((user) => axios.post('http://localhost:8080/user', user), {
	onSuccess: (res) => {
		console.log(res)
	},
	onError: (err) => {
		console.log(err)
	},
	onSettled: () => {
		console.log('end') // finally
	},
}) // useMutate 정의

const onSavePerson = () => {
	// mutate에서도 사용 가능
	users.mutate(person, {
		onSuccess: (res) => {
			console.log(res)
		},
		onError: (err) => {
			console.log(err)
		},
		onSettled: () => {
			console.log('end') // finally
		},
	})
}

invalidateQueries

invalidateQueries를 통해 리액트 쿼리의 유효성을 제거해주어 새롭게 서버에 요청을 보낼 수 있다.
useQuery에 값이 저장되어있는 상태에서 useMutation을 사용하고 나면 캐싱된 값을 읽어오기 때문에,
유효성을 제거해준 다음 다시 요청을 보내게 하여 새로 업데이트된 값을 불러오게 도와주는 키워드이다.

const queryClient = useQueryClient() // 등록된 quieryClient 가져오기

const users = useMutation((user) => axios.post('', user), {
	onSuccess: (res) => {
		console.log(res)
		queryClient.invalidateQueries('user') // useQuery에서 사용하는 유효성 제거
	},
	onError: (err) => {
		console.log(err)
	},
	onSettled: () => {
		console.log('end') // finally
	},
})

setQueryData

setQueryData는 invalidateQueries를 통해 유효성을 제거하지 않고 값을 업데이트하는 방식이다.

const queryClient = useQueryClient()

const users = useMutation((user) => axios.post('', user), {
	onSuccess: (res) => {
		console.log(res)
		queryClient.setQueryData('user', (data) => {
			const res = data
			res.data.push(user)

			return res
		})
	},
	onError: (err) => {
		console.log(err)
	},
	onSettled: () => {
		console.log('end') // finally
	},
})

이렇게 코드를 짜면 서버에 다시 요청을 보내지 않고도 값을 업데이트할 수 있다.

profile
프론트엔드 공부중

0개의 댓글