React Query는 서버 상태를 관리하는 강력한 라이브러리야.

TypeScript와 함께 사용할 때는 query/mutation의 입력, 응답 타입을 정확하게 설정하는 것이 핵심이야.


📘 예제 86: useQuery에 제네릭 타입 적용

import { useQuery } from '@tanstack/react-query';

type User = {
  id: number;
  name: string;
};

const fetchUser = async (): Promise<User> => {
  const res = await fetch('/api/user');
  return res.json();
};

const UserComponent = () => {
  const { data, isLoading, error } = useQuery<User>({
    queryKey: ['user'],
    queryFn: fetchUser,
  });

  if (isLoading) return <p>로딩 중...</p>;
  if (error) return <p>에러 발생</p>;

  return <h2>{data?.name}</h2>;
};

✅ useQuery<User>() 처럼 응답 타입을 제네릭으로 지정하면 data의 타입이 정확하게 추론돼.

queryKey는 항상 고유하게 설정해야 캐시가 구분돼.


📘 예제 87: useMutation의 입력/응답 타입

import { useMutation } from '@tanstack/react-query';

type NewTodo = {
  title: string;
};

type CreatedTodo = {
  id: number;
  title: string;
};

const createTodo = async (todo: NewTodo): Promise<CreatedTodo> => {
  const res = await fetch('/api/todos', {
    method: 'POST',
    body: JSON.stringify(todo),
    headers: { 'Content-Type': 'application/json' },
  });
  return res.json();
};

const TodoAdder = () => {
  const mutation = useMutation<CreatedTodo, Error, NewTodo>({
    mutationFn: createTodo,
    onSuccess: (data) => {
      console.log('생성된 TODO:', data.id);
    },
  });

  return (
    <button
      onClick={() => mutation.mutate({ title: '새 작업 추가하기' })}
    >
      추가
    </button>
  );
};

✅ useMutation<응답, 에러, 입력> 순서로 타입 지정해.

mutate() 함수에 전달하는 데이터도 자동 완성돼서 사용이 훨씬 편해져.


📘 예제 88: 쿼리 키와 타입 일치화

const usePost = (id: number) => {
  return useQuery<Post>({
    queryKey: ['post', id],
    queryFn: async () => {
      const res = await fetch(`/api/posts/${id}`);
      return res.json();
    },
  });
};

✅ queryKey에 ID나 파라미터 값을 포함하면 캐싱이 ID 단위로 작동돼.

쿼리 키에 따라 데이터가 캐싱되고 자동으로 갱신돼.


📘 예제 89: 쿼리 리턴 타입 분해

const useTodos = () => {
  return useQuery<Todo[]>({
    queryKey: ['todos'],
    queryFn: async () => {
      const res = await fetch('/api/todos');
      return res.json();
    },
  });
};

const TodoList = () => {
  const { data: todos = [], isLoading } = useTodos();

  return isLoading ? (
    <p>로딩 중...</p>
  ) : (
    <ul>
      {todos.map((todo) => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  );
};

✅ data: todos = [] 처럼 기본값을 설정하면 undefined 체크 없이 안전하게 사용할 수 있어.