4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Next.js PPR使ったアプリを SSTでデプロイしたらハマった

4
Last updated at Posted at 2025-12-12

Next.js のデプロイ先といえば Vercel が定番ですが、AWS Lambda へのデプロイを可能にする SST (Serverless Stack) という選択肢があります。

本記事では、SST で Next.js 16 をデプロイする方法と、PPR (Partial Pre-Rendering) を有効化する際にハマったポイントと対処法を記載します。

TL;DR

  • SST は Next.js を AWS Lambda + CloudFront にデプロイできる
  • Vercel に比べてコスト削減AWS エコシステム統合がメリット
  • Next.js 16 の PPR(cacheComponents)を使う場合、Lambda ストリーミング設定が必須
  • 設定を間違えるとすべてのリクエストが30秒タイムアウトしてしまう

目次

  1. SST とは
  2. SST で Next.js をデプロイする
  3. Next.js 16 と PPR の設定
  4. Lambda ストリーミングの落とし穴
  5. トラブルシューティング
  6. 参考リンク

SST とは

SST (Serverless Stack) は、AWS 上にサーバーレスアプリケーションをデプロイするためのフレームワークです。

# SST でデプロイ
npx sst deploy --stage production

内部では以下の AWS サービスを自動構築します:

[CloudFront] → [Lambda Function URL] → [Next.js App]
      ↓
[S3] (静的アセット)

SST の特徴

SST 公式ドキュメントより:

  • Infrastructure as Code: TypeScript で AWS リソースを定義
  • ローカル開発: sst dev でローカルと AWS を接続
  • OpenNext 統合: Next.js を Lambda 向けに最適化ビルド
  • 自動スケーリング: Lambda の特性を活かしたスケール

OpenNext について

SST は内部で OpenNext を使用しています。OpenNext は Next.js のビルド出力を AWS Lambda や Cloudflare Workers などの環境にデプロイ可能な形式に変換するフレームワークです。


SST で Next.js をデプロイする

1. SST のインストール

# 新規プロジェクト
npx create-sst@latest my-nextjs-app
cd my-nextjs-app

# 既存 Next.js プロジェクトに追加
npx sst@latest init

2. sst.config.ts の設定

// sst.config.ts
export default $config({
  app(input) {
    return {
      name: "my-nextjs-app",
      removal: input?.stage === "production" ? "retain" : "remove",
      protect: ["production"].includes(input?.stage),
      home: "aws",
      providers: {
        aws: {
          region: "ap-northeast-1",
        },
      },
    };
  },
  async run() {
    const site = new sst.aws.Nextjs("Web", {
      path: ".",
      domain: {
        name: "example.com",
        aliases: ["www.example.com"],
      },
      warm: 5, // Lambda ウォームスタート
      server: {
        runtime: "nodejs22.x",
        memory: "2048 MB",
        timeout: "30 seconds",
      },
    });

    return {
      url: site.url,
    };
  },
});

3. デプロイ

# 開発環境
npx sst dev

# ステージング
npx sst deploy --stage staging

# 本番
npx sst deploy --stage production

これだけでドメインが AWS にあれば Next.js の環境が立ち上がります。


Next.js 16 と PPR の設定

PPR とは

PPR (Partial Pre-Rendering) は、ページの静的部分を事前レンダリングし、動的部分をストリーミングする機能です。

従来: [リクエスト] → [全体レンダリング] → [レスポンス]
                          ↑
                    データ取得待ち(遅い)

PPR:  [リクエスト] → [静的シェル即返却] → [動的部分ストリーミング]
                          ↑                      ↑
                    ビルド時生成(高速)    Suspense でストリーム

PPR の有効化

// next.config.ts
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  experimental: {
    // Next.js 15 以前
    // ppr: true,
  },
  // Next.js 16 以降(ppr は cacheComponents に統合)
  cacheComponents: true,
};

export default nextConfig;

レイアウト設計のポイント

PPR を活かすには、RootLayout を静的に保つことが重要です。

// app/layout.tsx - 静的(セッション取得しない)
export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <AppProviders session={null}>
          {children}
        </AppProviders>
      </body>
    </html>
  );
}

認証が必要なページは、下層のレイアウトで connection() を使って動的オプトインします。

// app/(protected)/layout.tsx - 動的(connection()でオプトイン)
import { connection } from "next/server";

const ProtectedLayout = async ({ children }) => {
  await connection(); // 動的レンダリングを明示
  const session = await getServerAuthSession();
  if (!session) redirect("/auth/sign-in");
  return <>{children}</>;
};

なぜこの設計か?

  • RootLayout でセッション取得すると、サイト全体が動的になる
  • PPR は「静的シェル」を即座に返すことで高速化する
  • RootLayout が動的だと、静的シェルをプリレンダリングできない

Lambda ストリーミングの落とし穴

症状:全リクエストが30秒タイムアウト

PPR を有効化して SST でデプロイしたら、以下の症状が発生しました:

  • 全ページのナビゲーションが約30秒かかる
  • CloudWatch Logs で全リクエストがタイムアウト直前で完了
  • Vercel では正常に動作する

原因:Lambda の InvokeMode

Lambda Function URL には2つのモードがあります:

モード 動作 Next.js Streaming
BUFFERED レスポンス全体をバッファ 動作しない
RESPONSE_STREAM ストリーミング 動作する

SST はデフォルトで BUFFERED モードを使用します。

Next.js の Suspense/Streaming は RESPONSE_STREAM を必要とするため、BUFFERED ではレスポンスが完了せず、30秒タイムアウトまでハングします。

解決:open-next.config.ts を作成

SST は内部で OpenNext を使用します。ストリーミングは OpenNext の設定ファイルで有効化します。

// open-next.config.ts(プロジェクトルートに作成)

/**
 * OpenNext configuration for AWS Lambda streaming
 *
 * CRITICAL: Without streaming enabled, Lambda uses BUFFERED mode which causes
 * all requests to hang until 30s timeout because Next.js Suspense/Streaming
 * never completes in buffered mode.
 */
const config = {
  default: {
    override: {
      wrapper: "aws-lambda-streaming",
    },
  },
};

export default config;

設定確認

ビルド後、.open-next/open-next.output.json を確認:

{
  "origins": {
    "default": {
      "streaming": true,
      "wrapper": "aws-lambda-streaming"
    }
  }
}

デプロイ後、AWS CLI で確認:

aws lambda get-function-url-config --function-name <function-name>
# "InvokeMode": "RESPONSE_STREAM" を確認

トラブルシューティング

React Compiler でビルドエラー

Next.js 16 の React Compiler で一部ファイルがエラーになる場合、ファイル先頭でオプトアウトできます。

"use no memo";

export const ProblematicComponent = () => {
  // ...
};

コールドスタートが遅い

// sst.config.ts
new sst.aws.Nextjs("Web", {
  warm: 5, // プリウォーミング
  server: {
    memory: "2048 MB", // メモリ増加 = CPU増加
  },
});

宣伝

参考リンク

公式ドキュメント

関連記事

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?