icon안동민 개발노트

정적 데이터 생성 (SSG)


 Next.js의 정적 사이트 생성(Static Site Generation, SSG) 기능은 빌드 시 페이지를 미리 생성하여 성능을 최적화하고 서버 부하를 줄이는 강력한 도구입니다.

 이 절에서는 SSG의 구현 방법과 장점에 대해 자세히 알아보겠습니다.

SSG의 장점

  1. 빠른 페이지 로드 : 미리 생성된 HTML을 제공하므로 매우 빠릅니다.
  2. 서버 부하 감소 : 요청마다 페이지를 생성할 필요가 없습니다.
  3. SEO 최적화 : 검색 엔진이 정적 컨텐츠를 쉽게 크롤링할 수 있습니다.
  4. 보안 : 동적 서버 없이 정적 파일만 제공할 수 있습니다.

generateStaticParams 함수 사용

 generateStaticParams 함수는 동적 라우트에 대한 정적 페이지를 생성할 때 사용됩니다. 이 함수는 빌드 시 실행되어 생성할 페이지의 매개변수를 결정합니다.

 예시

// app/blog/[slug]/page.js
export async function generateStaticParams() {
  const posts = await getPosts() // 모든 포스트 데이터 가져오기
 
  return posts.map((post) => ({
    slug: post.slug,
  }))
}
 
export default function BlogPost({ params }) {
  // params.slug를 사용하여 포스트 데이터 가져오기
  return <div>{/* 포스트 내용 렌더링 */}</div>
}

 이 예제에서 generateStaticParams는 모든 블로그 포스트에 대한 정적 페이지를 생성합니다.

정적 데이터와 동적 데이터의 조합

 SSG를 사용하면서도 일부 동적 데이터를 포함할 수 있습니다.

 예를 들어, 포스트 내용은 정적으로 생성하고 댓글은 클라이언트 사이드에서 동적으로 불러올 수 있습니다.

// app/blog/[slug]/page.js
import { Suspense } from 'react'
import Comments from './Comments'
 
export async function generateStaticParams() {
  // ... (이전과 동일)
}
 
async function getPostData(slug) {
  // 포스트 데이터 가져오기 (빌드 시 실행)
}
 
export default async function BlogPost({ params }) {
  const postData = await getPostData(params.slug)
 
  return (
    <article>
      <h1>{postData.title}</h1>
      <div>{postData.content}</div>
      <Suspense fallback={<div>Loading comments...</div>}>
        <Comments postId={postData.id} />
      </Suspense>
    </article>
  )
}

 여기서 Comments 컴포넌트는 클라이언트 사이드에서 동적으로 데이터를 불러옵니다.

빌드 시 정적 페이지 생성 과정

  1. next build 명령 실행
  2. 페이지 컴포넌트 분석
  3. generateStaticParams 함수 실행 (있는 경우)
  4. 각 경로에 대해 페이지 렌더링 및 HTML 생성
  5. 정적 파일 생성 (HTML, JSON 등)

 빌드 로그를 통해 어떤 페이지가 정적으로 생성되었는지 확인할 수 있습니다.

15장 배포와의 연결

 SSG로 생성된 페이지는 15장에서 다루는 배포 과정과 밀접하게 연관됩니다.

 정적으로 생성된 페이지는 CDN에 쉽게 배포할 수 있으며, 서버리스 환경에서도 효율적으로 운영할 수 있습니다.

 이는 확장성과 비용 효율성 측면에서 큰 이점을 제공합니다.

실습 : 블로그 포스트 목록과 개별 포스트 페이지 SSG 구현

 다음 요구사항을 만족하는 블로그 시스템을 SSG로 구현해보세요.

  1. 블로그 포스트 목록 페이지 /blog
  2. 개별 블로그 포스트 페이지 /blog/[slug]
  3. 각 포스트는 제목, 내용, 작성일을 포함
  4. 최소 5개의 샘플 블로그 포스트 데이터 사용

 구현 예시

// lib/posts.js
export const posts = [
  { id: 1, title: 'First Post', content: '...', date: '2023-01-01', slug: 'first-post' },
  // ... 더 많은 포스트
]
 
export async function getAllPosts() {
  return posts
}
 
export async function getPostBySlug(slug) {
  return posts.find(post => post.slug === slug)
}
 
// app/blog/page.js
import Link from 'next/link'
import { getAllPosts } from '@/lib/posts'
 
export default async function BlogList() {
  const posts = await getAllPosts()
 
  return (
    <div>
      <h1>Blog Posts</h1>
      <ul>
        {posts.map(post => (
          <li key={post.id}>
            <Link href={`/blog/${post.slug}`}>{post.title}</Link>
          </li>
        ))}
      </ul>
    </div>
  )
}
 
// app/blog/[slug]/page.js
import { getAllPosts, getPostBySlug } from '@/lib/posts'
 
export async function generateStaticParams() {
  const posts = await getAllPosts()
  return posts.map(post => ({
    slug: post.slug,
  }))
}
 
export default async function BlogPost({ params }) {
  const post = await getPostBySlug(params.slug)
 
  return (
    <article>
      <h1>{post.title}</h1>
      <p>Published on: {post.date}</p>
      <div>{post.content}</div>
    </article>
  )
}

 이 실습을 통해 Next.js의 SSG 기능을 사용하여 블로그 시스템을 구현하는 방법을 경험할 수 있습니다.

 이는 성능 최적화와 SEO 향상이 중요한 실제 프로젝트에서 매우 유용한 패턴입니다.