iBetter Books
수정

Ch 04. 미리 만들어두다 — SSG

문을 열기 전에 모든 것을 준비한다

빵집을 운영한다고 상상해봅니다. 손님이 오면 그때마다 반죽하고 오븐에 넣는 방법이 있습니다. SSR처럼요. 반면에 문을 열기 전 새벽부터 모든 빵을 구워두는 방법도 있습니다. 손님이 오면 이미 준비된 빵을 바로 내드립니다. 기다릴 필요가 없습니다.

SSG(Static Site Generation)가 두 번째 방식입니다. 배포하기 전, 빌드하는 시점에 모든 페이지의 HTML을 미리 만들어둡니다. 사용자가 요청하면 서버는 미리 만들어진 HTML 파일을 그대로 내려줍니다. 서버가 HTML을 "생성"하는 시간이 없으므로 응답이 매우 빠릅니다.

빌드 시점에 HTML 생성

SSG 페이지는 npm run build를 실행하는 순간에 생성됩니다. 이 시점에 데이터를 가져오고, 컴포넌트를 렌더링하고, HTML 파일을 저장합니다. 이후 사용자 요청은 이 미리 만들어진 파일을 받을 뿐입니다.

Next.js App Router에서는 fetch의 기본 동작이 force-cache입니다. 즉, 특별한 옵션을 주지 않으면 SSG처럼 동작합니다.

// 파일: app/about/page.tsx
// fetch 옵션을 주지 않으면 기본값은 force-cache (SSG처럼 동작)
async function getCompanyInfo() {
  const res = await fetch('https://api.example.com/company', {
    cache: 'force-cache', // 기본값이므로 생략해도 동일
  });
  return res.json();
}

export default async function AboutPage() {
  const info = await getCompanyInfo();

  return (
    <main>
      <h1>{info.name}</h1>
      <p>{info.description}</p>
      <p>설립: {info.foundedYear}년</p>
    </main>
  );
}
```text

### `generateStaticParams` 사용하기

블로그 게시글 페이지처럼 동적 경로가 있는 경우, 어떤 경로들을 미리 만들어둘지 알려줘야 합니다. `generateStaticParams` 함수가 이 역할을 합니다.

```tsx
// 파일: app/posts/[slug]/page.tsx
interface Post {
  slug: string;
  title: string;
  content: string;
  author: string;
}

// 빌드 시점에 존재하는 모든 slug를 반환
export async function generateStaticParams() {
  const res = await fetch('https://api.example.com/posts');
  const posts: Post[] = await res.json();

  return posts.map((post) => ({
    slug: post.slug,
  }));
}

async function getPost(slug: string): Promise<Post> {
  const res = await fetch(`https://api.example.com/posts/${slug}`, {
    cache: 'force-cache',
  });
  return res.json();
}

export default async function PostPage({
  params,
}: {
  params: Promise<{ slug: string }>;
}) {
  const { slug } = await params;
  const post = await getPost(slug);

  return (
    <article>
      <h1>{post.title}</h1>
      <p>작성자: {post.author}</p>
      <div>{post.content}</div>
    </article>
  );
}

generateStaticParams가 반환하는 배열의 각 항목이 하나의 정적 페이지가 됩니다. 슬러그가 10개라면 10개의 HTML 파일이 빌드 시점에 생성됩니다.

장점과 단점

SSG의 장점은 명확합니다.

속도. 미리 만들어진 HTML을 CDN에서 제공하면, 전 세계 어디서든 빠르게 로드됩니다. 서버가 HTML을 생성하는 시간이 없습니다.

서버 부하 없음. 요청마다 서버가 일하지 않습니다. CDN이 파일을 내려줄 뿐입니다.

SEO 최상. 완성된 HTML이 있으므로 크롤러가 내용을 완벽하게 읽습니다.

단점은 실시간성 없음입니다. 빌드 이후에 데이터베이스가 바뀌어도 이미 만들어진 HTML은 변하지 않습니다. 새 게시글이 추가되었다면 다시 빌드해야 반영됩니다. 자주 바뀌는 데이터에는 맞지 않습니다.

다음 챕터에서는

다음 챕터에서는 SSG의 단점을 보완하는 ISR을 살펴봅니다. 빌드 이후에도 정기적으로 페이지를 새로 굽는 방법입니다. "정해진 시간마다 다시 구운 빵"처럼요.