ウェブサイトを公開するまでの全工程

#12018minFeatured

toniolab.comのデプロイ作業を通して、Cloudflare Workers、GitHub Actions、D1データベース、DNS設定、バンドルサイズ制限まで、ウェブサイト公開に必要なインフラ知識を体系的に解説。

#インフラ#デプロイ#技術解説Cloudflare WorkersGitHub ActionsD1CI/CDDNSNext.js

はじめに -- この記事の目的

toniolab.comを公開した。
「コードを書いて、ネットで見れるようにする」
たったこれだけのことに丸2日かかった。
なぜか。コードを書くネットで見れるようにする は、全く別のスキルだから。
プログラミングの解説は無限にある。でも「書いたコードをどうやってインターネットに出すのか」を体系的に解説してる記事は意外と少ない。
この記事は、toniolab.comのデプロイ作業を通じて学んだことを、初心者向けに整理したもの。自分のための勉強ノートでもある。

第1章 -- デプロイとは何か

「ローカル」と「本番」

コードを書いているとき、ブラウザで http://localhost:3000 にアクセスして動作確認する。これはローカル環境。自分のPCの中だけで動いている。
この状態では他の誰もアクセスできない。
デプロイとは、このローカルで動いているアプリケーションを、インターネット上のサーバーに配置して、世界中の誰でもアクセスできるようにすること。

デプロイに必要な3つの要素

  1. コード -- 動くアプリケーション(Next.js、React等)
  2. サーバー -- コードを実行する場所(Vercel、Cloudflare、AWS等)
  3. ドメイン -- アクセスするためのURL(toniolab.com等)
この3つを繋ぐ作業がデプロイ。

第2章 -- サーバーの選択肢

Vercel -- 最も簡単

iwasaki-naisou.comはVercelで動いている。
Vercelの特徴:
  • GitHubにプッシュするだけで自動デプロイ
  • Next.jsの開発元なので相性が完璧
  • 設定ほぼゼロ
  • 無料枠が大きい
欠点: Cloudflare D1のようなエッジデータベースとの統合が面倒。

Cloudflare -- 速いが複雑

toniolab.comはCloudflareで動いている。
Cloudflareの特徴:
  • 世界300以上のエッジロケーション
  • D1データベースがネイティブに使える
  • Workerとして動くのでレスポンスが速い
欠点: 設定が多い。Next.jsを動かすのに変換ツールが必要。

重要な違い

Vercelは「そのまま動く」。Cloudflareは「変換して動かす」。
Next.jsはもともとNode.jsサーバーで動くように作られている。Cloudflare Workersは独自のランタイム(V8 isolate)で動く。環境が違う
だから @opennextjs/cloudflare という変換ツールが必要。これがNext.jsのコードをCloudflare Workerで動く形に変換する。

第3章 -- Cloudflare Workers vs Pages

Cloudflareには2つのデプロイ方式がある。初見では違いがわからない。

Pages -- 静的ファイル配信

HTMLやCSS、JavaScriptの静的ファイルを配信する。SPAやSSG(Static Site Generation)向け。
イメージ: ファイルを置く棚。置いたものがそのまま配られる。

Workers -- サーバーサイド実行

コードがリクエストごとに実行される。SSR(Server-Side Rendering)やAPI処理ができる。
イメージ: 注文を受けて料理を作る厨房。リクエストに応じて動的にレスポンスを生成する。

toniolab.comがWorkersを使う理由

Next.jsはSSRが必要。ページの表示時にサーバーでHTMLを組み立てる処理がある。API(/api/user-phrases等)もサーバーで実行される。
Pagesでは静的ファイルしか配信できないので、SSRやAPIが動かない。
最初はPagesでデプロイしようとして失敗した。 ページは表示されたがAPIが404を返した。WorkersにSSRワーカーがいなかったから。

wrangler.toml -- Workers設定ファイル

name = "toniolab" main = ".open-next/worker.js" compatibility_date = "2024-11-27" compatibility_flags = ["nodejs_compat"] [assets] directory = ".open-next/assets" [[d1_databases]] binding = "DB" database_name = "iwasaki-phrases" database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
各行の意味:
  • name: Worker名。Cloudflareダッシュボードに表示される
  • main: 実行されるJavaScriptファイル。opennextjsが生成する
  • compatibility_flags: Node.jsのAPIを使えるようにするフラグ
  • [assets]: 静的ファイル(CSS、画像等)の置き場所
  • [[d1_databases]]: D1データベースの接続設定

第4章 -- CI/CD -- 自動デプロイの仕組み

CI/CDとは

CI = Continuous Integration(継続的インテグレーション) CD = Continuous Deployment(継続的デプロイ)
簡単に言うと: コードをプッシュしたら自動でビルドしてデプロイする仕組み
手動でやるとこうなる:
  1. コードを書く
  2. npm run build でビルド
  3. ビルド結果をサーバーにアップロード
  4. サーバーを再起動
CI/CDがあると:
  1. コードを書く
  2. git push する
  3. 以上。あとは自動。

GitHub Actions

GitHubが提供するCI/CDサービス。リポジトリに .github/workflows/ ディレクトリを作り、YAMLファイルを置くだけで使える。
name: Deploy to Cloudflare on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 22 cache: npm - run: npm ci - run: npx @opennextjs/cloudflare build - run: npx @opennextjs/cloudflare deploy env: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
上から順に読む:
  1. on: push: branches: [main] -- mainブランチにプッシュされたら実行
  2. runs-on: ubuntu-latest -- GitHubのLinuxサーバーで実行
  3. actions/checkout@v4 -- リポジトリのコードをダウンロード
  4. actions/setup-node@v4 -- Node.js 22をインストール
  5. npm ci -- パッケージをインストール
  6. opennextjs/cloudflare build -- Next.jsをCloudflare用にビルド
  7. opennextjs/cloudflare deploy -- Cloudflareにデプロイ
env にある secrets.CLOUDFLARE_API_TOKEN は、GitHub Settingsで設定する秘密の値。コードに直接書くとセキュリティリスクになるため。

なぜWindowsではなくGitHub Actionsか

opennextjs-cloudflareのビルドツールはLinux前提で作られている。Windowsでは動かない。
GitHub Actionsは ubuntu-latest(Linux)で動くので、Windowsの開発マシンからでもLinux上でビルド・デプロイできる。
これがCI/CDの大きなメリットの一つ: 開発環境とデプロイ環境の違いを吸収できる。

第5章 -- D1データベース

D1とは

CloudflareのエッジSQLデータベース。SQLiteベースで、Workerから直接アクセスできる。
普通のWebアプリ: アプリ → インターネット → データベースサーバー CloudflareのD1: アプリ(Worker) → 同じエッジロケーションのD1
データベースが物理的に近い場所にあるのでレスポンスが速い。

アクセス方法: バインディング vs REST API

バインディング(直接接続): WorkerのコードからD1に直接アクセス。最速。ただしWorker内部からしか使えない。
REST API(HTTP経由): どこからでもHTTPリクエストでD1にアクセス。APIトークンが必要。
toniolab.comはREST APIを使っている。理由: 元々Vercel(iwasaki-naisou.com)からもアクセスする設計だったため。

Worker Secrets -- 秘密情報の管理

データベースにアクセスするにはAPIトークンが必要。これをコードに直接書いてはいけない。GitHubに公開されてしまう。
Worker Secret: Workerの環境変数として安全に保存する仕組み。
echo "YOUR_TOKEN" | npx wrangler secret put CLOUDFLARE_API_TOKEN
コード内では process.env.CLOUDFLARE_API_TOKEN で参照する。

APIトークンの権限

Cloudflare APIトークンには細かい権限設定がある。
今回の失敗: 最初に作ったトークンに Workers の権限しかなく、D1 の権限がなかった。
結果: Workerは動くがデータベースにアクセスできない。APIが「account not authorized」エラーを返す。
教訓: トークン作成時に必要な権限を全て付与する。足りない権限は後から追加できない。新しいトークンを作り直す必要がある。

第6章 -- Workerサイズ制限

無料プランの壁: 3 MiB

Cloudflare Workersの無料プランでは、Workerの圧縮後サイズが 3 MiB(約3.07 MB) に制限されている。
有料プラン($5/月)では 10 MiB まで。

なぜサイズが大きくなるのか

Next.jsアプリをWorkerにすると、全ページのSSRコードが1つの handler.mjs ファイルにバンドルされる。
ページが多い = バンドルが大きい = 制限に引っかかる。
最初のデプロイ: gzip 3,471 KiB → 制限超え 不要ファイル削除後: gzip 3,160 KiB → まだ超え さらに削除後: gzip 2,966 KiB → 制限内

何を削ったか

  1. 世界地図ページ6個: 大きなTopoJSONデータ(739KB)を含む
  2. 業務用コンポーネント170個: 請求書、工程表、顧客管理等。英語学習に不要
  3. 未使用データファイル40個: 料理日記、健康ジャーナル等
  4. プロトタイプページ13個: 古いダッシュボード、レビュー機能等
削除の判断基準: ページから import されていないコンポーネント・データは全てバンドルから除外できる。grepで依存関係を確認してから削除する。

重要な教訓

サイズ制限は無料プランの宿命。対策:
  1. 不要なコードを入れない: 使わないページ・コンポーネントは削除
  2. 大きなデータファイルに注意: JSONの地図データ等は特に重い
  3. 有料プランの検討: $5/月で10 MiBまで。3 MiBがきつい場合は現実的
  4. バンドル分析: どのファイルが大きいかを確認してから最適化する

第7章 -- DNS とカスタムドメイン

ドメインとは

toniolab.com はドメイン名。人間が覚えやすいWebサイトの住所。
実際のサーバーはIPアドレス(例: 104.21.23.45)で特定される。ドメイン名をIPアドレスに変換するのが DNS(Domain Name System)。

ドメインの設定手順

  1. ドメイン購入: Cloudflare Registrar等で取得(年$10程度)
  2. DNSレコード設定: ドメインがどのサーバーを指すか設定
  3. Workers側の設定: このドメインのリクエストを受け付けるよう設定
Cloudflareでは、Workers設定画面の「Custom Domains」からドメインを追加する。DNS設定が自動で行われる。

作業の流れ

toniolab.comの場合:
  1. Cloudflare Registrarでドメイン取得済み
  2. Workers設定 → Routes → Custom Domains → toniolab.com を追加
  3. SSL証明書が自動発行
  4. https://toniolab.com でアクセス可能に

第8章 -- トラブルシューティング実録

今回実際に踏んだエラーとその解決策。

エラー1: Pages デプロイで404

症状: ページは表示されるがAPIが404 原因: PagesモードではSSRワーカーがデプロイされない 解決: PagesからWorkersに切り替え

エラー2: GitHub push が拒否される

症状: .github/workflows/ を含むプッシュが rejected 原因: GitHubのPersonal Access Tokenに workflow スコープがない 解決: GitHubの Settings → Developer settings → Personal access tokens で workflow スコープを追加

エラー3: D1が空のデータを返す

症状: APIは200を返すがフレーズ数が0 原因: Worker SecretにCLOUDFLARE_API_TOKENが設定されていない 解決: wrangler secret put で設定

エラー4: D1アクセスが「account not authorized」

症状: APIが認証エラー 原因: APIトークンにD1の権限がない(Workersの権限のみ) 解決: D1 Edit権限付きの新しいトークンを作成

エラー5: Worker サイズ超過

症状: デプロイ時に「exceeded size limit of 3 MiB」 原因: 全ページが1つのバンドルにまとまり、圧縮後3 MiB超え 解決: 不要なページ・コンポーネント・データを削除して2,966 KiBまで圧縮

エラー6: TypeScript型エラーでビルド失敗

症状: declare global の Window 拡張が conflict 原因: 複数ファイルで同じプロパティのoptional修飾子が不一致(YG: vs YG?:解決: next.config.ts に ignoreBuildErrors: true を追加(iwasaki側と同じ設定)

第9章 -- 全体像の整理

デプロイパイプラインの流れ

ローカルでコードを編集
      ↓
git push origin main
      ↓
GitHub Actions が起動(ubuntu-latest)
      ↓
npm ci(パッケージインストール)
      ↓
opennextjs/cloudflare build(Next.js→Worker変換)
      ↓
opennextjs/cloudflare deploy(Cloudflareにアップロード)
      ↓
Worker更新 → toniolab.com に反映

関係する技術の地図

  • Next.js: アプリケーションフレームワーク
  • opennextjs-cloudflare: Next.js→Cloudflare変換ツール
  • Cloudflare Workers: サーバーレス実行環境
  • Cloudflare D1: エッジSQLデータベース
  • GitHub Actions: CI/CDパイプライン
  • GitHub Secrets: 秘密情報の安全な保管
  • Worker Secrets: ランタイムの環境変数
  • DNS: ドメインとサーバーの紐付け
  • wrangler: Cloudflareの管理CLI

覚えておくべき数字

| 項目 | 値 | |------|-----| | Worker無料サイズ上限 | 3 MiB(gzip後) | | Worker有料サイズ上限 | 10 MiB(gzip後) | | Workers有料プラン | $5/月 | | GitHub Actions無料枠 | 2,000分/月 | | D1無料枠 | 5 GB ストレージ | | ビルド時間(目安) | 約40秒 | | デプロイ時間(目安) | 約15秒 |

おわりに

Vercelなら git push だけで済む作業に、Cloudflareでは2日かかった。
でも2日で学んだこと:
  • Workers/Pagesの違い
  • CI/CDの仕組み
  • データベースの認証と権限
  • バンドルサイズの最適化
  • DNSとドメインの設定
Vercelの「何もしなくても動く」は魔法じゃない。裏で同じことが自動化されてるだけ。
自分でやると面倒。でも何が起きてるか全部わかる。
面倒なことを一度やっておくと、次から何が壊れても直せる。
これがインフラを理解するということ。