-
Notifications
You must be signed in to change notification settings - Fork 0
Description
useQuery
개괄
React-Query, 또는 Tanstack-Query(최신 버전의 이름)는 서버에 값을 요청하고 가져오는 fetch 동작을 깔끔하고 선언적으로 수행하기 위해 이용되는 라이브러리이다. 그 중 useQuery는 GET을 할 때 사용하는 훅으로, 서버에서 GET 요청을 보내 가져온 값을 저장하고 새로고침하는 역할을 한다. (POST나 PUT 등의 요청은 useMutation을 사용한다).
최신 버전의 공식 문서
예시 (@nuagenic 코드에서 발췌)
const { data, error, status } = useQuery({
queryKey: ["project", id],
queryFn: () =>
fetch(`/api/mock/project/${id}`).then((res) =>
res.ok ? res.json() : Promise.reject(res),
),
staleTime: 1000 * 60,
gcTime: 1000 * 60 * 60,
retry: 0,
});queryFn
useQuery 훅의 동작은 다음과 같다.
- queryFn 함수를 자동으로 실행시켜 값을 가져온다
- 가져온 값을 data에 저장하고, 필요시 queryFn을 추가적으로 실행시켜 값을 새로고침한다
리액트 쿼리의 특징은 1에서 queryFn을 개발자가 직접 콜하지 않아도, 자동으로 작동이 된다는 것이다. 즉 react-query 라이브러리의 어딘가에는 다음과 비슷한 코드 조각이 있을 것이다.
const response = await queryFn(); // 서버 데이터를 가져오니 await을 한다고 가정하자.
setData(response); // data 부분에 응답을 저장하는 가상의 함수.주목해야할 것은, queryFn이 어떠한 인자도 없이 콜 된다는 것이다. queryFn에 특정 인자를 넣어서 작동시키려면 개발자가 직접 함수를 콜해줘야 할 것인데, 아까 1에서 보았듯 훅이 자동으로 함수를 실행시켜 값을 가져온다고 했다. 애초에 GET 요청에는 body를 보낼 수 없으니, hook을 선언하는 시점에서 한 번 등록된 함수를 계속 재사용하게 만들어도 문제가 없다고 판단한 것 같다.
여튼, queryFn이 제대로 동작하려면 위와 같은 이유에서 아무 인자를 받지 않는 함수여야 한다. 예시에도 () =>fetch("엔드포인트").then(~) 꼴의 함수를 사용한 모습을 볼 수 있다. 만약 fetch("엔드포인트").then(~)이나 (id:number)=>fetch("엔드포인트"+id).then(~)과 같은 함수를 넣는다면 제대로 동작하지 않을 것이다.
그런데 우리는 종종 GET 요청에도 인자를 넣고 싶은 기분을 느낄 때가 있다. 바로 project/${project_id}와 같은 동적 엔드포인트에 요청을 보내야 하는 경우이다. 이럴 때는 어떻게 해야할까? 기본적으로는 위의 예시처럼 ${project_id}와 같은 리터럴 문법을 이용해 엔드포인트를 다르게 해줄 수 있을 것이다. 그런데 만약 api 함수를 따로 분리하고 싶다면, 어쩔 수 없이 함수에 인자를 넣어야만 할 텐데 어떻게 하면 좋을까?
고차 함수를 이용한 코드 분리
바로 ()=>fetch() 꼴의 함수를 리턴하는 고차 함수를 이용하는 것이다. useQuery는 훅이니 반드시 컴포넌트 내에서만 사용할 수 있고, 동적인 컴포넌트가 마운트 될 때 project_id와 같은 동적 라우팅 정보는 이미 결정된다. 다시 말해 useQuery를 훅을 콜하는 순간에 project_id의 값은 반드시 정해져 있다는 것이다.
그렇다면 (id)=>()=>fetch()와 같은 모양의 고차 함수가 있다고 해보자. 이 함수(편의상 functionA로 지칭)에 id 인자를 넣어 콜하면 queryFn 필드가 요구하는 인자가 없는 함수가 리턴된다. 따라서 다음과 같이 선언된 useQuery 함수는 아무 문제 없이 작동할 것이다.
const id = props.params.id;
const {data, status} = useQuery({
queryKey: ["some", "query", "key"],
queryFn: functionA(id) // ()=>fetch(~~~) 꼴의 함수와 동일
});코드를 분리한 예시
위 내용을 종합하여 예시 코드를 고쳐보면 다음과 같을 것이다.
const getProjectById = (id:string) => () => fetch(`/api/mock/project/${id}`).then((res) => res.ok ? res.json() : Promise.reject(res));
function Component () {
const id = props.params.id; // id 가져오기
const { data, error, status } = useQuery({
queryKey: ["project", id],
queryFn: getProjectById(id) // ()=>fetch(`/api/mock/project${id}`).then(~~) 과 동일
});
return ...(이하 생략)
}queryKey
추후 작성 예정.