Next App Router에서 Axios, TanStack Query 사용에 대한 고찰

·2024년 11월 22일
1
post-thumbnail

개요

Next의 fetch 기능이 강력해짐(캐싱, 재검증)에 따라 Axios나 TanStack Query와 같은 데이터 관리에 도움을 주는 라이브러리들을 사용하는 것이 좋을지에 대한 의문이 생겼습니다.
지금까지는 어떠한 이유로 Axios와 TanStack Query를 사용해왔고, 지금은 왜 의문이 들게되었는지 정리하며 그럼 앞으로는 어떻게 환경을 세팅할지까지 정리해 보겠습니다.

왜 이런 고민이 필요해졌을까?

먼저, 이러한 고민이 왜 필요해졌을까요? 한줄로 요약하자면,

React18 부터 Server Component 라는 개념이 도입되고, Next13.4부터 Server Component와 fetch 기능을 강력하게 지원하는 App Router가 stable하게 되어서 비동기 데이터 관리에 대한 도움을 다른 라이브러리에 전이하는 것에 대한 의문이 생김

먼저, Server Component와 App Router가 무엇인지부터 살펴보도록 하겠습니다.

React Server Component (RSC)

서버 컴포넌트는 React18 부터 도입된 개념으로 말그대로 서버에서 실행되는 컴포넌트 입니다.

export const getServerSideProps = () => {
  const data = "hello";
  return {
    props: {
      data,
    },
  };
};

export default function Home({ data }: InferGetServerSidePropsType<typeof getServerSideProps>) {
  return (
    <div>{data}</div>
  );
}

이전에는 서버에서 실행되는 코드를 작성하기 위해서는 위와 같이 getServerSideProps, getStaticProps, getStaticPaths 함수를 사용하고 props를 작성해서 클라이언트에 데이터를 넘겨줘야 했습니다.

import { HomeData } from "@/types";

async function HomeFetch() {
  const response = await fetch('/api');
  if (!response.ok) {
    return <div>오류</div>;
  }
  const homeLists: HomeData[] = await response.json();
  return (
    <div>
      {homeLists.map((home) => (
        <BookItem key={home.id} {...home} />
      ))}
    </div>
  );
}

export default function Home() {
  return (
    <HomeFetch/>
  );
}

하지만 이제는 직접적으로 컴포넌트에 비동기를 사용하고, 렌더링 안에 직접 사이드 이펙트를 가질 수 있습니다.

위 표와 같이 서버 컴포넌트, 클라이언트 컴포넌트의 사용 범위가 구분되기 때문에 상황에 맞게 배치하여 사용해야 합니다.
서버 컴포넌트와 클라이언트 컴포넌트는 렌더링되는 장소가 서버냐 클라이언트냐의 차이로 나뉘게 되며, 클라이언트 컴포넌트는 서버, 클라이언트에서 모두 실행됩니다. (클라이언트 컴포넌트는 사전 렌더링을 위해서 1번, 하이드레이션을 위해 1번, 즉, 서버와 클라이언트 모두에서 실행됩니다)

서버 컴포넌트는 아래와 같은 장점들을 갖고 있습니다.

1️⃣ 서버 측에서의 데이터 처리로 성능 향상
서버에서 데이터를 가져와 렌더링하므로, 클라이언트 측의 부담이 줄어듭니다. 클라이언트는 이미 준비된 HTML을 받아 빠르게 렌더링할 수 있어 초기 로딩 속도가 개선됩니다.

2️⃣ 보안과 데이터 보호
민감한 데이터는 서버에서 렌더링된 상태로만 클라이언트에 전달되므로 데이터 보안의 측면에서 장점이 있습니다.

3️⃣ 더 쉬운 데이터 페칭 로직
서버에서 데이터 패칭 로직을 직접 작성할 수 있기 때문에 클라이언트 측에서 비동기 데이터 요청과 상태 관리 코드를 덜 필요로 해 간단해지고, 서버와 클라이언트 코드가 분리되어 유지보수가 쉬워집니다.

Next App Router

Next13.4 부터 App Router 방식이 stable하게 되었습니다. app 폴더 내부의 모든 컴포넌트가 기본적으로 서버 컴포넌트로 동작하여 기존의 클라이언트 컴포넌트를 사용하고 싶다면 파일 최상단에 "use client"를 명시해야 합니다.
Next에서는 App Router 외에도 기본 fetch 웹 API를 확장하여 데이터를 가져오고, 캐시하고, 유효성을 재검증하는 방법을 제공 합니다. 확장된 fetch 기능은 아래과 같습니다.

1️⃣ 자동으로 fetch 중복 요청 제거

2️⃣ 캐싱, 재검증 처리 제공

// 특정 주소 페이지 재검증
revalidatePath(`/test`)
    
// 특정 경로의 동적 페이지 재검증
revalidatePath("/test/[id]", "page")
    
// 특정 레이아웃을 갖는 페이지 재검증
revalidatePath("/(test-layout)", "layout")
    
// 모든 데이터 재검증
revalidatePath("/", "layout")
    
// 태그 기준으로 데이터 재검증
revalidateTag('test-tag')

3️⃣ SSG, SSR, ISR 렌더링 기법을 아래와 같은 옵션으로 사용 가능하여 하나의 API를 통해 구현 가능 (서버 컴포넌트를 사용하기 때문에 컴포넌트 레벨에서 SSR 적용 가능)

// This request should be cached until manually invalidated.
// Similar to `getStaticProps`.
// `force-cache` is the default and can be omitted.
fetch(URL, { cache: 'force-cache' })
    
// This request should be refetched on every request.
// Similar to `getServerSideProps`.
fetch(URL, { cache: 'no-store' })
    
// This request should be cached with a lifetime of 10 seconds.
// Similar to `getStaticProps` with the `revalidate` option.
fetch(URL, { next: { revalidate: 10 } })
  • SSR(Server Side Rendering) : 서버에서 HTML을 생성하여 클라이언트에 전달하는 방식
  • SSG(Static Site Generation) : 빌드 타임에 모든 HTML을 생성하여 페이지가 생성된 이후에는 데이터가 변경되지 않는 방식
  • ISR(Incremental Static Regeneration) : SSG 방식으로 생성된 정적 HTML을 기본으로 제공하지만, 설정된 재생성 주기에 따라 정적 페이지를 새로 갱신하는 방식

이제 본론으로 들어가 이런 사항들이 업데이트 된 Next.js에서, Axios와 TanStack Query가 필요할지 고민해보기 위해 두 라이브러리에 대해 알아보겠습니다.


Axios

Axios와 fetch는 모두 HTTP 요청을 보내는 데 사용되는 도구이지만 Axios는 install 하여 사용해야되는 반면에, fetch는 내장 웹 API이기 때문에 따로 install 하지 않아도 됩니다. 하지만 Axios는 fetch보다 직관적이고 편리한 방법을 제공합니다.

axios.get('/api/users')
  .then(response => {
    console.log(response.data);
  })
  .catch(error => {
    console.error(error);
  });
fetch('/api/users').
  .then(response => response.json())
  .then(data => {
    console.log(data);
  })
  .catch(error => {
    console.error(error);
  });

위와 같이 GET 요청을 보낼 때 Axios는 get 메서드를 통해 간략하게 요청을 보낼 수 있으며, 자체적으로 JSON 형식으로 파싱해주므로 따로 변환해주지 않아도 됩니다. 그 외의 차이점은 아래 표와 같습니다.

AxiosFetch
패키지 설치 필요설치 없이 사용 가능
자동 JSON 데이터 변환 지원JSON 변환 필요
baseUrl, default headers 설정 가능기본적으로 제공되지 않지만 구현 가능
Request 취소와 Request Timeout 설정 가능기본적으로 제공되지 않지만 구현 가능 (AbortController)
Request의 Intercept 가능기본적으로 제공되지 않지만 구현 가능
상태 코드에 따른 에러 처리상태 코드에 상관없이 응답 반환 (추가 에러 처리 필요)

비록 fetch문이 Axios에 비해 작성하기 복잡하지만, 커스텀을 통해 Axios와 유사한 기능을 구현할 수 있으며, Next의 fetch가 제공하는 기능을 포기할 만큼 Axios가 장점을 갖고 있는지에 대해서는 의문이 들었습니다. (캐시, 재검증이 fetch를 통해 가능하면 Tanstack Query 또한 도입하지 않아도 되겠다는 생각이 들기 때문에)

과거 fetch는 IE11(지원 종료된 IE…)에서 지원되지 않았고 Axios는 XMLHttpRequest 객체 기반으로 동작하기 때문에 크로스 브라우징 측면에서 강점을 갖고 있었습니다.

하지만 이제 IE는 종료됐고, fetch의 기능은 보완되었으며, Next에서는 더 강력한 기능을 지원합니다.
Axios는 작성이 간편하고 오래된 도구로 안정성이 높지만, 이제는 fetch로의 새로운 도전(?)을 이어나가도 괜찮지 않을까하는 생각이 들었습니다.


TanStack Query

TanStack Query는 흔히 React Query라고도 불리우는 비동기 데이터 상태 관리 툴입니다. TanStack Query는 데이터의 로딩, 에러, 성공 처리 등의 강력한 기능을 제공하고 아래와 같은 장점을 갖고 있습니다.

1️⃣ 기존의 비동기 API 로직을 한곳에서 확인이 가능해서 관심사 분리에 용이

2️⃣ onSuccess onError isFetching 등 ErrorFlag를 지원해줘서 편리하게 사용 가능

3️⃣ 지원해주는 다양한 옵션을 이용하여 효율적으로 데이터 변환 가능

// Get test
const { data, refetch, isLoading, isError } = useQuery({
  queryKey: ['user'],
  queryFn: async() => {
  	const response = await axios.get("/test");
	  return response
	},
})

// Post test
const { mutate, isSuccess } = useMutation({
  mutationFn: async(req) => {
  	const response = await axios.post("/test", req)
    return response
  },
  onSuccess: () => {
    queryClient.invalidateQueries({ queryKey: ['test'] })
  },
})

위는 간단한 TanStack Query 사용 예제입니다.
상태에 대한 값과 캐싱, 리페칭 등의 처리를 돕기 때문에 굉장히 강력한 데이터 상태 관리 도구로 많이 사용되고 있습니다. 이렇게 좋은 TanStack Query, 공식 문서에 이러한 문구가 있습니다.

It's hard to give general advice on when it makes sense to pair React Query with Server Components and not. If you are just starting out with a new Server Components app, we suggest you start out with any tools for data fetching your framework provides you with and avoid bringing in React Query until you actually need it. This might be never, and that's fine, use the right tool for the job!

React Query를 Server Components와 페어링하는 것이 언제 합리적인지, 그렇지 않은지에 대한 일반적인 조언을 하기는 어렵습니다. 새로운 Server Components 앱을 막 시작하는 경우 프레임워크에서 제공하는 데이터 페칭 도구로 시작하고 실제로 필요할 때까지 React Query를 가져오지 않는 것이 좋습니다. 절대 없을 수도 있지만 괜찮습니다. 작업에 적합한 도구를 사용하세요!

전 영어를 못하므로 번역기를 사용해봤습니다.
또한 TanStack Query의 개발자는 “React Query가 필요하지 않을 수도 있습니다.” 라는 주제의 을 남기기도 했습니다.

TanStack Query의 캐싱, 재검증은 fetch의 강화된 기능으로 대체가 가능하며 useFormState 훅을 사용해(React19에서는 useActionState) 에러 제어 또한 가능 합니다. 또한 Next App Router 환경에서 Tanstack Query를 도입하려 한다면, 서버 컴포넌트의 활용을 위해 약간의 세팅이 필요합니다.

무한 스크롤, 페이지네이션과 같은 클라이언트에서 처리가 필요한 까다로운 데이터 대응이 필요할 때는 Tanstack Query를 사용해도 좋을 것 같지만, 일반적인 데이터 페칭 기능만 필요 때에는 fetch 만으로도 충분하다는 생각이 들었습니다.


결론

라이브러리 사용을 결정하기 전에 먼저 프로젝트에서 필요한 기능들이 무엇인지 그리고 그것이 기존 환경에서 대체가 가능한지 파악하는 것이 중요하다는 생각이 들었습니다.

fetch API는 Axios, TanStack Query를 사용하는 것에 비해 에러 처리 등이 조금 귀찮고 코드의 길이가 길어지지만 서버 컴포넌트의 이점을 추가적인 세팅 없이 사용할 수 있는 좋은 방법이라는 생각이 들었습니다.

fetch의 부족한 기능들(baseUrl, defualt headers 지정 등…)은 따로 코드를 작성하여 보완할 수 있을 것 같으며 기능을 보완하는 추가적인 라이브러리들도 존재하기 때문에 해당 라이브러리의 도움을 받아 fetch를 더 강력하게 사용할 수도 있을 것 같습니다.

위와 같은 생각들로 인해 현재 진행중인 업무에서 추가적인 라이브러리 없이 fetch를 사용하는 것을 생각하고 있으나, 또 다른 좋은 방법을 찾는다면 그것을 적용하고 싶습니다! 🌝

profile
FE ✨

2개의 댓글

comment-user-thumbnail
2024년 12월 14일

tanstack query를 관성적으로 사용하면서 next.js app router 환경에서 꼭 필요한가를 생각해보는 계기가 되었습니다

1개의 답글

관련 채용 정보