개발냥발/FE (FrontEnd)👩🏻‍💻

Next.js...그리고 react query와 prefetch, hydration까지

승이버섯 2024. 6. 5. 10:25

이번에 우리 프로젝트에서 페이지의 UI 작업을 마치고 이제 본격적으로 API 연동 작업을 시작하기에 앞서,

우리 팀은 Tanstack query와 Next.js의 App router를 모두 처음 써보기 때문에 고민의 시간을 가졌다.

어떤 고민이었냐면, 바로 Next.js를 Next.js답게 써보고 싶은 마음과 Tanstack query(=리액트 쿼리)를 그냥 Fetch로 사용하는것과 다르게 Tanstack query만의 이점을 살리고 싶었다.

Next.js로는 RSC 즉 리액트 서버 컴포넌트를 잘 활용하고 싶었고, Tanstack query로는 캐싱, 무한 스크롤, Optimistic Updates를 잘 사용하고 싶었다.

 

암튼 요약하자면 우리 프로젝트는 Next.js의 App Router 환경에서, 서버 상태 관리 및 API 요청을 Tanstack Query를 활용하여 Next는 Next만의 장점을 살려서, Tanstack Query는 그만의 장점을 살려서 작업할 계획이었다.

하지만 여기서 큰 문제가 생겼다.

바로 App Router 환경에서 Tanstack Query를 사용하면 상단에 ‘use client’를 붙여야 하고, (useQuery와 같이 'use'가 들어가는 훅을 사용하려면 상단에 'use client'를 붙여야한다)

그러면 클라이언트 컴포넌트가 되기 때문에, Next.js의 장점인 서버 사이드 렌더링(SSR)을 가져갈 수 없었다.

그래서 Tanstack Query의 공식 문서에서 힌트를 얻어 prefetch를 활용하여 서버 렌더링 방식을 구현하기로 했다. (샤라웃 to 수민님( @suMin-97 )...🔥)



먼저 전체 페이지를 나타내는 page.tsx는 서버 컴포넌트로 동작하도록 ‘use client’나 Tanstack Query를 사용하지 않는다. 그리고 queryClient를 생성한 뒤, prefetchQuery 메서드를 통해 미리 데이터를 요청하고 응답받은 초기 데이터를 캐싱한다.

(~~Options라는 함수 안에서 데이터를 패칭하는 함수를 queryFn으로 넣어주었다.)


이후 HydrationBoundary를 통해 초기 데이터가 담긴 queryClient를 데이터 요청이 필요한 하위 컴포넌트로 내려준다.

 


그다음 하위 컴포넌트에서 캐싱된 초기 데이터를 사용할 수 있도록 prefetchQuery 메서드를 요청할 때와 동일한 queryOption을 사용해서 useSuspenseQuery를 요청한다. 그러면 해당 컴포넌트는 이미 캐싱한 데이터가 있기 때문에 데이터 요청을 하지 않고 캐싱된 정보를 바탕으로 화면이 렌더링된다.


여기서 왜 useQuery가 아니라, useSuspenseQuery를 사용하지? 라고 생각할 수도 있다.

Next.js의 App Router는 스트리밍 방식으로 렌더링이 된다. 쉽게 말하면, 데이터 요청이 먼저 완료되는 순서대로 브라우저에 렌더링을 시작한다.

이때 구분되는 단위는 Suspense다. 그래서 우리 팀은 프로젝트에서 페이지 작업을 할때, 그 HydrationBoundary를 Suspense로 감싸주었고, 스켈레톤을 적용하였다.

이를 통해 해당 페이지는 스트리밍 방식이 적용되어 FCP 속도가 향상되었다.

최종적으로 prefetch를 통해 페이지 전체 초기 로딩 속도(Speed Index)를 0.5초로 향상시켰으며, 스트리밍 방식을 통해 FCP를 0.5초로 향상시킬 수 있었다.

 


★ 우리팀이 프로젝트 하면서 참고한 공식문서 링크 : 

https://tanstack.com/query/latest/docs/framework/react/guides/advanced-ssr

https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming