Next.js 기초 입문 20회차: Static Export (정적 사이트 생성) 완전 정복

📚
제20주차 학습 목표

Static Export (정적 사이트 생성)

Next.js 애플리케이션을 정적 HTML로 내보내는 방법을 학습합니다.

Next.js 기초 입문 20회차: Static Export (정적 사이트 생성)

안녕하세요! Next.js 기초 입문 과정의 스무 번째 시간입니다. 오늘은 Next.js의 강력한 기능 중 하나인 Static Export (정적 사이트 생성)에 대해 학습하겠습니다. 이 기능을 통해 Next.js 애플리케이션을 서버 없이도 배포 가능한 순수 정적 HTML, CSS, JavaScript 파일로 변환할 수 있습니다.

📌 이번 회차 학습 목표

  • Next.js의 Static Export 개념과 필요성을 설명할 수 있습니다.
  • 정적 사이트 생성(SSG)의 장점을 이해하고, 어떤 상황에 적합한지 판단할 수 있습니다.
  • Next.js 프로젝트를 Static Export 방식으로 빌드하는 방법을 실습할 수 있습니다.
  • next.config.js 파일을 활용하여 Static Export 설정을 구성할 수 있습니다.
  • Static Export 시 발생할 수 있는 제약 사항과 주의할 점을 파악할 수 있습니다.

📝 개념 설명

정적 사이트(Static Site)란 무엇인가요?

정적 사이트는 사용자의 요청에 따라 서버에서 동적으로 콘텐츠를 생성하지 않고, 미리 생성된 HTML, CSS, JavaScript 파일들을 그대로 제공하는 웹사이트를 의미합니다. 이러한 파일들은 웹 서버(예: Nginx, Apache)나 CDN(콘텐츠 전송 네트워크)을 통해 사용자에게 전송됩니다. 서버 측 로직이 최소화되므로 속도가 빠르고 보안에 강하며, 호스팅 비용이 저렴하다는 장점이 있습니다.

Next.js의 Static Export (정적 사이트 생성)

Next.js는 기본적으로 서버 사이드 렌더링(SSR)이나 정적 사이트 생성(SSG)을 지원하는 프레임워크입니다. 이 중 Static Export는 Next.js 애플리케이션을 빌드할 때, 모든 페이지를 서버에서 미리 렌더링하여 순수한 HTML 파일로 내보내는 기능입니다. 이렇게 생성된 파일들은 별도의 Node.js 서버 없이도 웹 서버나 CDN에 배포하여 서비스할 수 있습니다.

🔄 흐름: Static Export 과정
Next.js 프로젝트 개발
next build 실행
next export 실행
out/ 디렉토리에 정적 파일 생성
정적 파일 배포 (CDN, 웹 서버)

Static Export의 장점

  • 성능 향상: 미리 생성된 HTML 파일을 제공하므로, 서버에서 페이지를 동적으로 생성하는 과정이 없어 응답 시간이 매우 빠릅니다.
  • 보안 강화: 서버 측 로직이 없거나 최소화되어, 서버 관련 취약점으로부터 비교적 안전합니다.
  • 배포 용이성 및 비용 절감: Node.js 서버가 필요 없으므로, S3, Netlify, Vercel, GitHub Pages 등 다양한 정적 호스팅 서비스에 쉽게 배포할 수 있으며, 서버 유지보수 비용을 절감할 수 있습니다.
  • SEO 최적화: 모든 콘텐츠가 미리 HTML로 존재하므로, 검색 엔진 크롤러가 페이지 내용을 쉽게 파악할 수 있어 SEO에 유리합니다.

Static Export의 제약 사항

Static Export는 모든 Next.js 기능을 사용할 수 있는 것은 아닙니다. 다음과 같은 제약 사항이 존재합니다.

  • 서버 사이드 렌더링(SSR) 불가: getServerSideProps를 사용하는 페이지는 Static Export할 수 없습니다. 이는 페이지가 요청 시점에 서버에서 동적으로 데이터를 가져와야 하기 때문입니다.
  • API 라우트 불가: /pages/api 디렉토리에 정의된 API 라우트는 Node.js 서버 환경에서만 동작하므로 Static Export 시 포함되지 않습니다.
  • 동적 라우트 처리: getStaticPaths를 사용하여 빌드 시점에 모든 가능한 동적 경로를 미리 생성해야 합니다. 그렇지 않으면 해당 동적 페이지는 Static Export되지 않습니다.
  • next/image 최적화: 기본 next/image 컴포넌트는 이미지 최적화를 위해 Next.js 서버가 필요합니다. Static Export 시에는 이미지 최적화가 작동하지 않거나, 외부 이미지 최적화 서비스(예: Cloudinary)를 사용해야 합니다.
⚖️ Static Export vs. SSR/ISR
항목 Static Export (SSG) SSR (Server-Side Rendering)
데이터 페칭 시점 빌드 시점 (getStaticProps) 요청 시점 (getServerSideProps)
서버 필요 여부 불필요 (정적 파일만 배포) 필요 (Node.js 서버)
페이지 업데이트 재빌드 및 재배포 필요 요청 시마다 최신 데이터 반영
주요 사용처 블로그, 문서 사이트, 마케팅 페이지 등 콘텐츠 변화가 적은 사이트 사용자별 맞춤형 콘텐츠, 실시간 데이터가 중요한 사이트
성능 매우 빠름 (CDN 활용 가능) 빠름 (첫 로드 시 HTML 제공)

💡 예제 & 실습

이제 Next.js 프로젝트를 생성하고 Static Export를 실습해 보겠습니다.

1. Next.js 프로젝트 생성

먼저 새로운 Next.js 프로젝트를 생성합니다.

npx create-next-app static-export-demo --ts
cd static-export-demo

2. next.config.js 설정

프로젝트 루트에 있는 next.config.js 파일을 열어 output: 'export' 설정을 추가합니다. 이 설정은 Next.js에게 빌드 시 정적 파일을 내보내도록 지시합니다.

// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'export',
  // 선택 사항: 이미지 최적화를 위한 설정 (Static Export 시 기본 next/image는 서버 필요)
  // unoptimized: true, // next/image 최적화 비활성화
  // images: {
  //   loader: 'custom',
  //   loaderFile: './my-image-loader.js',
  // },
};

module.exports = nextConfig;

💡 팁: output: 'export' 설정은 Next.js 13 버전부터 도입된 새로운 방식입니다. 이전 버전에서는 next export 명령어를 사용했지만, 이제는 이 설정을 통해 빌드 과정에서 자동으로 정적 파일이 생성됩니다.

3. 페이지 생성 및 데이터 페칭 (getStaticProps 활용)

Static Export는 빌드 시점에 데이터를 가져와 HTML을 생성하므로, getStaticProps를 사용해야 합니다. 간단한 블로그 게시물 목록 페이지와 상세 페이지를 만들어 보겠습니다.

pages/index.tsx (게시물 목록)

메인 페이지에서 게시물 목록을 가져와 표시합니다.

// pages/index.tsx
import Link from 'next/link';
import { GetStaticProps } from 'next';

interface Post {
  id: number;
  title: string;
}

interface HomeProps {
  posts: Post[];
}

export default function Home({ posts }: HomeProps) {
  return (
    

블로그 게시물

    {posts.map((post) => (
  • {post.title}
  • ))}
); } export const getStaticProps: GetStaticProps = async () => { // 실제 API 호출 대신 가상 데이터 사용 const posts: Post[] = [ { id: 1, title: '첫 번째 게시물' }, { id: 2, title: '두 번째 게시물' }, { id: 3, title: '세 번째 게시물' }, ]; return { props: { posts, }, }; };
pages/posts/[id].tsx (게시물 상세)

동적 라우트를 사용하여 각 게시물의 상세 내용을 표시합니다. getStaticPaths를 사용하여 빌드 시점에 모든 가능한 경로를 미리 생성해야 합니다.

// pages/posts/[id].tsx
import { GetStaticPaths, GetStaticProps } from 'next';
import { useRouter } from 'next/router';

interface Post {
  id: number;
  title: string;
  content: string;
}

interface PostDetailProps {
  post: Post;
}

const allPosts: Post[] = [
  { id: 1, title: '첫 번째 게시물', content: '이것은 첫 번째 게시물의 내용입니다.' },
  { id: 2, title: '두 번째 게시물', content: '이것은 두 번째 게시물의 내용입니다.' },
  { id: 3, title: '세 번째 게시물', content: '이것은 세 번째 게시물의 내용입니다.' },
];

export default function PostDetail({ post }: PostDetailProps) {
  const router = useRouter();

  // fallback: 'blocking' 또는 'true' 설정 시, 빌드되지 않은 경로에 대한 처리
  // 'blocking'은 새로운 요청이 들어오면 서버에서 렌더링 후 캐싱
  // 'true'는 로딩 스피너 등을 보여주고 클라이언트에서 데이터 페칭
  // Static Export에서는 'fallback: false'를 사용하여 빌드 시점에 모든 페이지를 생성해야 합니다.
  if (router.isFallback) {
    return <div>로딩 중...</div>;
  }

  if (!post) {
    return <div>게시물을 찾을 수 없습니다.</div>;
  }

  return (
    

{post.title}

{post.content}

 

목록으로 돌아가기

); } export const getStaticPaths: GetStaticPaths = async () => { // 모든 게시물 ID를 가져와 경로를 생성합니다. const paths = allPosts.map((post) => ({ params: { id: post.id.toString() }, })); return { paths, fallback: false, // 빌드 시점에 생성되지 않은 경로는 404 페이지를 반환합니다. }; }; export const getStaticProps: GetStaticProps = async ({ params }) => { const postId = Number(params?.id); const post = allPosts.find((p) => p.id === postId); if (!post) { return { notFound: true, // 해당 ID의 게시물이 없으면 404 페이지를 반환합니다. }; } return { props: { post, }, }; };

4. 프로젝트 빌드 및 Export

package.json 파일의 scripts 섹션을 확인합니다. build 스크립트가 next build로 설정되어 있을 것입니다. 이 스크립트를 실행하면 output: 'export' 설정에 따라 자동으로 정적 파일이 생성됩니다.

// package.json (scripts 부분)
{
  "name": "static-export-demo",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "next": "^14.0.4",
    "react": "^18",
    "react-dom": "^18"
  },
  "devDependencies": {
    "@types/node": "^20",
    "@types/react": "^18",
    "@types/react-dom": "^18",
    "autoprefixer": "^10.0.1",
    "eslint": "^8",
    "eslint-config-next": "14.0.4",
    "postcss": "^8",
    "tailwindcss": "^3.3.0",
    "typescript": "^5"
  }
}

터미널에서 다음 명령어를 실행하여 빌드합니다.

npm run build

빌드가 완료되면 프로젝트 루트에 out/ 디렉토리가 생성된 것을 확인할 수 있습니다. 이 디렉토리 안에 index.html, posts/1.html, posts/2.html 등 모든 정적 파일이 포함되어 있습니다.

5. 정적 파일 확인

out/ 디렉토리에 생성된 파일을 웹 서버처럼 서빙하여 확인할 수 있습니다. 간단하게 serve 패키지를 설치하여 확인해 보겠습니다.

npm install -g serve
serve out

이제 웹 브라우저에서 http://localhost:3000 (또는 터미널에 표시된 주소)로 접속하면, Next.js 애플리케이션이 정적 파일로 서비스되는 것을 확인할 수 있습니다.

⚠️ 자주 틀리는 것 / 주의사항

  • getServerSideProps 사용 금지: Static Export는 빌드 시점에 모든 페이지를 생성하므로, getServerSideProps를 사용하는 페이지는 빌드되지 않거나 오류가 발생합니다.
  • getStaticPaths 누락: 동적 라우트(예: [id].tsx)를 사용하는 경우, getStaticPaths를 반드시 구현하여 빌드 시점에 생성할 모든 경로를 지정해야 합니다. fallback: false로 설정하면 빌드되지 않은 경로는 404 페이지로 처리됩니다.
  • API 라우트 사용 불가: /pages/api 경로는 Node.js 서버 환경에서만 작동합니다. Static Export된 사이트에서는 API 라우트를 사용할 수 없으므로, 외부 API를 직접 호출하거나 클라이언트 사이드에서 데이터를 페칭해야 합니다.
  • next/image 컴포넌트: 기본 next/image 컴포넌트는 이미지 최적화를 위해 Next.js 서버가 필요합니다. Static Export 시에는 unoptimized: true 옵션을 사용하거나, 외부 이미지 최적화 서비스 또는 일반 <img> 태그를 사용해야 합니다.
  • 환경 변수: Static Export 시에는 NEXT_PUBLIC_ 접두사가 붙은 클라이언트 측 환경 변수만 포함됩니다. 서버 측 환경 변수는 빌드 시점에 사용되고, 배포된 정적 파일에는 포함되지 않습니다.

💡 중요: Static Export는 웹사이트의 모든 콘텐츠가 빌드 시점에 확정되어야 하는 경우에 가장 적합합니다. 블로그, 마케팅 페이지, 문서 사이트 등 콘텐츠 업데이트 주기가 길고 사용자별 맞춤형 데이터가 필요 없는 경우에 강력한 이점을 제공합니다.

🔗 다음 회차 예고

이번 회차에서는 Next.js의 Static Export를 통해 정적 사이트를 생성하는 방법을 학습했습니다. 다음 21회차에서는 Next.js의 배포 전략 (Vercel, Netlify)에 대해 심도 있게 다룰 예정입니다. 오늘 학습한 Static Export된 애플리케이션을 포함하여, Next.js 프로젝트를 실제 운영 환경에 배포하는 다양한 방법을 알아보겠습니다.

✅ 이번 회차 핵심 정리
  • Static Export는 Next.js 앱을 순수 정적 HTML, CSS, JS 파일로 내보내는 기능입니다.
  • next.config.js 파일에 output: 'export' 설정을 추가하여 활성화합니다.
  • 빌드 시점에 데이터를 가져오는 getStaticProps와 동적 경로를 정의하는 getStaticPaths를 사용해야 합니다.
  • 성능, 보안, 배포 용이성, 비용 절감, SEO 최적화 등 많은 장점이 있습니다.
  • SSR, API 라우트, getServerSideProps, next/image의 서버 측 최적화 기능은 Static Export 시 사용할 수 없습니다.
  • 빌드 후 생성되는 out/ 디렉토리의 파일을 정적 호스팅 서비스에 배포할 수 있습니다.

댓글 남기기

Wordpress Social Share Plugin powered by Ultimatelysocial
Copy link
URL has been copied successfully!
THREADS
RSS
error: 저작권 콘텐츠보호를 부탁드립니다.