TanStack Query를 Nuxt 3에 실전 적용하기

심추진·2024년 12월 4일

Nuxt3

목록 보기
4/4

최근 Nuxt 3 기반 프로젝트에서 데이터 fetching 방식을 두고 고민했던 경험이 있다. 처음에는 Nuxt 3의 공식 기능인 useAsyncData를 사용했으나, 프로젝트 특성상 CSR(Client-Side Rendering)으로 배포하기로 결정하면서 더 적합한 도구를 찾기 위해 TanStack Query로 전환하게 되었다. 이번 글에서는 그 이후 TanStack Query를 실제 프로젝트에 적용하면서 느낀 점과 개선된 부분을 기록해본다.

https://nuxt.com/modules/vue-query

1. TanStack Query의 간단한 사용 예제

TanStack Query는 데이터를 효율적으로 관리하기 위해 캐싱, 상태 관리, 의존성 제어 등을 제공한다. 기본적인 사용법은 다음과 같다.

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

interface ExampleData {
  id: number;
  name: string;
}

const { data, refetch, isLoading, error } = useQuery<ExampleData[]>({
  queryKey: ['exampleData'], // 데이터 캐싱 키
  queryFn: async () => {
    const response = await fetch('/api/data');
    return response.json(); // 데이터를 반환
  },
  staleTime: 5000, // 캐싱 지속 시간
});

const refetchData = (): void => {
  refetch(); // 데이터를 다시 가져오기
};

위 예제에서는 useQuery를 사용해 데이터를 패칭하고, 캐싱된 데이터를 재사용하며, 필요할 때 refetch로 데이터를 다시 가져온다.

2. TanStack Query의 실전 활용

TanStack Query의 실전 활용

TanStack Query로 전환 후, 데이터를 fetching하고 관리하는 방식이 크게 변화했다. 기존에는 데이터를 수동으로 관리했지만, TanStack Query는 이를 자동화하고 효율적으로 처리할 수 있었다.

데이터 패칭

const { data: quests, refetch: refetchQuests } = useQuery<Quest[]>({
  queryKey: ['quests'], // 캐싱 키
  queryFn: QuestService.fetchQuests, // 데이터를 가져오는 함수
  enabled: computed(() => props.isShow), // 특정 조건에서만 쿼리 활성화
});

데이터 업데이트

const { mutate: saveQuestOrders } = useMutation({
  mutationFn: async (): Promise<void> => {
    const requestBody: QuestOrderRequest = {
      patient_id: patientStore.patient.id,
      daily_count: dailyCount?.value ?? 0,
      orders: questOrders.value.map((quest, index) => ({
        quest_id: quest.id,
        order: index + 1,
      })),
    };
    return await PatientService.updatePatientQuestOrder(patientStore.patient.id, requestBody);
  },
  onSuccess: () => {
    toastStore.showNoti('success', t('update_success_message')); // 성공 메시지
  },
  onError: (error: unknown) => {
    toastStore.showNoti('error', t('update_fail_message')); // 실패 메시지
  },
});

위 코드는 TanStack Query의 자동 데이터 무효화, 리패칭, 오류 처리를 통해 데이터 관리의 효율성을 크게 개선했다.

3. 기존 방식: useAsyncData를 사용했던 사례

초기에는 Nuxt 3의 useAsyncData를 사용해 데이터를 패칭하고 관리했다. 아래는 환자의 퀘스트 데이터를 패칭하고 업데이트했던 코드이다.

const { data: questOrders, refresh: refreshQuestOrders } = await useAsyncData<Quest[]>(
  'fetchQuestOrders',
  async () => {
    if (!patientStore.patient.value?.id) {
      throw new Error('No patient ID available');
    }
    const response = await PatientService.fetchPatientQuestsOrder(patientStore.patient.value.id);
    return response.data;
  },
  {
    immediate: true, // 컴포넌트 로드시 데이터를 가져옴
    watch: [() => patientStore.patient.value?.id], // 환자 ID가 변경되면 데이터를 다시 가져옴
  }
);

const saveQuestOrders = async (): Promise<void> => {
  if (!patientStore.patient.value?.id) {
    console.error('No patient ID available');
    return;
  }

  const requestBody: QuestOrderRequest = {
    patient_id: patientStore.patient.value.id,
    daily_count: dailyCount.value,
    orders: questOrders.value.map((quest, index) => ({
      quest_id: quest.id,
      order: index + 1,
    })),
  };

  try {
    await PatientService.updatePatientQuestOrder(patientStore.patient.value.id, requestBody);
    refreshQuestOrders(); // 업데이트 후 데이터를 수동으로 다시 가져오기
    alert('Update successful');
  } catch (error) {
    console.error('Failed to save quest orders:', error);
  }
};

이 방식은 간단한 데이터 패칭과 업데이트를 처리하기에는 적합했지만, 프로젝트가 점점 복잡해지면서 몇 가지 한계가 드러났다.

4. useAsyncData의 한계

useAsyncData는 단순한 데이터 패칭 및 SSR 환경에서는 충분히 유용했지만, 아래와 같은 한계를 느꼈다:

  1. 데이터 캐싱 : 데이터를 매번 새로 가져오며, 동일한 데이터를 여러 컴포넌트에서 재사용하려면 중복 요청이 발생한다.
  2. 자동 상태 관리 : 데이터가 오래된 상태(stale)인지 확인하거나, 자동으로 최신 데이터를 가져오는 기능이 없다.
    업데이트 후 수동으로 refresh를 호출해야만 데이터를 갱신할 수 있다.
  3. 복잡한 의존성 관리 : 의존성 변경에 따라 데이터를 가져오려면 watch를 수동으로 설정해야 한다.
    복잡한 데이터 의존성을 가진 경우 로직이 번거로워진다.
  4. 자동 데이터 무효화 : 데이터를 업데이트한 후, 관련 데이터를 자동으로 무효화하고 다시 가져오는 기능이 없다.

5. TanStack Query로 개선된 점

TanStack Query는 위의 문제를 다음과 같은 방식으로 해결해주었다:

  1. 자동 캐싱 : 동일한 queryKey로 요청한 데이터는 자동으로 캐싱되어, 불필요한 API 호출을 줄이고 데이터를 재사용할 수 있다.

  2. 자동 리패칭 : 데이터를 업데이트하면 연관된 쿼리(queryKey)가 자동으로 무효화되고 최신 데이터로 리패칭된다.

  3. 효율적인 상태 관리 : isLoading, isError, onSuccess 등의 상태를 자동으로 관리하며, 코드의 간결성과 유지보수성을 높인다.

  4. 복잡한 의존성 관리 : queryKey를 통해 데이터와 의존성을 명확히 정의할 수 있어 관리가 훨씬 쉬워졌다.

6. 회고

TanStack Query를 도입한 이후, 데이터 관리가 자동화되고 복잡한 상태를 간단하게 처리할 수 있었다. 특히, CSR 환경에서 자동 데이터 무효화캐싱은 프로젝트의 데이터 흐름을 안정적으로 유지하는 데 크게 기여했다.

useAsyncData에서 시작해 TanStack Query로 전환하는 과정을 통해, 프로젝트의 요구사항에 맞는 도구를 선택하는 것이 얼마나 중요한지 다시 한번 깨달았다. 앞으로도 상황에 맞는 최적의 도구를 선택하여 생산성과 안정성을 동시에 높이는 데 집중하고 싶다.

profile
Product 개발에 항상 설레는 Web • App 개발자입니다.

0개의 댓글