[TanstackQuery] 쿼리 키, 쿼리 함수

강연주·2024년 12월 9일

 🏝️ TanstackQuery

목록 보기
4/7

탠스택 쿼리는 쿼리 키를 기반으로 데이터를 캐시에 저장.
포스트 전체를 받아오는 쿼리를 ['posts']라는 쿼리키로 관리하는데, 왜 배열의 형태일까?

현재 구현하려는 코드스터딧 사이트는 홈 피드와 내 피드로 이루어져있다.
홈 피드에서는 모든 포스트를 다 받아와서 보여주고, 내 피드에서는 사이트에 로그인한 특정 유저의 포스트만 보여준다. 이때 백엔드로부터 특정 유저의 포스트만 받아오는 API 함수가 필요하다.

🖥️ export async function getPostsByUsername(username) {
  const response = await fetch(`${BASE_URL}/posts?username=${username}`);
  return await response.json();
}

위와 같이 가져온 데이터를 캐시에 저장하는 방법은? 전체 포스트를 저장하고 있는 ['posts'] 쿼리 키와는 구분돠는 방식으로 특정 유저의 포스트 데이터만 따로 저장하는 것이 좋을 것 같다. 아래처럼 계층적인 쿼리 키 지정이 가능하다.


🖥️ import { useQuery } from "@tanstack/react-query";
import { getPosts, getPostsByUsername } from "./api";

function HomePage() {
  const username = "codeit";
  const { data: postsDataByUsername } = useQuery({
    queryKey: ["posts", username],
    queryFn: () => getPostsByUsername(username),
  });
  console.log(postsDataByUsername);

  const result = useQuery({ queryKey: ["posts"], queryFn: getPosts });
  console.log(result);
  return <div>홈페이지</div>;
}

export default HomePage;

"함수를 실행하는 것""함수 자체를 전달하는 것"
1. queryFn: getPostsByUsername(username)

  • 문제 : 이렇게 작성하면 함수 getPostsByUsername이 즉시 호출되어 결과값이 queryFn에 전달된다.
  • 동작 방식 : getPostsByUsername(username)은 함수 호출이고, 호출 후 반환된 결과값(여기서는 Promise)이 queryFn에 설정되므로 useQuery는 이를 적절히 사용할 수 없다.
🖥️ javascript

// 예시
queryFn: getPostsByUsername(username); // 잘못된 사용
이 경우, queryFn은 "함수를 실행"한 결과를 담게 되고, useQuery는 이를 함수로 인식하지 못한다. queryFn에는 실행 가능한 함수가 필요하다.
  1. queryFn: () => getPostsByUsername(username)
  • 올바른 작성 방식 : 이 형태는 익명 화살표 함수를 사용하여 queryFn에 함수 자체를 전달한다.
  • 동작 방식 : queryFn은 나중에 useQuery가 실행해야 할 함수를 필요로 한다. () => getPostsByUsername(username)는 익명 화살표 함수로, 이 함수가 호출되었을 때getPostsByUsername(username)이 실행된다.
🖥️ javascript

// 올바른 사용
queryFn: () => getPostsByUsername(username);
여기서는 useQuery가 queryFn을 호출할 준비만 하며, 실행은 React Query가 적절한 시점에 수행.

이걸 아직도 헷갈리고 있네..🫠


이렇게 배열을 활용해 계층적인 쿼리 키 설정이 가능하고, 상황에 따라 다양한 파라미터를 활용해 쿼리 키를 설정할 수도 있다. 만약 포스트를 나만 볼 수 있는 private 상태로 지정하려고 한다면, 특정 유저의 포스트 중 private 상태의 포스트만 받아와서 아래와 같은 쿼리 키로 저장한다.

🖥️ const { data: postsDataByUsername } = useQuery({
    queryKey: ["posts", username, {status: private }], 
    //식별자가 필요합니다. 'private'은(는) strict 모드의 예약어입니다. 모듈은 자동으로 strict 모드가 됩니다.ts(1214)
    queryFn: () => getPostsByUsername(username),
  });

또한 위 코드에서 queryFn은 화살표 함수 형태로 바꿔서 아규먼트로 username을 전달해주고 있는데, 아규먼트를 전달할 때 이렇게 화살표 함수로 만들면 된다. 참고로 queryFn은 Promise를 리턴하는 형태라면 어떤 형태의 함수여도 괜찮다.

만약 쿼리 키에 있는 값을 아규먼트로 전달하고 싶다면 아래처럼 사용할 수도 있다. 쿼리 함수의 파라미터로 queryKey가 전달되는데, 쿼리 키에 있는 usename 값을 이용하고 싶으면 queryKey 배열의 1번 인덱스 요소를 아규먼트로 넣어준다.

🖥️ const { data: postsDataByUsername } = useQuery({
    queryKey: ["posts", username], 
    queryFn: ({queryKey}) => getPostsByUsername(queryKey[1]),
  });

혹은 아래처럼 쿼리 키에서 객체의 특정 값을 가져와 활용할 수 있다.

🖥️ const username = 'codeit';
const { data: postsDataByUsername} = useQuery({
  queryKey: ['posts', { username }],
  queryFn: ({ queryKey }) => {
    const [key, { username }] = queryKey;
    return getPostsByUsername(username);
  }
})

주의할 점 : 객체를 쿼리 키로 전달할 시, 그 안에서는 순서와 상관 없이 같은 값들을 가진 객체는 같은 쿼리 키로 인식하지만, 배열을 쿼리 키로 전달하면 요소의 순서가 중요하다. 순서가 달라지면 다른 쿼리 키로 인식하기 때문에 배열의 요소로 쿼리 키 지정 시 순서에 꼭 유의하도록 한다.

🖥️ 
// 다음 세 가지는 모두 같은 쿼리로 인식한다
useQuery({ queryKey: ['posts', { username, userEmail }], ... });
useQuery({ queryKey: ['posts', { userEmail, username }], ... });
useQuery({ queryKey: ['posts', { userEmail, username, other: undefined }], ... });

// 다음 세 가지는 모두 다른 쿼리로 인식한다
useQuery({ queryKey: ['posts', username, userEmail], ... });
useQuery({ queryKey: ['posts', userEmail, username], ... });
useQuery({ queryKey: ['posts', undefined, userEmail, username], ... });

출처 : 코드잇 ReactQuery

profile
아무튼, 개발자

0개의 댓글