ブログアプリ技術構成
-今回はテストも兼ねてzennで投稿したものと同じ内容を投稿しましたが、このようなことはなるべく避けていこうと思っています-
Next.jsを学習する中で、一度すべて自分でフルスタックアプリを作ってみたいと思い、それならば今後も使い続けられるブログアプリを作成しようとこのブログアプリを作成しました。
このアプリの目的
このアプリでは、自分の趣味や学習したことを文章で簡単にまとめ、どちらかというと自分のための備忘録として使うために、比較的安価に環境を構築できてかつ今後も継続して運用できそうな Cloudflare 上でフルスタックのNext.jsを使ったブログアプリを作成することにしました。
また、この記事ではあくまで初学者が一通り学習した後のアウトプットとして、何か作る際の参考になれば良いと考えています。なのでこれは完成例の紹介ではなく公開できる最小構成の設計や判断の過程を記録したもので、今後も改良・拡張し続けていくつもりです。
技術スタックと選定理由
先ほど書きましたが、フルスタックNext.jsをCloudflare Workers上で動かす構成です。学習した時のバージョン(15.x)から、今回はNext.jsの比較的新しいバージョンのserver actionをなるべく使う構成にしました。
awsやcloud runも選択肢として考えていたのですが、今回の目的の一つである「比較的安価で済むものを使う」という条件から、Cloudflare を選択しました。
実際に作成してみると、wrangler でデプロイするビルドサイズが想定より大きくなり、有料プラン(Workers Paid)を利用する判断に至りました。
Total Upload: 10984.12 KiB / gzip: 3860.78 KiB //ここがFreeプランの上限を超えた
Worker Startup Time: 23 ms
Your Worker has access to the following bindings:
Free プラン
gzip 後:1 MiB まで
Paid(Workers Paid / Bundled / Unbound)
gzip 後:10 MiB まで
参考: @Limits・Cloudflare Workers docs
この点から、今後さらに機能追加やアップデートを進めてサイズが増大する場合には、hono などを用いてフロントエンドと API を明確に分離する構成へ移行する可能性もあると考えています。
このように今回のブログ作成では、「学習したNext.jsを使用すること」と「インフラ側は本番環境でもある程度使えるサービスのなかで比較的安価で済むものを使う」という二つの軸を重視しました。 これは、学習用として一度作って終わる構成ではなく実際に使い続けながら改善していくことを前提にしたかったためです。そのため、初期段階では完成度を追いすぎず後から作り直しや拡張がしやすい構成であることを優先しました。
-
最小構成として実装した主な機能
- 文章の投稿機能
- 画像配信
- マークダウン方式での記述
- ダークモードライトモードの切り替え
- googleアカウントを使ったログイン機能
-
あえて実装しなかったこと
- アニメーション
- コメント機能
- 追加した画像のアプリ側での削除機能
いずれも学習フェーズでは優先度が低く、まずはブログとして最低限「使い続けられる」構成を確認することを重視しました。ただの実力不足の言い訳では?)
また、これに加えて致命的なミスをしていないか確認してもらうため、ChatGPTに度々コードの評価をしてもらいながら進めました。 たまに、自分でも分かるくらいの記述ミスをしたものを提示してきたり、ハルシネーションを起こしたりしまいたが、あらかじめ分かる範囲でバージョンや構成を詰めたものをファイルで渡しておくとある程度正しく評価してくれるようになりました。 ChatGPTいわく「Next.jsはバージョンによって記述方法が変わることがあり、そのため間違うことがある」とのことでした。
以下に、具体的な技術スタックについて書いていきます。
1. データベース
データベースの選択肢として、Supabase や最近話題になることの多い CockroachDB なども検討しました。ただし、本プロジェクトでは Cloudflare Workers 上でのフルスタック構成を採用しているため、同じエコシステム内で完結でき運用や接続の手間を抑えられる D1 を利用するのが最もシンプルだと判断しました。
主にユーザー情報と投稿情報を管理する役割を担っています。今回の構成では、複雑な分析や大量データの集計を行うことは想定しておらず、あくまでブログとして必要最低限の情報を安定して扱えることを重視しました。ただし、後述しますが本文(Markdown)のみR2で管理しています。
@ネイティブにサーバーレスなSQLデータベースをCloudflare D1で構築
ただし、D1 には後からスキーマ変更を行いにくいという制約があります(D1 が SQLite ベースであるため)。そのため、初期段階からカラム構成を複雑にせずユーザーと投稿という最小限のエンティティに絞った設計にし、さらにアプリでDBを使う機能とそれに必要なテーブルやカラムをあらかじめしっかりChatGPTと詰めてから実装しました(一度作りが甘くてDBを作り直しました)。 将来的に要件が大きく変わった場合には別のDBへ移行することも視野に入れていますが、現時点では学習と運用のバランスを取るため、この割り切りが適切だと考えています。
2. オブジェクトストレージ
基本的な考え方はデータベースと同様で、Cloudflare Workers と同じエコシステム内で完結できる R2 を採用しています。本ブログでは、記事内に表示する画像と本文(Markdown)の保存先として利用しています。
本文をデータベースに直接格納せず R2 に保存した Markdown へ紐づくキーのみをデータベース側で管理する構成にしました。これは、投稿によっては本文が長くなりそのままデータベースに保存すると容量面や取り回しの面で不利になる可能性があると考えたためです。 また、本文データをストレージ側に分離することで、DBの役割を「構造化されたデータの管理」に絞ることができると考えました。
R2 は小規模な利用であればコストを抑えやすく、CDN と親和性が高い点もブログ用途と相性が良いと感じています。一方で、現時点では高度なライフサイクル管理や最適化までは行っておらず、あくまで最小構成として必要十分な使い方に留めています。 今後、画像配信において Cloudflare KV を用いて配信に関する情報をキャッシュし、リクエスト処理を簡略化することで、より高いパフォーマンスを発揮できる構成の実装を考えています。
3. メール送信(招待メール)
本ブログの著者は招待制を採用しており、新規ユーザーを追加する際に招待メールを送信する必要があったため、最小構成でメール送信機能を組み込めるサービスとしてResend を選択しました。
今回の用途はあくまで招待メールの送信に限定しており、通知メールや大量配信といった本格的なメール運用までは想定していません。そのため、テンプレート管理や配信分析などの高度な機能は利用せず「確実にメールを送れること」と「実装や運用がシンプルであること」を重視しました。
4. IaC(Infrastructure as Code)
インフラ構成の管理については、一番はじめにTerraform を用いて環境構築を行いました。dev / stg / prod といった環境差分を意識しながら開発をするという理由と、IaCを使ったことがなかった為試しに使ってみたという理由があったからです。 ただ、最終的に Cloudflare Workers 上でのフルスタック構成にしたため、実際のデプロイや設定管理の多くは wrangler に集約される形になりました。その結果、構成としては「wrangler だけでも十分だったかもしれない」と感じる部分もあります。
しかし、最初に Terraform を使って全体像を整理できたことで構成を理解する助けになりました。初学者ながら、それがIaCの魅力のひとつであるような気がするので学習の観点からTerraformを使う有用性があったのかなと思います。
5. 認証
本ブログでは、主に記事を投稿・管理するユーザーを識別するために認証機能を導入しています。 閲覧自体は誰でも可能とし、ブログを書くことや管理操作のみを認証済みユーザーに限定するシンプルな役割分担です。 認証方式としては、Auth.js(NextAuth)と OAuth を用いた Google アカウントによるログインを採用しました。
当初は、ID とパスワードをデータベースで管理する方式も検討しましたが、認証情報を自前で保持・運用することのリスクや実装コストを考慮し外部 ID プロバイダに委ねる形を選択しました。 昨今、Next.js に限らず Web アプリケーション全般で、認証・認可まわりの実装不備が深刻な脆弱性につながる事例が繰り返し報告されています。特定の脆弱性が直接の理由というよりも、「自分でパスワードを扱う領域を極力減らしたい」という判断が大きく、結果として Google OAuth を利用する構成が、今回の最小構成において最も安全で現実的だと考えました。 招待制により誰でも自由にgoogleアカウントでログインできる状態を避け、権限管理をシンプルに保つことも意識しています。複雑なロール設計や細かな権限制御は行わず、まずは「誰が管理者か」を明確にすることを優先しました。
また、あらためて書くかもしれませんが、Next.jsの認証周りの脆弱性の報告もあるので、定期的なバージョンの確認 & 更新や middleware だけでなくサーバー側処理内で session を検証させたりとできる限りの対策はしているつもりです。
6. スタイル
スタイルには Tailwind CSS を採用しています。コンポーネント単位でスタイルを完結させやすく設計や実装に集中できる点が、今回のような最小構成の個人開発に適していると感じました。 また、ライトモード/ダークモードの切り替えも実装しており、OS の設定やユーザーの選択に応じて表示を切り替えられるようにしています。なお、ダークモード・ライトモード切り替えの具体的な実装方法については記述量が多くなるため、本記事では扱わず別の記事としてあらためてまとめる予定です。
現時点ではアニメーションは実装していませんが、今後追加する場合 CSS Modules や素の CSS を用いたシンプルな表現に留める予定です。まずは見た目を作り込みすぎず、可読性と保守性を優先する方針としています。(デザインにあまり自信がないという点もあります)
7. Markdown
ブログ本文の記述形式としては Markdown を採用しています。 react-markdown と remark-gfm を利用し、表やコードブロックなどを含む GitHub Flavored Markdown(GFM)に対応させています。
Zenn や Qiita などと近い記法で記事を書けることを重視しており、執筆時の負担を減らしつつ記事として読み慣れた表現をそのまま使える点が大きな理由です。実装の詳細については本記事では扱わず別の記事としてあらためてまとめる予定です。
8. CI/CD
CI/CD については、GitHub Actions を利用してgit push をトリガーに Cloudflare Workers へ自動でデプロイされる構成にしています。
ローカルでのビルド確認後、リポジトリへ反映することで、そのまま wrangler を用いたデプロイが実行される流れです。
現時点ではテスト工程までは組み込めておらず、手動作業を減らしデプロイ手順を再現可能にするということを目的としてビルドとデプロイに特化した最小構成の CI/CD に留めています。テストについて、どこまで実装すれば良いのかや現環境での具体的な実装方法について学習中なため、テストやテストの自動化については今後の改善点として検討・実装しています。
今後の課題と拡張余地
- UI/UX の改善
- アニメーションの導入 ただし過度に作り込まず、CSS Modules や素の CSS を用いたシンプルな表現を想定
- コード品質の改善
- 一部に煩雑な記述が残っているため、責務の整理やリファクタリングによる最適化
- テストの導入
- 現状はビルド・デプロイ中心の CI/CD に留まっている 単体テストや最低限の統合テストを追加し、変更時の安心感を高めたい
- ログ・運用面の強化
- エラーログやアクセスログの整理 障害発生時に原因を追いやすい体制づくり
まとめ
今回のブログアプリ開発は、Next.js の学習の延長として「最小構成で、かつ比較的安価に運用できる」ことを重視して進めました。 最初から完成度の高い構成を目指すのではなく、実際に動かし使い続けながら改善できる土台を作ることを目的としています。その過程で、技術選定や設計での割り切りの重要性を実感できたように思います。
本記事が、学習を一通り終えたあとに「何か作ってみたい」と考えている方にとって、一つの判断例や考え方の参考になれば幸いです。