티스토리 뷰

TIL

Next.js SSR

윤미주 2024. 8. 18. 00:11

☁️환경☁️

  • Next.js(page router)
  • typeScript
  • react-query

 

❓SSR

Server Side Rendring 의 약자로 ssr이라고 한다. 

 

실행될 때 가장먼저 ssr컴포넌트가 실행되고, 

이후에 실행되는게 _app.tsx 부분이다. 

 

ssr 컴포넌트에서는 console.log("getServerSideProps")

_app.tsx 에서는 console.log("_app")

으로 확인해보면 아래와 같이 순서가 찍혀서 나온다. 

위 순서를 기억하고 아래 내용을 읽으면 조금 더 이해가 쉽다.

 

1. query-key 만들어주기

import { getCards } from '@/remote/card'
import { useQuery } from 'react-query'

export default function useCard() {
  return useQuery(['cards'], () => getCards(), {
    suspense: true,
  })
}

 

 

2. SSR에서의 React Query 데이터 사전 처리

import { getCards } from '@/remote/card'
import { dehydrate, QueryClient } from 'react-query'

export default function CardListPage() {
  return <div>CardListPage</div>
}

export async function getServerSideProps() {
  const client = new QueryClient()

  await client.prefetchInfiniteQuery(['cards'], () => getCards())

  return {
    props: {
      dehydratedState: JSON.parse(JSON.stringify(dehydrate(client))),
    },
  }
}

`CardListPage`

클라이언트가  보는 UI를 그리는 부분 

 

 

` getServerSideProps `

CardListPage 가 서버에서 렌더링 될 때 실행될 부분

 

await client.prefetchInfiniteQuery(['cards'], () => getCards())

useCard에서 생성해준 query-key와 맞춰주어야한다. 그래야 cards에 값을 저장하고 사용할 수 있다. 

 

  return {
    props: {
      dehydratedState: JSON.parse(JSON.stringify(dehydrate(client))),
    },
  }

json 형태로 반환해 주어야한다. 

 

` getServerSideProps `에서 받은 값은 _app.tsx 에서 받게된다. 

 

서버에서 렌더링되어 값을 받았을 테니 다음으로 실행될 클라이언트 사이드 ` _app.tsx `로 전송된다. 

 

 

3. _app.tsx 에서 dehydratedState 받기

서버사이드에서 수행한 값을

클라이언트 사이드에서 Hydrate 컴포넌트가 `dehydratedState` 를 받아 클라이언트 상태를 복원해주어야 사용가능하다. 

 

서버사이드 렌더링이 끝난 후

다음 렌더링 순서인 ` _app.tsx `에서 ` rehydrate ` 후 React Query의 클라이언트 상태로 복원하면 된다.  

import type { AppProps } from 'next/app'
import globalStyles from '@/styles/globalStyles'
import { Global } from '@emotion/react'
import Layout from '@/components/shared/Layout'
import { Hydrate, QueryClient, QueryClientProvider } from 'react-query'

const client = new QueryClient({})

export default function App({ Component, pageProps }: AppProps) {
  return (
    <Layout>
      <Global styles={globalStyles} />
      <QueryClientProvider client={client}>
        <Hydrate state={pageProps.dehydratedState}>
          <Component {...pageProps} />
        </Hydrate>
      </QueryClientProvider>
    </Layout>
  )
}

 

 

✨ rehydrate 하는 방법

// 1. pageProps 객체에서 직접 dehydratedState를 참조하는 방법

        <Hydrate state={pageProps.dehydratedState}>
          <Component {...pageProps} />
        </Hydrate>

 

// 2. 함수의 파라미터에서 pageProps를 구조 분해 후
//    dehydratedState와 나머지 프로퍼티들을 명시적으로 분리하는 방법


export default function App({
  Component,
  pageProps: { dehydratedState, ...pageProps },
}: AppProps) {
  return (
    <Layout>
      <Global styles={globalStyles} />
      <QueryClientProvider client={client}>
        <Hydrate state={dehydratedState}>
          <Component {...pageProps} />
        </Hydrate>
      </QueryClientProvider>
    </Layout>
  )
}

 

4. 데이터 사용하기

  const { data: cardList } = useInfiniteQuery(
    ['cards'],
    ({ pageParam }) => getCards(pageParam),

    {
      getNextPageParam: (snapshot) => {
        return snapshot.lastVisible
      },
    },
  )

설정해 주었던 query-key를 이용해 데이터를 불러올 수 있다.