[Chingu] Next.js에서 리액트 쿼리 사용하기

soyeon·2022년 4월 21일
0

TIL

목록 보기
18/32
post-thumbnail

들어가며

Chingu 팀 프로젝트를 하며 배운 것을 정리한다.
나는 React로 리액트 쿼리를 사용한 적이 있다.
이번 NextJs를 사용하는 프로젝트에도 공식 문서의 퀵 스타트 파트에 나와있는 튜토리얼을 그대로 따라서 데이터를 fetch했다.

그리고 풀 리퀘스트에서 팀원이 나에게 이런 피드백을 해줬다.


Next.js의 리액트 쿼리 hydration method를 사용한다고 한다.
Next.js를 사용해 본 적이 없어서 문서를 읽어도 뭐가 뭔지 모르겠음..🤔 아무튼 공식 문서에 있는 내용들을 보고 정리하려고 한다.

Next.js에서 리액트 쿼리 사용하기

Next.js에서 리액트 쿼리를 사용할 때 queryClient에 데이터를 전달하는 방법으로 2가지가 있다.
하나는 initialData를 통해 데이터를 직접 전달하는 것과 Hydration을 통해 전달하는 방법이 있다.

Hyderate란?

= Next.js의 웹페이지 구성 원리
1. 서버 사이드 단에서 미리 웹페이지를 pre-rendering 한다.
2. 자바스크립트 요소들이 없는 HTML document가 생성된다. 이를 클라이언트에게 전송한다.
3. 서버에서 리액트가 번들링 된 자바스크립트 코드들을 클라이언트에게 전송한다.
4. 자바스크립트 코드들이 이전에 전송된 HTML DOM 위에 한번 더 렌더링을 하면서, 자기 자리를 찾아감.

자바스크립트 코드들이 DOM 요소 위에 물을 부어서 요소들을 채워나가는 것 같다고 해서 Hydrate라고 한다.

SSG(static site generator)란?

  • SSR처럼 서버로부터 완성된 HTML을 받아옴
  • BUT HTML 파일의 생성시점이 "빌드 타임"(npm run build). SSR은 요청 때마다 HTML 문서를 생성한다.
  • HTML을 빌드 타임에 각 페이지별로 생성하고 해당 페이지로 요청이 올 경우 이미 생성된 HTML 문서를 반환한다.
    장점
  • 생성이 완료된 HTML 문서를 사용하기 때문에 응답 속도가 빠름
  • 이미 생성된 HTML 파일을 받기 때문에 SEO 친화적
    단점
  • 모든 URL에 대한 개별 HTML 파일을 생성해야함.

추천

  • 마케팅 페이지/ 블로그 게시물/제품 목록 등 정적 생성하여 각 요청에 동일한 문서를 반환할 수 있는 경우
  • 퍼포먼스에 집중(응답이 빠른 편이라)

SSG 방식에서 리액트 쿼리 사용하기

  • 레시피의 목록을 만드는 것을 작업하고 있기 때문에 SSG 방식으로 진행하는 것 같음
  • 공식 문서의 예제와 팀원이 작성해준 코드를 비교해보았다.

index.js

export async function getStaticProps() {
  const queryClient = new QueryClient()

  await queryClient.prefetchQuery(['posts', 10], () => fetchPosts(10))

  return {
    props: {
      dehydratedState: dehydrate(queryClient),
    },
  }
  • 팀원이 작성한 코드는 이렇다. 위의 예시를 그대로 가져왔다.
export async function getStaticProps() {
  const queryClient = new QueryClient();

  await queryClient.prefetchQuery("categories", getCategories);
//await queryClient.prefetchQuery("데이터이름", fetch func);
  return {
    props: {
      dehydratedState: dehydrate(queryClient),
    },
  };
}

_app.js

import React from 'react'
import { Hydrate, QueryClient, QueryClientProvider } from 'react-query'
import { ReactQueryDevtools } from 'react-query/devtools'

export default function MyApp({ Component, pageProps }) {
  const [queryClient] = React.useState(() => new QueryClient())

  return (
    <QueryClientProvider client={queryClient}>
      <Hydrate state={pageProps.dehydratedState}>
        <Component {...pageProps} />
      </Hydrate>
      <ReactQueryDevtools />
    </QueryClientProvider>
  )
}
  • 팀원이 작성한 코드 (위와 똑같음)
    _app.js
import GlobalStyles from "../components/GlobalStyles";
import Layout from "../components/Layout";
import { Hydrate } from "react-query";

export default function MyApp({ Component, pageProps }) {
  return (
    <>
      <GlobalStyles />
      <Layout>
        <Hydrate state={pageProps.dehydratedState}>
          <Component {...pageProps} />
        </Hydrate>
      </Layout>
    </>
  );
}

fetch 함수를 저장해놓는 hooks 폴더

hooks/usePosts/index.js

import ky from 'ky-universal'
import { useQuery } from 'react-query'

const fetchPosts = async (limit = 10) => {
  const parsed = await ky('https://jsonplaceholder.typicode.com/posts').json()
  const result = parsed.filter(x => x.id <= limit)
  return result
}

const usePosts = limit => {
  return useQuery(['posts', limit], () => fetchPosts(limit))
}

export { usePosts, fetchPosts }
  • 팀원과 내가 쓴 코드
    hooks/useCategories.js
import { useQuery } from "react-query";
import { MEALDB_BASE_PATH } from "../constants";

export async function getCategories() {
  const data = await (
    await fetch(`${MEALDB_BASE_PATH}/list.php?c=list`)
  ).json();

  return data;
}

export default function useCategories() {
  return useQuery("categories", getCategories);
}

📎참고

React Query 한글 메뉴얼
공식 문서 - https://react-query.tanstack.com/examples/nextjs
리액트 쿼리 in nextjs - https://gingerkang.tistory.com/123
hydrate - https://helloinyong.tistory.com/315
ssg,ssr 차이점 - https://higher77.tistory.com/105

profile
공부중

0개의 댓글