Next.js Data Fetching

bongbong·2024년 6월 24일
0

Next.js

목록 보기
5/6
post-thumbnail

| 이 포스팅은 Next.js 문서 번역 및 정리한 내용입니다.

기본적으로 Next.js는 모든 페이지를 pre-render한다. React를 단독으로 사용할 때 클라이언트측에서 JavaScript로 렌더링하는 대신 각 페이지에 대해 미리 HTML을 생성한다. pre-rendering을 사용하면 성능과 SEO가 향상될 수 있다.

생성된 각 HTML은 페이지를 위해 필요한 최소한의 JavaScript로 결합하게 되는데 이것을 Hydration이라고 부른다. 브라우저에서 페이지가 로드될 때, JavaScript가 실행되고 페이지를 인터렉티브하게 만든다. Hydration이 완료되어야 이벤트나 로직이 실행될 수 있다.

Pre-rendering

Next.js에는 정적 생성과 서버 측 렌더링이라는 두 가지 형태의 pre-rendering이 있다. 차이점은 페이지의 HTML을 생성하는 시점에 있다.

  • Static Generation : HTML은 빌드 시 생성되며 각 요청에서 재사용된다.
  • Server-side Rendering : 각 요청마다 HTML이 생성된다.

Next.js는 각 페이지마다 사용할 pre-rendering 방식을 선택할 수 있다. 그래서 대부분의 페이지에서 Static Generation을 사용하고 다른 페이지는 Server-side Rendering을 사용해서 하이브리드 Next.js 앱을 만들 수 있다.

성능상의 이유로 Server-side Rendering 보다 Static Generation을 사용하는 것이 좋으며 성능을 높이기 위해 정적으로 생성된 페이지를 CDN으로 캐시할 수 있다. 하지만 몇몇 케이스에서 Server-side Rendering이 유리한 선택일 수 있다.

📎Next.js는 useEffect나 서버사이드 함수를 사용하지 않는 이상 기본적으로 SSG로 렌더링한다.

📎next.js는 기본적으로 pre-rendering을 지원한다. 프리렌더링은 정적 생성, 동적 생성 두 가지로 나뉘는데 정적 생성은 빌드 타임에 HTML을 생성하는 것이고 후자는 서버단에서 HTML을 생성하는 것이다. 브라우저에서 페이지를 로드할 때 js 파일을 받아와 실행시키고 pre-rendering된 HTML과 js 파일을 결합해서 사용자와 상호작용가능하도록 만드는 작업을 Hydration이라고 부르는데 마치 마른 대지에 물을 뿔려주는것같은 단어이다.

Data Fetching

getStaticProps

페이지에서 getStaticProps 함수를 반환하면 빌드 타임에 pre-rendering 한다.

언제 getStaticProps을 사용해야 하나?

  • 페이지를 렌더링하는 데 필요한 데이터가 빌드 타임에 제공된다.
  • CMS로부터 데이터를 받는다.
  • 페이지는 사전 렌더링 되어야 하고 매우 빠르게 서빙되어야 한다. 성능을 위해 CDN에 의해 캐시될 수 있는 HTML, JSON 파일을 생성한다.
  • 특정 상황에서는 미들웨어를 사용하여 경로를 우회하여 사용자별로 다른 페이지를 보여줄수도 있다.

getStaticProps는 언제 실행되나요?

getStaticProps는 항상 서버에서 실행되고 클라이언트에서는 실행되지 않는다. getStaticProps로 작성한 코드는 클라이언트측 번들에서 제거된다.

  • getStaticProps는 항상 next build하는 동안 실행된다.
  • getStaticPropsfallback:true를 사용했을 때 백그라운드에서 실행된다.
  • getStaticPropsfallback:blocking을 사용할 때 초기 렌더링 전에 호출된다.
  • getStaticProps는 revalidate를 사용할 때 백그라운드에서 실행된다.

ISR과 결합해서 사용할 때, getStaticProps는 stale page가 revalidate되는 동안 백그라운드에서 실행되고 새로 생성된 페이지를 브라우저에 제공한다.

getStaticProps는 정적 HTML을 생성하기 때문에 클라이언트의 쿼리 파라미터, HTTP Header에 접근할 수 없다. 이러한 데이터에 접근하려면 미들웨어 사용을 고려해야한다.

server-side code 직접 작성하기

getStaticProps는 서버측에서 실행되므로 클라이언측에서 실행되지 않고 직접 데이터베이스 쿼리를 작성할 수 있다.

getStaticProps에서 API route를 가져오는 대신 서버측 코드를 직접 작성할 수 있다.

그리고 getStaticProps에서 API Routes로 직접 호출하는 것은 서버 내에서 추가 호출이 발생하기 때문에 성능이 저하된다. API Routes 없이 직접 데이터를 가져오는 것이 더 효율적이다.

HTML & JSON 정적 생성

getStaticProps로 pre-rendering된 페이지는 HTML 외에 JSON 파일도 생성한다.

이 JSON 파일은 next/link 또는 next/router를 통한 클라이언트 측 라우팅에 사용된다. getStaticProps를 사용해서 프리렌더링된 페이지로 이동할 때, Next.js는 JSON 파일을 fetch하고 page component의 props로 받아 사용한다. 이는 export한 JSON만 오직 사용하기 때문에 getStaticProps를 호출하지 않는다는 것을 의미한다.

ISR을 사용하는 경우, getStaticProps는 클라이언트측 navigation을 위해 필요한 JSON을 백그라운드에서 생성한다.

✍️ getStaticProps는 빌드 타임에 실행되는 함수이며 이때 JSON으로 반환된다. 그래서 블로그나 랜딩 페이지처럼 정적인 데이터를 표시하는 페이지에서 사용하면 빌드 시점에 이미 HTML로 생성되어 있기 때문에 매우 빠르게 사용자에게 렌더링할 수 있다. 그런데 ISR로 생성하는 경우 fallback 옵션에 따라 서버사이드에서 다시 렌더링되기도 한다.

getStaticPath

페이지에 동적 경로가 있고 getStaticProps를 사용하는 경우 정적으로 생성될 경로 목록들을 정의해줘야 한다.

동적 경로를 사용하는 페이지에서 getStaticPaths 함수를 내보내면 지정된 모든 경로를 pre-rendering 한다.

언제 getStaticPaths를 사용하나?

동적 라우팅을 사용하면서 정적으로 pre-rendering을 하고 싶을 때 사용한다.

  • headless CMS로부터 제공되는 데이터
  • database로부터 제공되는 데이터
  • file system에서 제공하는 데이터
  • 캐시될 수 있는 데이터
  • 페이지가 pre-rendering(for SEO) 되어야 하며 빨라야한다.

언제 getStaticPaths가 실행되나?

빌드 타임에 실행되기 때문에 runtime는 호출되지 않는다. 즉 client-side 번들에는 해당 소스가 포함되지 않는다.

getStaticPaths는 어디에서 사용하나?

  • getStaticProps와 반드시 함께 사용해야한다.
  • getServerSideProps에는 사용할 수 없다.
  • page file에서만 사용할 수 있다.

fallback 옵션

빌드 시에 미리 생성되지 않은 경로에 사용자가 접근했을 때 어떻게 동작할 것인지 결정하는 옵션

  • fallback: false : 빌드 시 지정한 경로 외의 모든 경로는 404 페이지로 처리
  • fallback: true : 백그라운드에서 동적으로 HTML을 생성하고 이 동안 사용자는 로딩 상태를 보게 된다.
  • fallback: blocking : 백그라운드에서 동적으로 HTML을 생성하고, 이 동안 사용자가 기다렸다가 페이지를 서빙받는다.

✍️ getStaticPaths는 빌드 시점에 렌더링을 할 건데 동적인 경로들을 가질 때 사용하는 함수이다. fallback 옵션을 통해 지정한 경로만 허용할 것인지, 동적으로 다시 생성할 것인지, 이 과정에서 로딩을 보여줄 것인지 말것인지 정할 수 있다. 그리고 많은 양의 동적 경로를 생성하게 되면 빌드 시간이 지연되게 되는데 이는 아래처럼 path를 대괄호 처리해서 요청 시점에 생성하도록 해결할수도 있다.

export async function getStaticPaths() {
  // When this is true (in preview environments) don't
  // prerender any static pages
  // (faster builds, but slower initial page load)
  if (process.env.SKIP_BUILD_STATIC_GENERATION) {
    return {
      paths: [],
      fallback: 'blocking',
    }
  }

getServerSideProps

요청 시점에 데이터를 가져오고 페이지의 내용을 렌더링하는 데 사용할 수 있는 Next.js 함수

import type { InferGetServerSidePropsType, GetServerSideProps } from 'next'
 
type Repo = {
  name: string
  stargazers_count: number
}
 
export const getServerSideProps = (async () => {
  // Fetch data from external API
  const res = await fetch('https://api.github.com/repos/vercel/next.js')
  const repo: Repo = await res.json()
  // Pass data to the page via props
  return { props: { repo } }
}) satisfies GetServerSideProps<{ repo: Repo }>
 
export default function Page({
  repo,
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
  return (
    <main>
      <p>{repo.stargazers_count}</p>
    </main>
  )
}

언제 getServerSideProps를 사용하면 좋을까?

개인화된 사용자 요청 데이터, 요청 시점에 최신 데이터를 보여줘야하는 경우

Behavior

  • getServerSideProps는 서버에서 실행된다.
  • getServerSideProps는 오직 페이지에서만 사용할 수 있다.
  • getServerSideProps는 JSON을 반환한다.
  • 사용자가 페이지를 방문하면 getServerSideProps가 요청 시점에 데이터를 가져와 초기 HTML을 렌더링한다.
  • 페이지 컴포넌트에 전달된 props는 초기 HTML 일부로 클라이언트에서 볼 수 있기때문에 민감한 정보는 props에 포함하지 않도록 주의해야한다.
  • 사용자가 'next/link'나 'next/router'를 통해 페이지를 방문하면, Next.js는 서버에 API 요청을 보내고 이 요청에서 getServerSideProps가 실행된다. -> 해당 객체를 통해 서버에서 페이지를 렌더링해서 다음 페이지를 보여주는 것이기 때문에 <a> 링크나 window.location 사용시 화면 전환시 깜빡거리는 현상이 발생한다.
  • getServerSideProps를 사용할 때 CMS, DB를 직접 호출할 수 있다.

Error handling

getServerSideProps 내부에서 오류 발생시 pages/500.js 파일이 표시된다.

Caching with Server-Side Rendering

caching header 설정을 통해 캐싱할 수 있다. cache-control을 직접 제어하기 전에 ISR이 더 적합한 방법인지 고려해보는 것을 추천한다.

export async function getServerSideProps({ req, res }) {
  res.setHeader(
    'Cache-Control',
    'public, s-maxage=10, stale-while-revalidate=59'
  )
 
  return {
    props: {},
  }
}

CSR(Client side Rendering)

브라우저가 로드된 후 Javascript 파일을 실행시켜서 HTML을 만드는 방식이다. 초기 실행시 사용자가 페이지를 보기까지 시간이 걸릴 수 있다는 단점이 있으나 그 뒤부터는 페이지 이동시 깜빡임 없는 부드러운 화면전환을 제공한다는 장점이 있다.

next.js에서 CSR을 사용하는 방법은
1. useEffect()
2. SWR or TanStack Query와 같은 라이브러리

CSR로만 렌더링을 하게 되면 SEO에 불리하다는 단점이 있고 초기 사용자가 페이지를 보기까지 시간이 걸리기 때문에 CSR과 SSR을 결합하여 하이브리드로 사용하기를 장려하고 있다.

ISR(Incremental Static Regeneration)

사이트를 빌드한 후에도 정적 페이지를 생성하거나 업데이트할 수 있게 해준다.

  • Good to know : edge runtime은 ISR과 호환되지 않는다. 캐싱이 필요하면 직접 cache-control 헤더를 설정하여 stale-while-revalidate를 사용해야 한다.

ISR을 사용하려면 getStaticProps에 revalidate props를 추가한다.

function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  )
}
 
// 이 함수는 서버 측에서 빌드 시에 호출.
// 새 요청이 들어오면 revalidate가 활성화된 경우 서버리스 함수에서 다시 호출될 수 있다.
export async function getStaticProps() {
  const res = await fetch('https://.../posts')
  const posts = await res.json()
 
  return {
    props: {
      posts,
    },
    revalidate: 10,
  }
}
 
export async function getStaticPaths() {
  const res = await fetch('https://.../posts')
  const posts = await res.json()
 
  // 포스트를 기반으로 사전 렌더링할 경로를 가져온다.
  const paths = posts.map((post) => ({
    params: { id: post.id },
  }))
 
  // 이 경로들만 빌드 시에 사전 렌더링.
  // { fallback: 'blocking' }은 경로가 존재하지 않는 경우 요청 시에 페이지를 서버 렌더링한다.
  return { paths, fallback: 'blocking' }
}
 
export default Blog
  • 초기 요청 후 10초 이내의 모든 요청은 즉시 캐시된다.
  • 10초가 지난 후 다음 요청도 캐시된 페이지가 표시된다.
  • Next.js는 백그라운드에서 페이지를 다시 생성한다.
  • 페이지 생성에 성공하면 캐시를 무효화하고 업데이트된 페이지를 표시한다.
  • 백그라운드에서 재생성에 실패하면 여전히 이전 페이지를 보여준다.

Good to know : CDN 캐싱이 활성화되어있는지 확인해야한다.

Reference

0개의 댓글