통신해서 가져온 데이터를 chashing해두었다가 사용하기 위해 react-query를 사용했다.
그 중 조금 이해가지 않았던 query key와 query function에 대해 정리해보려고 한다.
먼저 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를 사용하면 된다.
cont result = useQuery('todos', fetchTodoList);
useQuery의 return 값인 result는 object이며 다양한 속성을 가지고 있다.
'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을 넣어 줄 것이다.
위에서 말했던 것 처럼 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를 통해 검색 결과가 있을 때와 없을 때의 데이터를 다르게 가져올 수 있도록 했다.
이처럼 쿼리키 여러개를 배열로 담아주어 조금 더 간결하고 명확하게 고유성을 유지할 수 있다.
마지막 쿼리 함수이다. 쿼리 함수는 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가지가 있다.
...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))
}
화살표 함수를 사용하면 호출되었을 때 리턴이 된다.
...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 (
...
)
}