본문 바로가기
카테고리 없음

[React]React Query와 SWR 맛보기로 강력하고 심플한 코드 바로 적용하고 중복줄이기

by sighan 2024. 1. 7.
반응형

React Query와 SWR의 특징을 코드를 통해 구체적으로 비교해보겠습니다.

 

eact Query: 복잡한 캐싱 및 데이터 동기화

React Query는 세밀한 캐싱 제어와 데이터 동기화를 위한 다양한 설정을 제공합니다.

import { useQuery, useMutation, useQueryClient } from 'react-query';

// 캐시 시간과 재요청 설정
const { data: todos } = useQuery('todos', fetchTodos, {
  staleTime: 2000, // 2초 동안 데이터를 신선(fresh)으로 간주
  cacheTime: 5000, // 캐시에서 데이터가 유지되는 시간
  refetchOnWindowFocus: true // 윈도우 포커스 시 재요청
});

// 쿼리 클라이언트를 사용한 캐시 무효화 및 업데이트
const queryClient = useQueryClient();

const mutation = useMutation(updateTodo, {
  onSuccess: () => {
    // 쿼리 'todos'에 대한 캐시 무효화
    queryClient.invalidateQueries('todos');
  }
});

 

 

여기서 staleTime과 cacheTime은 캐시 동작을 세밀하게 제어합니다. useMutation과 invalidateQueries를 사용하여 데이터 변경 후 특정 쿼리의 캐시를 무효화하고 재요청할 수 있습니다.

SWR: 간결하고 효율적인 사용

SWR은 간단하고 직관적인 API를 제공하며, 자동 revalidation 전략에 중점을 둡니다.

import useSWR from 'swr';

// 자동 revalidation 설정
const { data: user, mutate } = useSWR('/api/user', fetcher, {
  revalidateOnFocus: false, // 윈도우 포커스 시 재요청하지 않음
  refreshInterval: 3000 // 3초마다 데이터 자동 갱신
});

// mutate를 사용한 수동 업데이트 및 재요청
function updateUser(newUser) {
  mutate('/api/user', newUser, false); // 로컬 데이터 업데이트
  mutate('/api/user'); // 서버에서 최신 데이터 재요청
}

SWR에서 mutate 함수는 로컬 데이터를 빠르게 업데이트하고, 필요에 따라 서버에서 최신 데이터를 재요청할 수 있게 해줍니다.

 

 

결론

  • React Query는 데이터의 staleTime, cacheTime을 설정하여 캐시 동작을 더 세밀하게 조절할 수 있으며, useMutation과 invalidateQueries를 사용해 데이터 변경 후 자동 또는 수동으로 캐시를 무효화하고 재요청할 수 있습니다.
  • SWR은 revalidateOnFocus, refreshInterval 등을 설정하여 자동 revalidation을 조절할 수 있으며, mutate 함수를 사용해 로컬 데이터를 빠르게 업데이트하고 서버에서 데이터를 재요청할 수 있습니다.

React Query는 복잡한 캐싱 및 데이터 동기화 시나리오에 적합한 반면, SWR은 간단하고 직관적인 사용에 중점을 둡니다. 프로젝트의 요구 사항과 개발자의 선호에 따라 적절한 라이브러리를 선택할 수 있습니다.





React Query 전체코드

예시: 할 일(Todos) 목록 업데이트하기

먼저, 서버와 통신하는 API 함수를 정의합니다. 이 예시에서는 fetchTodos 함수가 서버로부터 할 일 목록을 가져오고, updateTodo 함수가 할 일을 업데이트하는 데 사용됩니다.

 

// API 호출을 위한 함수들
const fetchTodos = async () => {
  const response = await fetch('/api/todos');
  if (!response.ok) {
    throw new Error('Network response was not ok');
  }
  return response.json();
};

const updateTodo = async (todo) => {
  const response = await fetch('/api/todo', {
    method: 'PUT',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(todo),
  });
  if (!response.ok) {
    throw new Error('Network response was not ok');
  }
  return response.json();
};

 

 

다음으로, React Query의 useQueryuseMutation 훅을 사용하여 이러한 함수들을 컴포넌트에 통합합니다

import { useQuery, useMutation, useQueryClient } from 'react-query';

function TodosComponent() {
  // useQueryClient를 사용하여 queryClient 인스턴스를 가져옵니다.
  const queryClient = useQueryClient();

  // 'todos' 쿼리를 사용하여 데이터를 가져옵니다.
  const { data: todos, isLoading, error } = useQuery('todos', fetchTodos, {
    staleTime: 2000,
    cacheTime: 5000,
    refetchOnWindowFocus: true,
  });

  // useMutation을 사용하여 할 일 업데이트 로직을 설정합니다.
  const { mutate } = useMutation(updateTodo, {
    onSuccess: () => {
      // 업데이트 성공 후, 'todos' 쿼리의 캐시를 무효화합니다.
      queryClient.invalidateQueries('todos');
    },
  });

  // UI에서 사용될 핸들러 함수
  const handleUpdateTodo = (todo) => {
    mutate(todo);
  };

  // 데이터 로딩 중이거나 에러가 발생한 경우 처리
  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>An error has occurred: {error.message}</div>;

  // 데이터를 기반으로 UI 렌더링
  return (
    <div>
      <h1>Todos</h1>
      <ul>
        {todos.map(todo => (
          <li key={todo.id}>
            {todo.content}
            {/* 각 할 일 항목에 대한 업데이트 버튼 */}
            <button onClick={() => handleUpdateTodo({
              ...todo,
              completed: !todo.completed
            })}>
              Toggle Complete
            </button>
          </li>
        ))}
      </ul>
    </div>
  );
}

이 코드는 할 일 목록을 불러오고, 각 할 일 항목에 대해 완료 상태를 토글하는 기능을 구현합니다. 사용자가 "Toggle Complete" 버튼을 클릭하면, updateTodo 함수가 호출되어 서버에 데이터 변경을 요청하고, 성공적으로 변경된 후에는 'todos' 쿼리의 캐시가 무효화되어 최신 데이터를 다시 가져옵니다.

 

 

 

 

 

서로 다른 POST 요청을 처리하는 경우, React Query의 useMutation 훅을 사용하여 각 요청에 대한 로직을 개별적으로 관리할 수 있습니다. 여기서는 두 개의 다른 POST 요청을 예시로 들겠습니다: 하나는 새로운 할 일을 추가하는 것이고, 다른 하나는 사용자의 프로필을 업데이트하는 것입니다.

서로 다른 POST 요청을 위한 API 함수 정의

const addTodo = async (newTodo) => {
  const response = await fetch('/api/todos', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(newTodo),
  });
  if (!response.ok) {
    throw new Error('Network response was not ok');
  }
  return response.json();
};

const updateProfile = async (profileData) => {
  const response = await fetch('/api/profile', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(profileData),
  });
  if (!response.ok) {
    throw new Error('Network response was not ok');
  }
  return response.json();
};

 

React Query의 useMutation 사용

import { useMutation, useQueryClient } from 'react-query';

function MyComponent() {
  const queryClient = useQueryClient();

  // 새로운 할 일 추가를 위한 mutation
  const addTodoMutation = useMutation(addTodo, {
    onSuccess: () => {
      // 새 할 일을 추가한 후, 'todos' 쿼리를 무효화하여 새 데이터를 가져옵니다.
      queryClient.invalidateQueries('todos');
    },
  });

  // 프로필 업데이트를 위한 mutation
  const updateProfileMutation = useMutation(updateProfile, {
    onSuccess: () => {
      // 프로필을 업데이트한 후, 'profile' 쿼리를 무효화합니다.
      queryClient.invalidateQueries('profile');
    },
  });

  // 새 할 일 추가 핸들러
  const handleAddTodo = (newTodo) => {
    addTodoMutation.mutate(newTodo);
  };

  // 프로필 업데이트 핸들러
  const handleUpdateProfile = (profileData) => {
    updateProfileMutation.mutate(profileData);
  };

  // UI 컴포넌트 반환...
}

이 예시에서 addTodoMutationupdateProfileMutation은 각각 다른 POST 요청을 처리합니다. 각 mutation은 onSuccess 콜백을 통해 성공적으로 요청이 완료된 후에 캐시를 무효화하고 필요한 쿼리를 다시 가져옵니다. 이 방법은 서로 다른 요청을 명확하게 분리하고 각각의 결과에 따라 적절한 조치를 취할 수 있도록 합니다.

 

 

 

 

두 비슷한 로직을 가진 POST 요청에서 코드 중복을 피하기 위해, 공통 로직을 재사용할 수 있는 함수를 만드는 것이 좋습니다. 이렇게 하면 코드를 더 간결하고 관리하기 쉽게 만들 수 있습니다.

다음은 공통 로직을 재사용하기 위해 usePostMutation이라는 커스텀 훅을 만드는 예시입니다:

공통 로직을 위한 커스텀 훅 생성

import { useMutation, useQueryClient } from 'react-query';

function usePostMutation(apiFunc, queryKey) {
  const queryClient = useQueryClient();
  return useMutation(apiFunc, {
    onSuccess: () => {
      queryClient.invalidateQueries(queryKey);
    },
  });
}

 

이 커스텀 훅은 API 함수(apiFunc)와 쿼리 키(queryKey)를 인자로 받아, POST 요청을 처리하고 성공 시 해당 쿼리 키의 캐시를 무효화합니다.

 

커스텀 훅 사용하기

function MyComponent() {
  // 새로운 할 일 추가를 위한 mutation
  const addTodoMutation = usePostMutation(addTodo, 'todos');

  // 프로필 업데이트를 위한 mutation
  const updateProfileMutation = usePostMutation(updateProfile, 'profile');

  // 새 할 일 추가 핸들러
  const handleAddTodo = (newTodo) => {
    addTodoMutation.mutate(newTodo);
  };

  // 프로필 업데이트 핸들러
  const handleUpdateProfile = (profileData) => {
    updateProfileMutation.mutate(profileData);
  };

  // UI 컴포넌트 반환...
}

이 방식을 사용하면, 비슷한 로직을 수행하는 여러 요청에 대해 중복 코드를 줄이고, 재사용 가능한 구조를 만들 수 있습니다. 각 API 요청에 대해 고유한 API 함수와 쿼리 키를 usePostMutation 훅에 전달하기만 하면 됩니다. 이렇게 하면 코드의 가독성과 유지보수성이 향상됩니다.

반응형