[react-query] query key와 query function

mia·2023년 5월 9일
0

통신해서 가져온 데이터를 chashing해두었다가 사용하기 위해 react-query를 사용했다.

그 중 조금 이해가지 않았던 query key와 query function에 대해 정리해보려고 한다.

react-query의 사용

먼저 react-query를 사용하기 위해서는 사용하고자하는 컴포넌트에 QueryClientProvider를 감싸주어야한다.
queryClient와 Provider를 사용하는 것은 React의 useContext()의 개념을 이해하면 이해하기 쉽다.
useContext

// root.jsx

const queryClient = new QueryClient();

export default function Root() {
	return (
    	<>
      		<SearchHeader />
      		<QueryClientProvider client={queryClient}>
      			<Outlet />
      		<QueryClientProvier/>
      	</>
    )
}

이제 사용하고자 하는 곳에서 useQuery를 사용하면 된다.

useQuery

cont result = useQuery('todos', fetchTodoList);

useQuery의 return 값인 result는 object이며 다양한 속성을 가지고 있다.

  • 'isLoading' : 데이터를 가져오는 중임을 나타낸다.
  • 'isError' : 에러가 발생했음을 나타낸다.
  • 'isSuccess': 데이터를 성공적으로 가져왔음을 나타낸다.
boolean값으로 나타나는 위의 속성 말고도 아래의 값들도 가져올 수 있다.
  • 'error' : error의 정보를 알려준다.
  • 'data' : 성공적으로 가져온 데이터를 보여준다.

'isLoading'과 'error', 'data'를 사용할 것이다.

// Videos.jsx

export default function Videos() {
	const {query} = useParams();
  	const {isLoading, error, data: videos} = useQuery(...)
    // data는 videos라는 이름으로 가져올 것이다.
}

이제는 useQuery()에 필요한 query key와 query function을 넣어 줄 것이다.

query key

위에서 말했던 것 처럼 react-query는 useContext()의 아이디어와 같기 때문에 Provider 내의 자식컴포넌트 내에서는 모두 가져온 데이터를 자유롭게 가져다 쓸 수 있다.
따라서 고유한 키를 부여해야만 나중에 해당하는 키를 통해 데이터를 가져올 수 있다.

useQuery의 첫번째 요소가 바로 이 고유한 key인 query key이다.
쿼리키는 고유해야하고 간단해야하기 때문에 TanStack에서는 한번에 여러개의 쿼리키를 배열로 사용하여 고유성을 높일 수 있도록 했다.

// Videos.jsx

export default function Videos() {
	const {query} = useParams();
  	const {isLoading, error, data: videos} = useQuery(['videos', query], ...)
}

위의 코드를 보면 쿼리키를 배열의 형태로 담아주었다.
첫번째 videos는 해당 데이터가 video들을 가져옴을 암시 할 수 있으며 두번째 요소인 query를 통해 검색 결과가 있을 때와 없을 때의 데이터를 다르게 가져올 수 있도록 했다.
이처럼 쿼리키 여러개를 배열로 담아주어 조금 더 간결하고 명확하게 고유성을 유지할 수 있다.

query function

마지막 쿼리 함수이다. 쿼리 함수는 promise를 반환해야 한다.

이 부분을 이해하기 위해 나는 캡틴판교님의 자바스크립트 비동기 처리와 콜백 함수, 자바스크립트 Promise 쉽게 이해하기, 자바스크립트 async와 await를 읽고 시작했다.

일단 프로미스를 간단하게 요약하자면 자바스크립트 비동기 처리에 사용되는 객체로 어떠한 행동(보통 데이터 받아오기와 같은 시간이 걸리는 행동)을 하고 성공했을 때의 결과와 실패했을 때에 따라 다른 결과를 보여주는 객체이다.
결국 성공해야 data를 보여주고 실패해야 error를 보여주기 때문에 어쩌면 당연한 것이 아닐까 싶다.
다행히 우리가 사용하는 fetch api는 promise를 반환한다. 하지만 조금은 주의해야할 것이 있다.

// Videos.jsx

export default function Videos() {
	const {query} = useParams();
  	const {isLoading, error, data: videos} = useQuery(
      ['videos', query], 
      fetch(`/data/${query ? 'search' : 'popular'}.json`)
      .then(res => res.json())
      .then(data => data.item))
}

이렇게 하면 Query data cannot be undefined. 라는 오류가 뜬다.
왜냐하면 위의 함수가 promise를 반환하지 않았기 때문이다.
위의 상황은 함수가 호출되었을 때 리턴하는 것이 아니라 선언했을 때 리턴하기 때문에 우리가 원할 때 data를 반환하지 않는다.
우리가 할 수 있는 방법으로는 2가지가 있다.

  1. 화살표 함수 이용
...useQuery(
      ['videos', query], 
      () => fetch(`/data/${query ? 'search' : 'popular'}.json`)
      .then(res => res.json())
      .then(data => data.item))

위 처럼 중괄호 없이 호출하거나 아래와 같이 중괄호 + return의 조합으로 사용해도 된다.

...useQuery(
      ['videos', query], 
      () => {
        return fetch(`/data/${query ? 'search' : 'popular'}.json`)
      .then(res => res.json())
      .then(data => data.item))
      }

화살표 함수를 사용하면 호출되었을 때 리턴이 된다.

  1. async 사용!
    함수 앞에 async를 붙이면 해당 함수는 항상 프로미스를 반환한다.
    조금 더 확실하게 비동기적으로 함수가 작동하는 것을 막아줄 수 있다.
...useQuery(
      ['videos', query], 
      async () => await fetch(`/data/${query ? 'search' : 'popular'}.json`)
      .then(res => res.json())
      .then(data => data.item))

마지막으로 axios를 사용해 만든 최종본이다.

// Videos.jsx

export default function Videos() {
	const {query} = useParams();
  	const {isLoading, error, data: videos} = useQuery(
      ['videos', query], async () => 
      await axios
      .get(`/data/${query ? 'search' : 'popular'}.json`)
      .then(res => res.data.items);
      
      return (
    	...
    )
}
profile
노 포기 킾고잉

0개의 댓글