CodeGym /コース /ChatGPT Apps /Vercel へのデプロイ: リポジトリ、環境変数、preview → production

Vercel へのデプロイ: リポジトリ、環境変数、preview → production

ChatGPT Apps
レベル 7 , レッスン 3
使用可能

1. なぜ ChatGPT App に Vercel を使うのか

前回までの講義では、GiftGenius をローカルで起動し、Dev Mode とトンネル経由で ChatGPT に接続しました。今回はさらに一歩進めて「本番運用」に近づけ、同じコードを Vercel に載せます。

この時点で、あなたの手元には動作する GiftGenius(学習用のサンプル App)があります。ローカルでは Next.js 16 上で MCP エンドポイント(例: /api/mcp)を提供し、公式の ChatGPT Apps SDK Next.js Starter に基づいて構築されています。

「VPS を借りて Node と nginx を手動で入れて自分で全部設定する」道を選ぶこともできますが、Next.js に対してそれは、2025 年に素の document.write でフロントエンドを書くようなものです。動きはしますが、わざわざ自分を苦しめる選択です。

Vercel が私たちに向いている理由はいくつもあります。

まず、Vercel は Next.js をネイティブに理解しています。ビルド、SSR、静的配信、Edge レイヤー、サーバーレス関数を自動で設定してくれます。ChatGPT App にとっては特に便利で、ウィジェットと MCP エンドポイントをワンクリックで同一インフラに展開できます。

次に、Vercel は CI/CD が標準搭載です。Git リポジトリを接続すると、各 push ごとにユニークな URL のイミュータブルなデプロイが作られます。main ブランチからのデプロイは production、それ以外は preview として扱われます。

さらに、Vercel は環境とシークレットの扱いが優秀です。環境変数を Development/Preview/Production に明確に分け、暗号化して保存し、Next.js に簡単に注入できます。ChatGPT App では、キーや MCP サーバーの URL を環境ごとに切り替える必要があるため、まさに理想的です。

そして、Vercel には便利なロールバックがあります。新しいリリースが不調でも、以前の成功したデプロイをすばやくプロモートして、システムを健全な状態に戻せます。これにより「デプロイの恐怖」が減り、小さな頻繁リリースが促進されます。

最後に、Vercel は Next.js の開発元です。彼らは Next.js と自社のサーバーを相互に最適化しています。Vercel を使うと、いかにスムーズに数クリックで物事が動くかを何度も体感するでしょう。きっと気に入るはずです。

2. 出発点: GiftGenius のプロジェクト構成

このコースの方針では、GiftGenius は 1 つのリポジトリにあります。構成は 2 通りあり、どちらも Vercel で問題ありません:

1) 複数アプリのモノレポ — 例:

giftgenius/
  apps/
    web/   # Next.js(ウィジェット + MCP)
    mcp/   # 独立した MCP サーバー(もし切り出していれば)

2) 単一の Next.js プロジェクト(ウィジェットと MCP が同居。初期はこれが簡単で、公式スターターも同様):

giftgenius/
  app/
    page.tsx         # ウィジェット
    api/
      mcp/route.ts   # MCP エンドポイント
  next.config.mjs
  package.json
  ...

モジュール 2 の講義では Apps SDK Starter をクローンし、依存を入れて npm run dev を起動しました。ここでは次を前提とします:

  • プロジェクトはすでに Git(GitHub / GitLab / Bitbucket)で管理されている;
  • ローカルではキー(OPENAI_API_KEY など)を含む .env.local を使っている;
  • ChatGPT の Dev Mode はあなたのトンネルに接続されている。

目標は、同じコードを Vercel でもビルド・動作させ、ChatGPT がトンネルではなく、https://giftgenius.vercel.app のような安定した HTTPS ドメインにアクセスできるようにすることです。

3. デプロイ前のリポジトリ準備

Vercel で「New Project」を押す前に、リポジトリを少し整えておきましょう。どれも簡単ですが、後で大きな時間節約になります。

まず、.env.local.vercel がリポジトリに含まれないことを確認します。.gitignore には Next.js Starter で通常項目が入っていますが、念のため再確認を:

node_modules
.next
.env.local
.vercel

.env.local はローカルの設定とシークレットです。特に OPENAI_API_KEY や DB キーがあるなら、Git に入れてはいけません。Vercel 上では UI から別途シークレットを管理します。

次に、package.json を確認します。Vercel では正しい scripts が重要です:

{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start"
  }
}

Vercel はデフォルトで npm run build(pnpm を使うなら pnpm build)を呼びます。これでエラーなくビルドできなければなりません。

さらに、Next.js 16 に適した Node のバージョンが明示されているか確認します。Next.js 16 のリリースノートでは最小が 18.18.0 です。多くの場合、package.json に次の指定で足ります:

{
  "engines": {
    "node": ">=18.18.0"
  }
}

Vercel はアプリに互換な LTS の Node を選択します。

ここまでできたら、最新コードを Git に push して Vercel に進みましょう。

4. Vercel への初回インポート

ここから Vercel の Web インターフェースに進みます。まだ登録していない場合は、今がその時です。

Vercel にログインし「New Project」をクリック、リストから自分のリポジトリ giftgenius を選びます。この段階で Vercel はリポジトリ内容を解析し、ほぼ自動で Next.js プロジェクトだと判定して適切なプリセットを適用します。

プロジェクト設定では Vercel から次が提案されます:

  • Framework = Next.js;
  • Build Command = npm run build(または pnpm build/yarn build);
  • Output Directory = 標準の .next(変更不要)。

初回デプロイでは環境変数をすぐに指定しなくても構いません(後で追加します)。「Deploy」を押すと、Vercel がリポジトリをクローンし、依存をインストール、npm run build を実行し、成功すれば https://giftgenius-xyz.vercel.app のようなアドレスで最初のデプロイが作成されます。

重要なポイント: 各デプロイはイミュータブルです。変更を push すると新しい URL の新しいデプロイが作られ、古いものは履歴に残ります。プロダクションドメイン(giftgenius.vercel.app やカスタムドメイン)は特定のデプロイを指し、ロールバックで以前のデプロイに切り替えられます。

概念図は次の通りです:

flowchart LR
    A[GitHub repo
giftgenius] -->|git push| B[Vercel build] B --> C[Preview Deploy #1
unique URL] B --> D[Preview Deploy #2
unique URL] D --> E[Production Alias
giftgenius.vercel.app]

Git の main ブランチは通常 production ブランチとして扱われ、それ以外は preview です。これは設定で変更できます。

5. Vercel の環境変数

初回デプロイは多くの場合まだ正常に動きません。OPENAI_API_KEY が未設定だったり、MCP サーバーが外部 API にアクセスできなかったりするためです。ここで環境変数を整えます。

Vercel の環境変数は Settings → Environment Variables で管理します。ここで Development、Preview、Production の 3 つのスコープに分かれているのが分かります。

理解の助けになる対応表:

Scope 使用場所 ローカルの類推
Development vercel dev と Vercel CLI 経由のローカル開発 .env.local
Preview production ブランチ以外からのすべてのデプロイ staging / test
Production production ブランチ(通常は main)からのデプロイ 本番相当の .env.prod

ローカルの .env.local と違い、Vercel は値を暗号化して保存し、Next.js のコードからは process.env.MY_VAR として自動で参照できます。

プレフィックス NEXT_PUBLIC_ の意味を正しく理解しましょう。NEXT_PUBLIC_ で始まるものはブラウザのバンドルに含まれ、誰でも(DevTools で)見られます。これは公開設定(NEXT_PUBLIC_ENV=previewNEXT_PUBLIC_API_BASE_URL=https://giftgenius.vercel.app など)には有用ですが、OPENAI_API_KEY のようなキーには厳禁です。

シークレットは NEXT_PUBLIC_ を付けず、サーバーサイド(route handler、MCP ツールなど)だけで読み込みます。

6. GiftGenius の環境変数設定例

学習用の GiftGenius に必要な環境変数を見てみましょう。

最小構成の例:

  • OPENAI_API_KEY — モデル呼び出し/MCP クライアント用のキー;
  • APP_BASE_URL — アプリのベース URL(https://giftgenius.vercel.app もしくは preview の URL);
  • 必要に応じて GIFTDATA_API_URLPRODUCTS_API_URL(外部カタログがある場合)。

ローカル開発では .env.local に置きます:

OPENAI_API_KEY=sk-local-...
APP_BASE_URL=http://localhost:3000
PRODUCTS_API_URL=https://dev-api.gifts.example.com

Vercel では Settings → Environment Variables に進み、同じキーと値を該当するスコープに追加します。

MCP エンドポイントのコード側の例:

// app/api/mcp/route.ts
import { NextRequest } from 'next/server';

const apiKey = process.env.OPENAI_API_KEY!; // 実コードでは未チェックのままにしないでください :)

export async function POST(req: NextRequest) {
  if (!apiKey) {
    return new Response('Missing OPENAI_API_KEY', { status: 500 });
  }
  // apiKey を使って OpenAI や他サービスを呼び出す...
}

ウィジェットはサーバーサイドで APP_BASE_URL を使い、ChatGPT の iframe やスターターの assetPrefix/basePath 設定を考慮して絶対 URL を組み立てられます。

クライアントからの window.fetch でバックエンドの公開 API を叩く必要があれば、NEXT_PUBLIC_API_BASE_URL を用意しても良いでしょう。ただし NEXT_PUBLIC_OPENAI_API_KEY は決して設定してはいけません。

7. Preview デプロイ: 強力なステージング

ここからは一番楽しい preview デプロイの話です。Git リポジトリを接続すると、Vercel は production 以外のブランチへの push や各 Pull Request ごとに自動で preview デプロイを作成します。各デプロイには次のようなユニーク URL が付きます:

https://giftgenius-git-feature-new-layout-username.vercel.app

これらのデプロイは env の Preview スコープを使うため、例えば次のように分けられます:

# Vercel の Preview 環境変数
APP_BASE_URL=https://giftgenius-staging.vercel.app
PRODUCTS_API_URL=https://staging-api.gifts.example.com

これで production と取り違えることがありません。

ChatGPT の Dev Mode 的には、preview URL はステージングに最適です。Dev App の設定で、トンネルの URL を一時的に preview URL に変え、すでにビルドされた GiftGenius の挙動(ただしまだ production ではない)を確認できます。

よくある流れ: 機能開発用に feature/smart-recommendations ブランチを作って push → Vercel が preview リンクを発行。Dev Mode に行き、その URL を設定して、GPT とのシナリオ(ギフト選定、カード表示、MCP ツール呼び出し)を確認。問題なければ main にマージ。Production はその間、安定稼働を続けます。

パイプラインのメンタルモデル:

flowchart TD
    A[Local dev
localhost + トンネル] --> B[git push
feature/*] B --> C[Preview Deploy
preview-URL] C --> D[ChatGPT Dev Mode
App → preview-URL] C --> E[コードレビュー / テスト] E --> F[main にマージ] F --> G[Production Deploy
prod-URL] G --> H[ChatGPT Prod App
App → prod-URL]

8. Production デプロイとロールバック

main(または設定した production ブランチ)にマージすると、Vercel は production デプロイを作り、giftgenius.vercel.app やカスタムドメインといった production エイリアスを割り当てます。

この時点で(後ほど作成する)ChatGPT の Prod App は production の URL をエンドポイントに設定しておきます。Dev Mode では引き続きトンネルや preview URL を使って実験し、ChatGPT Store の一般ユーザーは production にアクセスするイメージです。

イミュータブルデプロイの利点は、ロールバックが非常に簡単なことです。新しいリリースが不調(たとえば本番データで MCP ツールが落ちる)でも、慌てて本番で直す必要はありません。Vercel のデプロイ一覧から以前の成功デプロイを選び「Promote to Production」のような操作をすると、遠くの K8s や Lambda が切り替わり、ドメインは安定版を指すようになります。

CLI でも vercel rollback のようなコマンドで自動化できますが、本講義の範囲では「各デプロイは独立したアーティファクトで、production エイリアスはどれにでも向け直せる」という理解で十分です。

9. Vercel 上の Next.js 16 + MCP の特性

Vercel から見た Next.js の MCP エンドポイントは、サーバーレス関数(設定によっては Edge 関数)です。これは短命で、リクエストで起動し、処理して終了します。外部の DB やストレージを使わない限り、呼び出し間で状態を保持できません。

これは MCP にとって致命的です。もし route.ts にあるグローバル配列 let history = [] に対話履歴を保存すると、コールドスタートのたびに初期化されます。状態を保持するには外部システム(KV、Postgres など)が必要ですが、その話は後のモジュールで扱います。

もう 1 つは実行タイムアウトです。無料プランの Vercel ではサーバーレス関数の実行時間に制限があります(本資料の作成時点では Hobby で約 10 秒、Pro はより長い)。LLM リクエストや特に MCP ツールのチェーンでは不足することがあります。

Next.js 16 の route handler では、Vercel に対してより長い実行時間を要求するために maxDuration を指定できます(プランの範囲内):

// app/api/mcp/route.ts
export const maxDuration = 60; // 秒。Pro では最大 300 まで

export async function POST(req: Request) {
  // 時間のかかる処理: OpenAI や外部 DB へのリクエストなど
}

これは「無制限に動く魔法のボタン」ではありませんが、「この関数は長めに動く可能性があるので、早まって殺さないで」と Vercel に正しく伝える方法です。

最後に、ChatGPT の iframe の特性を忘れないでください。Apps SDK Starter ではすでに assetPrefixbasePath が設定され、web-sandbox.oaiusercontent.com のネストした iframe 内でも静的ファイルやルーティングが正しく動くようにしてあります。これにより、リクエストは sandbox ではなくあなたのドメインに向きます。Vercel にデプロイしてもこの設定は維持され、ウィジェットは箱から出してすぐに正しく動作します。

10. デプロイ後の ChatGPT 連携

形式的には Store や本番運用のモジュール寄りの話ですが、デプロイ後のアプリの動かし方・ChatGPT との連携ロジックは単純で、今の段階でも理解しやすいです。

まず GiftGenius を Vercel にデプロイして production の URL を得ます。次に ChatGPT の Dev Mode で別の App(例: GiftGenius Prod)を作り、その設定でエンドポイントとしてこの URL(正確にはガイドに沿って https://giftgenius.vercel.app/api/mcp のような MCP エンドポイント)を指定します。

開発では引き続き Dev App を使い、トンネルや preview URL を向けます。日次/週次ビルドの検証用に Staging App を作り、安定した preview エイリアスに紐づけるのも良いでしょう。結果として 3 段構えになります:

Dev App     → ローカルトンネル または dev-URL(不安定)
Staging App → 安定した preview/staging URL(Vercel)
Prod App    → production URL(Vercel)

目安として、次の表にまとめます:

対象 URL / Vercel デプロイ Vercel のスコープ アクセスする人
Dev App ローカルトンネル / vercel dev Development あなた / チーム
Staging App 安定した preview エイリアス Preview チーム / QA
Prod App giftgenius.vercel.app / カスタムドメイン Production ユーザー

これは冒頭で述べた local / staging / prod モデルを、Vercel と ChatGPT Apps に結びつけたものです。もはや永遠の localhost ではなく、ちゃんとしたプロジェクトのアーキテクチャになっています。

11. Vercel デプロイでのよくあるミス

エラー No.1: .env.local にだけシークレットがあり、Vercel に設定していない。
ローカルでは動くので自信を持って「Deploy」を押したら、アプリはビルドできたが本番で MCP ツールが 500「Missing OPENAI_API_KEY」を返す——よくある話です。原因は単純で、Vercel はあなたのローカル .env.local を知りません。同じ変数を Vercel プロジェクト設定に(そして正しいスコープ Preview/Production に)登録する必要があります。

エラー No.2: 機微情報に NEXT_PUBLIC_ を使ってしまう。
「とにかく動かしたい」という思いから、クライアント側でキーにアクセスするために NEXT_PUBLIC_OPENAI_API_KEY を書いてしまうことがあります。結果、そのキーは JS バンドルに入り、誰でも取得できます。これは悪手というレベルではなく、漏洩とキー停止への最短経路です。すべてのシークレットはプレフィックスなし・サーバーサイドのみで使用しましょう。

エラー No.3: ローカルと Vercel の環境が不整合。
ローカルではプロダクトの URL が http://localhost:4000、Vercel の Preview では https://api.gifts-staging.com、本番では別の URL、という具合にバラバラになりがちです。必要な環境変数のリストを整備せず、Preview/Production で正しく埋まっているか点検しないと、「本番ウィジェットが staging バックエンドに、staging ウィジェットが本番に」という入れ違いが起きます。単純な対策ですが、変数を文書化し、各環境でチェックする習慣が効きます。

エラー No.4: MCP エンドポイントの実行時間制限を無視。
ローカルでは遅い外部システムの応答を 30 秒待っても問題に気づかないことがあります。Vercel では同じ関数が 10〜15 秒でタイムアウトし、ChatGPT はエラーを受け取ります。maxDuration を設定せず、MCP ツールの実行時間を監視していないと、本番で断続的な障害になります。

エラー No.5: MCP の状態をサーバーレス関数のメモリに保持しようとする。
対話履歴や推薦キャッシュを route handler のファイル内のグローバル変数 let cache = {} に置きたくなることがあります。ローカルでは dev サーバーが長く動いていて「動いているように見える」かもしれません。しかし Vercel では各サーバーレス関数は短命で頻繁に作り直されます。結果、あるリクエストは古い cache、別のリクエストは新しい cache、さらに別のリクエストは空の cache を見る、というカオスになります。再現しにくい厄介なバグの温床です。状態には外部 DB や KV ストアを使い、この講義のレベルでは MCP エンドポイントは stateless と考えるのが無難です。

コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION