Docs
Rendering
Static Site Generation

Static Site Generation (SSG)

예제

만약 페이지가 정적 생성을 사용한다면, 페이지 HTML은 빌드 타임에 생성됩니다. 즉, 운영 환경에서 페이지 HTML은 next build를 실행할 때 생성됩니다. 이 HTML은 각 요청에서 재사용됩니다. 이는 CDN에 의해 캐시될 수 있습니다.

Next.js에서는 데이터를 사용하거나 사용하지 않고 페이지를 정적으로 생성할 수 있습니다. 각 경우를 살펴보겠습니다.

Static Generation without data

기본적으로, Next.js는 데이터를 가져오지 않고 정적 생성을 사용하여 페이지를 미리 렌더링합니다. 다음은 예제입니다:

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

이 페이지는 미리 렌더링하기 위해 외부 데이터를 가져올 필요가 없다는 점을 주목합니다. 이와 같은 경우, Next.js는 빌드 타임에 페이지당 하나의 HTML 파일을 생성합니다.

Static Generation with data

몇몇 페이지는 미리 렌더링을 위해 외부 데이터를 가져와야 합니다. 두 가지 시나리오가 있으며, 둘 중 하나 또는 둘 다 적용될 수 있습니다. 각 경우에, Next.js가 제공하는 다음 함수를 사용할 수 있습니다:

  1. 페이지 콘텐츠가 외부 데이터에 의존하는 경우: getStaticProps를 사용합니다.
  2. 페이지 경로가 외부 데이터에 의존하는 경우: getStaticPaths를 사용합니다(getStaticProps와 함께 사용하는 경우가 많습니다).

Scenario 1: Your page content depends on external data

예제: 블로그 페이지가 CMS(콘텐츠 관리 시스템)에서 블로그 포스트 목록을 가져와야 할 수 있습니다.

// TODO: 페이지가 미리 렌더링되기 위해 (API 엔드포인트를 호출함으로써)
//       `posts`를 가져와야 합니다.
export default function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li>{post.title}</li>
      ))}
    </ul>
  )
}

프리 렌더링 과정에서 데이터를 가져오기 위해, Next.js는 동일한 파일에서 getStaticProps라는 비동기 함수를 export할 수 있도록 합니다. 이 함수는 빌드 타임에 호출되며, 가져온 데이터를 페이지의 props로 전달할 수 있습니다.

export default function Blog({ posts }) {
  // posts 렌더링...
}
 
// 이 함수는 빌드 타임에 호출됩니다
export async function getStaticProps() {
  // posts를 가져오기 위해 외부 API 엔드포인트를 호출합니다
  const res = await fetch('https://.../posts')
  const posts = await res.json()
 
  // { props: { posts } }를 리턴함으로써, Blog 컴포넌트는
  // 빌드 타임에 `posts`를 prop으로 받게 됩니다
  return {
    props: {
      posts,
    },
  }
}

getStaticProps가 어떻게 작동하는지 자세히 알아보려면, Data Fetching 문서를 확인합니다.

Scenario 2: Your page paths depend on external data

Next.js는 동적 경로를 사용하여 페이지를 생성할 수 있습니다. 예를 들어, id에 기반하여 단일 블로그 포스트를 보여주는 pages/posts/[id].js 파일을 만들 수 있습니다. 이렇게 하면 posts/1에 접근할 때 id: 1의 블로그 포스트를 보여줄 수 있습니다.

동적 라우팅에 대해 더 알아보려면, 동적 라우팅 문서를 확인합니다.

그러나, 빌드 타임에 어떤 id를 프리 렌더링하려는지는 외부 데이터에 따라 달라질 수 있습니다.

예제: 데이터베이스에 블로그 포스트 하나만 추가했다고 가정해보겠습니다(id: 1). 이 경우, 빌드 타임에 posts/1만 프리 렌더링하려고 할 것입니다.

이후, id: 2를 가진 두 번째 포스트를 추가할 수 있습니다. 이 경우 posts/2도 프리 렌더링하려고 할 것입니다.

따라서, 프리 렌더링될 페이지 경로는 외부 데이터에 따라 달라집니다. 이를 처리하기 위해, Next.js는 동적 페이지(여기서는 pages/posts/[id].js)에서 getStaticPaths라는 비동기 함수를 export할 수 있도록 합니다. 이 함수는 빌드 타임에 호출되며 프리 렌더링하고 싶은 경로를 지정할 수 있습니다.

// 이 함수는 빌드 타임에 호출됩니다
export async function getStaticPaths() {
  // posts를 가져오기 위해 외부 API 엔드포인트를 호출합니다
  const res = await fetch('https://.../posts')
  const posts = await res.json()
 
  // posts를 기반으로 프리 렌더링하고 싶은 경로를 가져옵니다
  const paths = posts.map((post) => ({
    params: { id: post.id },
  }))
 
  // 빌드 타임에 이 경로들만 프리 렌더링합니다.
  // { fallback: false }는 다른 경로는 404가 되어야 함을 의미합니다.
  return { paths, fallback: false }
}

또한 pages/posts/[id].js에서, 이 id에 대한 포스트 데이터를 가져와서 페이지를 프리 렌더링할 수 있도록 getStaticPropsexport해야 합니다:

export default function Post({ post }) {
  // post 렌더링...
}
 
export async function getStaticPaths() {
  // ...
}
 
// 이 함수 또한 빌드 타임에 호출됩니다
export async function getStaticProps({ params }) {
  // params에는 포스트 `id`가 포함됩니다.
  // 만약 경로가 /posts/1 이라면, params.id는 1입니다.
  const res = await fetch(`https://.../posts/${params.id}`)
  const post = await res.json()
 
  // 페이지에 post 데이터를 props로 전달합니다
  return { props: { post } }
}

getStaticPaths가 어떻게 작동하는지 자세히 알아보려면, Data Fetching 문서를 확인합니다.

When should I use Static Generation?

가능한 경우 정적 생성을 사용(데이터 포함 또는 미포함)하는 것을 권장합니다. 이렇게 하면 페이지가 한 번 빌드되고 CDN에 의해 제공되므로, 매 요청마다 서버가 페이지를 렌더링하는 것보다 훨씬 빠릅니다.

정적 생성을 많은 유형의 페이지에 사용할 수 있습니다:

  • 마케팅 페이지
  • 블로그 포스트 및 포트폴리오
  • 이커머스 제품 목록
  • 도움 및 문서

스스로 물어봐야 합니다: "이 페이지를 사용자 요청보다 앞서 미리 렌더링할 수 있을까요?" 만약 답이 "예"라면, 정적 생성을 선택해야 합니다.

반면, 사용자 요청에 앞서 페이지를 미리 렌더링할 수 없는 경우 정적 생성은 좋은 아이디어가 아닙니다. 페이지가 자주 업데이트되는 데이터를 보여주거나, 페이지 콘텐츠가 요청마다 변경되는 경우가 그 예입니다.

이런 경우, 다음 중 하나를 수행할 수 있습니다:

  • 클라이언트 사이드 데이터 페칭과 함께 Static Generation 사용: 페이지의 일부를 미리 렌더링하지 않고 클라이언트 사이드 JavaScript를 사용하여 채울 수 있습니다. 이 접근 방식에 대해 자세히 알아보려면 데이터 페칭 문서를 확인합니다.
  • 서버 사이드 렌더링 사용: Next.js는 각 요청에 대해 페이지를 미리 렌더링합니다. CDN에 의해 캐시될 수 없기 때문에 느릴 수 있지만, 미리 렌더링된 페이지는 항상 최신 상태가 됩니다. 이 접근 방식에 대해 아래에서 더 자세히 이야기하겠습니다.