バックエンド開発者にとって「デプロイ」とは、通常サーバーを構築することです。EC2インスタンスを立ち上げ、Dockerコンテナを動かし、nginxを設定する。それが私の知っているデプロイでした。でもQ-Fitを作りながら、初めて静的ホスティングというものを試しました。そしてデプロイしてみて初めて見えてくるものが、思ったよりずっとたくさんありました。
どこにデプロイするか:Geminiに状況を説明してみた
最初はサーバーを借りて上げればいいんじゃないかと思っていました。AWSに上げればいいと思っていたんですが、Geminiに話したら、静的サイトはサーバーなしでもデプロイできると言われました。ビルドするとHTML、CSS、JSファイルだけが出てくる構造なので、そのファイルをそのままCDNに上げてしまえばいいということでした。24時間サーバーを動かす必要がないのでコストがほとんどかからないことも、初めて知りました。
そこで候補をいくつか教えてくれました。Vercel、Netlify、Cloudflare Pages。最初はVercelが便利だと勧めてくれました。でもQ-Fitが商業目的で、ある程度トラフィックが来る可能性があることを説明したら、無料プランの商業利用制限とトラフィック上限の問題があると言われました。そこでCloudflare Pagesを代替案として提示してくれました。商業利用の制限がなく、無料プランでもトラフィック制限がないというのが理由でした。それでCloudflare Pagesに決めました。
初めてのデプロイ:Pagesメニューが見つからなかった
Cloudflareのダッシュボードに入ると、メニューがたくさんありました。Workers、R2、D1、KVといった名前が並んでいました。でもPagesがどこにあるのかしばらく見つかりませんでした。Workersの中にあるのか、それとも別にあるのか。結局スクリーンショットを撮ってGeminiに送りながら「この画面でPagesはどこをクリックするんですか?」と聞きました。
Geminiが画面を見て、どこをクリックすればいいか教えてくれました。どんな値を入力すればいいかも全部案内してもらいました。ビルドコマンド、出力ディレクトリまで。何をなぜ入力するのか完全に理解したわけではありませんでしたが、とにかく言われた通りに入力しました。そうしたら最初のデプロイが成功しました。思ったより難しくありませんでした。
画像の読み込みが遅すぎた
デプロイ後にサイトを開いてみると、画像の読み込みが目に見えて遅かったです。publicフォルダにPNGファイルをそのまま入れていたのですが、結構な容量がありました。ローカルではどうせすぐに読み込まれるので気づかなかったのですが、実際のデプロイ環境では差がはっきりしていました。心理テストの結果画像などが特に目立ちました。
Geminiに話したら、WebPに変換するように言われました。同じ画像なのにPNGよりファイルサイズがずっと小さくなるということで。変換コードも書いてくれました。画像を全部WebPに変えたら、体感できる読み込み速度が確かに変わりました。画像最適化というものをこの時初めて実感しました。ローカルでだけ開発していて、初めてデプロイ環境に触れると、こういうことが目に見えてくるんだなと思いました。
カカオトークで共有してみたら毎回メインに飛んだ
デプロイしてから、カカオトークで特定の心理テストのリンクを共有してみました。ところがリンクをクリックしても毎回メインページに飛んでしまうんです。送りたかったテストではなく、ホームに来てしまう状況でした。なぜかとGeminiに聞いたら、URLがページごとに変わるようにルーティング処理がされていないからだと言われました。
Geminiがルーティング処理をしてくれましたが、この過程でSPA特有のリフレッシュ404問題も同時に解決されました。SPAはページをクライアントサイドでレンダリングするため、特定のURLに直接アクセスしたりリフレッシュしたりすると、サーバーがそのパスを認識できずに404を返すということでした。静的ホスティング環境でこの問題を処理する方法もあると言うので、一緒に適用しました。SPAが何かは知っていましたが、静的デプロイでこういう問題が起きうるとは、実際に経験するまで知りませんでした。
カカオトークの共有プレビューに何も表示されなかった
ルーティングの問題を解決してから、再びカカオトークで共有してみました。今度はリンクをクリックすると正しく該当テストページに移動しました。でも共有するときにプレビューがありませんでした。他のサービスのリンクを共有すると、タイトルと画像が表示されるのに、Q-FitのリンクはただのURLだけが出てきました。
Geminiに理由を聞いたら、OGタグがないからと言われました。Open Graphタグというもので、リンクを共有するときにプレビュー画像やタイトル、説明が表示されるよう、HTMLにメタ情報を埋め込む方式だと説明してくれました。そして各ページごとに動的にOGタグを生成する必要があるが、静的サイトなのでプリレンダリングも必要だと言われました。SEOという概念もこの時初めてちゃんと聞きました。検索エンジンがページをどのように認識するか、コンテンツにどんなメタ情報が必要か。デプロイを通じて、自然にSEOの必要性を実感した形でした。
存在しないURLにアクセスすると画面が崩れた
ある日、存在しないURLにアクセスした時に画面がそのまま崩れるのを発見しました。何もない白い画面だったり、レイアウトが半分崩れた状態で表示されたりしました。404ページを別途作っておかなかったんですね。サーバーベースのデプロイをする時はnginxの設定で404処理を別にしていましたが、静的デプロイではそれがないので、ただ空白の画面が出てしまっていました。
404ページを作って解決しました。間違ったURLにアクセスした場合、「ページが見つかりません」という案内と一緒にメインに戻れるボタンを置きました。Cloudflare Pagesはルートに`404.html`ファイルを置くと自動的に処理してくれると、Geminiが教えてくれました。簡単に解決しましたが、これも直接経験するまでは考えていなかった部分でした。
デプロイ後にはじめて見えるもの
振り返ってみると、これらの問題はすべてローカルでは発見しにくいものでした。画像はローカルで素早く読み込まれ、カカオトーク共有はデプロイ前には試せず、OGタグのプレビューも実際のURLがあって初めて確認でき、404処理も間違ったURLにアクセスしてみるまでは顕在化しません。全部デプロイしてから初めてわかったことでした。
バックエンドでAPIを作る時は、ローカルでも大部分を再現できました。でも静的フロントエンドのデプロイは違いました。「実際にユーザーが使う環境」がローカルとかなり違う部分があって、その差が問題として顕れました。サーバーなしでデプロイするということが、単に「上げれば終わり」ではなく、その環境に合わせて気を使わなければならないことが別にあることがわかりました。
おわりに
静的ホスティングは確かにサーバーデプロイより参入障壁が低いです。設定も少なく、コストもほぼなく、自動化もよくできています。でも「サーバーがない」ということは「気にすることがない」という意味ではありませんでした。むしろデプロイ環境でしか顕在化しない問題があって、それを一つずつ直面しながら、静的デプロイがどのように動作するかを少しずつ理解していきました。バックエンドしかやってこなかった人間がフロントエンドのデプロイを初めてやりながら学んだことがかなりありました。次に似たようなプロジェクトをやる時は、今回よりは迷子にならないと思います。