문서
App Router 사용하기
정적 내보내기

정적 내보내기

Next.js는 정적 사이트나 단일 페이지 애플리케이션(SPA)으로 시작한 다음, 나중에 선택적으로 서버가 필요한 기능을 사용하도록 업그레이드할 수 있게 해줍니다.

next build를 실행할 때, Next.js는 각 라우트마다 HTML 파일을 생성합니다. 엄격한 SPA를 개별 HTML 파일로 분리함으로써, Next.js는 클라이언트 측에서 불필요한 JavaScript 코드 로딩을 피할 수 있어 번들 크기를 줄이고 더 빠른 페이지 로딩을 가능하게 합니다.

Next.js가 이러한 정적 내보내기를 지원하므로, HTML/CSS/JS 정적 자산을 제공할 수 있는 모든 웹 서버에 배포하고 호스팅할 수 있습니다.

구성

정적 내보내기를 활성화하려면 next.config.js 내의 출력 모드를 변경하세요:

next.config.js
/**
 * @type {import('next').NextConfig}
 */
const nextConfig = {
  output: "export",
 
  // 선택사항: 링크를 `/me` -> `/me/`로 변경하고 `/me.html` -> `/me/index.html`로 내보냅니다
  // trailingSlash: true,
 
  // 선택사항: 자동 `/me` -> `/me/` 변환을 방지하고 대신 `href`를 보존합니다
  // skipTrailingSlashRedirect: true,
 
  // 선택사항: 출력 디렉토리를 `out` -> `dist`로 변경합니다
  // distDir: 'dist',
};
 
module.exports = nextConfig;

next build를 실행한 후, Next.js는 애플리케이션의 HTML/CSS/JS 자산을 포함하는 out 폴더를 생성합니다.

지원되는 기능

Next.js의 핵심은 정적 내보내기를 지원하도록 설계되었습니다.

서버 컴포넌트

정적 내보내기를 생성하기 위해 next build를 실행할 때, app 디렉토리 내에서 사용되는 서버 컴포넌트는 빌드 중에 실행됩니다. 이는 전통적인 정적 사이트 생성과 유사합니다.

결과 컴포넌트는 초기 페이지 로드를 위한 정적 HTML로 렌더링되고, 라우트 간 클라이언트 네비게이션을 위한 정적 페이로드로 렌더링됩니다. 동적 서버 함수를 사용하지 않는 한, 정적 내보내기를 사용할 때 서버 컴포넌트에 대한 변경은 필요하지 않습니다.

app/page.tsx
export default async function Page() {
  // 이 fetch는 `next build` 중에 서버에서 실행됩니다
  const res = await fetch("https://api.example.com/...");
  const data = await res.json();
 
  return <main>...</main>;
}

클라이언트 컴포넌트

클라이언트에서 데이터 페칭을 수행하려면 SWR을 사용하는 클라이언트 컴포넌트를 사용하여 요청을 메모이제이션할 수 있습니다.

app/other/page.tsx
"use client";
 
import useSWR from "swr";
 
const fetcher = (url: string) => fetch(url).then((r) => r.json());
 
export default function Page() {
  const { data, error } = useSWR(
    `https://jsonplaceholder.typicode.com/posts/1`,
    fetcher
  );
  if (error) return "Failed to load";
  if (!data) return "Loading...";
 
  return data.title;
}
app/other/page.js
"use client";
 
import useSWR from "swr";
 
const fetcher = (url) => fetch(url).then((r) => r.json());
 
export default function Page() {
  const { data, error } = useSWR(
    `https://jsonplaceholder.typicode.com/posts/1`,
    fetcher
  );
  if (error) return "Failed to load";
  if (!data) return "Loading...";
 
  return data.title;
}

라우트 전환이 클라이언트 측에서 일어나기 때문에, 이는 전통적인 SPA처럼 동작합니다. 예를 들어, 다음 인덱스 라우트는 클라이언트에서 다른 포스트로 네비게이션할 수 있게 해줍니다:

app/page.tsx
import Link from "next/link";
 
export default function Page() {
  return (
    <>
      <h1>Index Page</h1>
      <hr />
      <ul>
        <li>
          <Link href="/post/1">Post 1</Link>
        </li>
        <li>
          <Link href="/post/2">Post 2</Link>
        </li>
      </ul>
    </>
  );
}
app/page.js
import Link from "next/link";
 
export default function Page() {
  return (
    <>
      <h1>Index Page</h1>
      <p>
        <Link href="/other">Other Page</Link>
      </p>
    </>
  );
}

이미지 최적화

next/image를 통한 이미지 최적화next.config.js에서 사용자 정의 이미지 로더를 정의하여 정적 내보내기와 함께 사용할 수 있습니다. 예를 들어, Cloudinary와 같은 서비스로 이미지를 최적화할 수 있습니다:

next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  output: "export",
  images: {
    loader: "custom",
    loaderFile: "./my-loader.ts",
  },
};
 
module.exports = nextConfig;

이 사용자 정의 로더는 원격 소스에서 이미지를 가져오는 방법을 정의합니다. 예를 들어, 다음 로더는 Cloudinary의 URL을 구성합니다:

my-loader.ts
export default function cloudinaryLoader({
  src,
  width,
  quality,
}: {
  src: string;
  width: number;
  quality?: number;
}) {
  const params = ["f_auto", "c_limit", `w_${width}`, `q_${quality || "auto"}`];
  return `https://res.cloudinary.com/demo/image/upload/${params.join(
    ","
  )}${src}`;
}
my-loader.js
export default function cloudinaryLoader({ src, width, quality }) {
  const params = ["f_auto", "c_limit", `w_${width}`, `q_${quality || "auto"}`];
  return `https://res.cloudinary.com/demo/image/upload/${params.join(
    ","
  )}${src}`;
}

그런 다음 애플리케이션에서 next/image를 사용할 수 있으며, Cloudinary의 이미지에 대한 상대 경로를 정의할 수 있습니다:

app/page.tsx
import Image from "next/image";
 
export default function Page() {
  return <Image alt="turtles" src="/turtles.jpg" width={300} height={300} />;
}
app/page.js
import Image from "next/image";
 
export default function Page() {
  return <Image alt="turtles" src="/turtles.jpg" width={300} height={300} />;
}

라우트 핸들러

라우트 핸들러는 next build를 실행할 때 정적 응답을 렌더링합니다. GET HTTP 메서드만 지원됩니다. 이는 캐시된 데이터나 캐시되지 않은 데이터에서 정적 HTML, JSON, TXT 또는 다른 파일을 생성하는 데 사용할 수 있습니다. 예를 들어:

app/data.json/route.ts
export async function GET() {
  return Response.json({ name: "Lee" });
}
app/data.json/route.js
export async function GET() {
  return Response.json({ name: "Lee" });
}

위의 app/data.json/route.ts 파일은 next build 중에 정적 파일로 렌더링되어 { name: 'Lee' }를 포함하는 data.json을 생성합니다.

들어오는 요청에서 동적 값을 읽어야 하는 경우, 정적 내보내기를 사용할 수 없습니다.

브라우저 API

클라이언트 컴포넌트는 next build 중에 HTML로 사전 렌더링됩니다. Web API와 같은 window, localStorage, navigator는 서버에서 사용할 수 없기 때문에, 브라우저에서 실행될 때만 이러한 API에 안전하게 접근해야 합니다. 예를 들어:

'use client';
 
import { useEffect } from 'react';
 
export default function ClientComponent() {
  useEffect(() => {
    // 이제 `window`에 접근할 수 있습니다
    console.log(window.innerHeight);
  }, [])
 
  return ...;
}

지원되지 않는 기능

Node.js 서버가 필요하거나 빌드 프로세스 중에 계산할 수 없는 동적 로직이 필요한 기능은 지원되지 않습니다:

next dev로 이러한 기능을 사용하려고 하면 루트 레이아웃에서 dynamic 옵션을 error로 설정한 것과 유사한 오류가 발생합니다.

export const dynamic = "error";

배포

정적 내보내기를 사용하면 Next.js를 HTML/CSS/JS 정적 자산을 제공할 수 있는 모든 웹 서버에 배포하고 호스팅할 수 있습니다.

next build를 실행할 때, Next.js는 out 폴더에 정적 내보내기를 생성합니다. 예를 들어, 다음과 같은 라우트가 있다고 가정해 봅시다:

  • /
  • /blog/[id]

next build를 실행한 후, Next.js는 다음 파일들을 생성합니다:

  • /out/index.html
  • /out/404.html
  • /out/blog/post-1.html
  • /out/blog/post-2.html

Nginx와 같은 정적 호스트를 사용하는 경우, 들어오는 요청을 올바른 파일로 리라이트하도록 구성할 수 있습니다:

nginx.conf
server {
  listen 80;
  server_name acme.com;
 
  root /var/www/out;
 
  location / {
      try_files $uri $uri.html $uri/ =404;
  }
 
  # 이는 `trailingSlash: false`일 때 필요합니다.
  # `trailingSlash: true`일 때는 이를 생략할 수 있습니다.
  location /blog/ {
      rewrite ^/blog/(.*)$ /blog/$1.html break;
  }
 
  error_page 404 /404.html;
  location = /404.html {
      internal;
  }
}

버전 이력

버전변경사항
v14.0.0next export가 제거되고 "output": "export"로 대체됨
v13.4.0App Router (안정화)가 React 서버 컴포넌트 및 라우트 핸들러를 포함한 향상된 정적 내보내기 지원을 추가함
v13.3.0next export가 더 이상 사용되지 않고 "output": "export"로 대체됨