こんにちは!
今回は、Webアプリの開発をメインで行っているエンジニアである私が、前々から興味があったモバイルアプリ開発に、Google Cloudを使って(普段はAWSがメイン)挑戦してみようと思います。これはあまり経験のない領域に対して、学習を主目的としたプロジェクトなので、細かい部分でツッコミどころは大糸思いますが、温かい目で見守っていただけると幸いです。まずは動くものを作って、Google CloudとFlutterに慣れるのが一番の目標!なので、完璧な設計よりもDone is better than perfectの精神でいきます。)
この記事では、開発に着手する前の「技術検討」の過程を資料としてまとめてみました。特に、個人開発で最も気になるコスト面を重点的に掘り下げています。あまりクラウドやモバイルの開発の経験が少ない方はもちろん、同じように個人開発でクラウド費用に不安に感じている方の参考になれば嬉しいです。
1. プロジェクトの概要とゴール
- 概要: Google Cloud+Firebaseを使って、iOS/Androidで動くシンプルなチャットアプリを開発します。
- 目的: ユーザー間のリアルタイムなコミュニケーションを可能にする、安定したチャットアプリを提供すること。(まずは1対1チャットがちゃんと動けば大成功としたい。)
-
ゴール:
- 作るものの要件を整理する。
- 技術スタックとアーキテクチャを決定する。
- コストを意識した基本設計の合意を自分の中で形成する。
2. 要件定義
まずは、どんなアプリを作りたいのか、機能と非機能に分けて整理します。
2.1. 機能要件
機能名 | 概要 | 優先度 (High/Mid/Low) | 備考 |
---|---|---|---|
ユーザー管理 | |||
ユーザー登録・退会 | メールアドレスやSNSアカウントによる新規登録、退会機能 | High | Firebase Authenticationを使えば楽に実装できそう。これはマスト。 |
プロフィール設定 | ユーザー名、アイコン画像、自己紹介文の設定 | High | 最低限、名前とアイコンは欲しい。 |
チャット機能 | |||
1対1チャット | 特定のユーザーと1対1でメッセージを送受信する機能 | High | アプリの核となる機能。まずはここから。 |
グループチャット | 複数人でのグループを作成し、メッセージを送受信する機能 | High | これもチャットアプリなら必須級。 |
メッセージ送信 | テキストメッセージの送受信 | High | 当然の機能。 |
画像・動画送信 | 画像や動画ファイルをアップロードし、送信する機能 | Mid | これはコストに直結するので慎重に。まずは画像からかな。 |
既読機能 | 相手がメッセージを読んだことを示す機能 | Mid | あると嬉しいけど、最初はなくても良いかも。実装コストも考慮。 |
その他 | |||
プッシュ通知 | 新着メッセージをリアルタイムで通知する機能 | High | Firebase Cloud Messaging(FCM)の出番。これがないとチャットアプリとして成り立たない。 |
友達リスト | 友達を追加・一覧表示する機能 | High | 誰とチャットするか選ぶために必要。 |
ユーザー検索 | 他のユーザーを検索する機能 | Mid | 後からでも追加できる機能。まずは友達追加から。 |
2.2. 非機能要件
項目 | 要件レベル | 備考 |
---|---|---|
パフォーマンス | メッセージ送受信の遅延: 500ms以内 | リアルタイム性を損なわない現実的な目標値。 |
可用性 | サービス稼働率: 99.9%以上 | Google Cloudのマネージドサービスを使えば、この目標は達成しやすいはず。 |
スケーラビリティ | 想定ユーザー数: 初期1000MAU | まずは友達に使ってもらうレベルから。サーバーレスなら急なスパイクにもある程度強いはず。 |
セキュリティ | 通信は全てTLSで暗号化 | Firebaseを使えばデフォルトでやってくれるので安心。 |
メッセージデータや個人情報は暗号化して保存 | Firestoreのサーバー側暗号化に期待。 | |
運用・保守 | ログ収集・監視体制の確立 | Cloud Logging, Cloud Monitoringを利用。何かあった時に追えるように。 |
コスト | 可能な限り低く抑える | 最重要項目。無料枠を使い倒し、有料サービスも低コストなものを選択する。 |
3. アーキテクチャ検討
ここが今回の技術選定の要です。フロントエンド、バックエンド、インフラの各層で、なぜその技術を選ぶのかを比較検討しながら明確にしていきます。
3.1. インフラアーキテクチャ (Google Cloud)
バックエンドを動かすためのGoogle Cloudのインフラをどう構成するかを比較検討します。
比較項目 | A案: フルサーバーレス (Cloud Functions) | B案: GKE (Google Kubernetes Engine) |
---|---|---|
運用負荷 | 低 | 高 |
コスト(低トラフィック時) | 安価 | 比較的高価 |
コスト(高トラフィック時) | 従量課金で増加 | 予測しやすい |
スケーラビリティ | 非常に高い(自動) | 高い(設定が必要) |
開発の複雑さ | 低(初期) | 高 |
-
選定結果: A案 フルサーバーレスアーキテクチャ
-
選定理由:
今回のプロジェクトは学習目的の個人開発であり、「コストの最小化」と「開発への集中」が最優先事項です。この観点から、フルサーバーレスアーキテクチャが圧倒的に有利と判断しました。- コスト効率: B案のGKEは、トラフィックがゼロでもクラスターを維持するためのノード費用が常に発生します。一方、A案のCloud Functionsはリクエストがなければ費用はほぼゼロです。ユーザー数が不確定な初期段階において、この差は致命的です。(本音: 使わないときにお金がかからない、というのは個人開発者にとって最高の響きです。)
- 運用負荷の低減: B案のGKEは非常に高機能で柔軟ですが、Kubernetesの学習コストやクラスターのバージョンアップ、セキュリティ設定などの運用管理は個人で行うには負担が大きすぎます。A案であれば、インフラの管理をGoogle Cloudに任せ、自分はアプリケーションロジックの実装に集中できます。
- スケーラビリティ: チャットアプリは、特定の時間帯にアクセスが集中する可能性があります。サーバーレスアーキテクチャは、こうした急な負荷変動に対して自動でスケールしてくれるため、機会損失を防ぎやすいというメリットもあります。
以上の理由から、開発速度とコスト効率を最大化できるA案: フルサーバーレスアーキテクチャを選定します。
3.2. ソフトウェアアーキテクチャ
3.2.1. サーバーサイドアーキテクチャ (Cloud Functions)
サーバーレスを選んだ上で、次に「どのように機能を実装していくか」を考えます。ここでは、伝統的なモノリシックとマイクロサービスの考え方を、Cloud Functionsに当てはめて比較します。
比較項目 | A案: マイクロサービス的アプローチ | B案: モノリシック的アプローチ |
---|---|---|
概要 | 機能単位(ユーザー登録、メッセージ送信等)でFunctionを分割 | 巨大な単一Functionに多くのロジックを実装 |
開発スピード(長期) | 速 | 遅 |
スケーラビリティ | 高 | 低 |
運用複雑性 | 高 | 低 |
耐障害性 | 高 | 低 |
-
選定結果: A案 マイクロサービス的アプローチ
-
選定理由:
- 関心の分離と保守性: B案のように一つのFunctionに全てのAPIロジックを詰め込むと、コードが密結合になり、少しの修正が全体に影響を及ぼす「スパゲッティコード」に陥りがちです。A案のように「ユーザー登録」「プロフィール更新」など機能ごとにFunctionを小さく分割することで、各Functionの役割が明確になり、改修やデバッグが容易になります。
- スケーラビリティとリソース最適化: 各Functionは独立してスケールします。例えば「メッセージ送信」は頻繁に呼ばれるが、「ユーザー退会」はそうでもない、といった場合に、それぞれの特性に合わせてメモリ割り当てなどのリソースを最適化できます。
- 耐障害性: 万が一、あるFunctionにバグがあって停止しても、影響はその機能だけに限定されます。アプリ全体がダウンするリスクを低減できます。
(本音: とは言え、個人開発で最初から過度に細かく分割しすぎると管理が煩雑になります。なので、「ドメイン駆動設計」の考え方を参考に、まずは
User
、Chat
といった大きな単位で分割を始め、必要に応じて徐々に細かくしていく、という現実的な進め方をしようと思います。)
3.2.2. クライアントサイドアーキテクチャ (iOS/Android)
最後に、ユーザーが直接触れるモバイルアプリ部分の開発手法を検討します。
比較項目 | A案: ネイティブ (Swift/Kotlin) | B案: クロスプラットフォーム(Flutter) |
---|---|---|
開発コスト・期間 | 高 | 低 |
パフォーマンス | 高 | 中〜高 |
UI/UXの自由度 | 高(プラットフォーム依存) | 高(独自UI) |
保守性 | 低(コードが2つ) | 高(コードが1つ) |
-
選定結果: B案 クロスプラットフォーム(Flutter)
-
選定理由:
- 圧倒的な開発効率: このプロジェクトは私一人の個人開発です。A案のネイティブ開発では、iOS(Swift)とAndroid(Kotlin)の2つの言語とフレームワークを学び、2つのコードを書き、メンテナンスする必要があります。これは現実的ではありません。B案のFlutterであれば、単一のコードベースから両方のOSのアプリをビルドできるため、開発工数を半分近くに削減できます。
- 十分なパフォーマンス: かつてクロスプラットフォームは「ネイティブより遅い」と言われましたが、Flutterのパフォーマンスは非常に高く、チャットアプリのような一般的なアプリケーションにおいて、ユーザーが体感できるほどの性能差はほぼありません。(実際問題、各面でネイティブ構成には劣ると思いますが、そもそもそこまでの機能性や性能を求めていことに加え、知識面的に作業量的にも難しいので妥協したという側面が強い認識です。)
- 一貫したUI/UX: Flutterは独自のレンダリングエンジンを持つため、OSのバージョンに依存せず、iOSでもAndroidでも一貫した美しいUIを提供できます。
迅速な開発とリリース、そしてメンテナンス性の高さを最優先し、B案: クロスプラットフォーム(Flutter) を選定します。
4. コスト最適化戦略(最重要)
個人開発を継続するためには、コスト管理が生命線です。Google Cloudの各サービスについて、コストを抑えるための具体的な戦略を立てます。
4.1. Firestore
Firestoreの料金は主に「読み取り、書き込み、削除のオペレーション数」「ストレージ容量」「ネットワーク帯域」で決まります。特にオペレーション数がコストに直結するため、ここをいかに減らすかが鍵です。
-
データモデルの工夫(非正規化の活用):
チャットルームのリストを表示する際に、最新メッセージと未読数を表示したいとします。正規化された設計だと「全チャットルーム取得」→「各チャットルームの最新メッセージを取得」というように大量の読み取りが発生します。
これを避けるため、chat_rooms
コレクションの各ドキュメントにlast_message
やunread_count
といったフィールドを持たせる「非正規化」を行います。メッセージが投稿されるたびにこのフィールドを更新する書き込みコストは増えますが、一覧表示時の読み取りコストを劇的に削減できます。(本音: 書き込みより読み取りのほうが圧倒的に多くなるはずなので、読み取り回数を減らす設計が正義。) -
リアルタイムリスナーの適切な管理:
Firestoreの強力な機能であるリアルタイムリスナーは、常に接続しているとコストがかさむ可能性があります。画面を離れたら必ずリスナーをデタッチ(購読解除)する処理を徹底します。 -
サブコレクションの活用:
チャットのメッセージのように、際限なく増えていくデータはサブコレクションに格納するのが定石です。 例えば、chat_rooms/{roomId}/messages/{messageId}
のような構造にすることで、特定のチャットルームのメッセージだけを効率的に取得できます。 -
セキュリティルールでの防御:
意図しないデータの読み取りを防ぐため、Firestoreのセキュリティルールをしっかり設定します。これにより、必要なデータのみを取得させ、無駄なオペレーションを防ぎます。
4.2. Cloud Functions
Cloud Functionsの料金は「呼び出し回数」「コンピューティング時間(GB秒)」「ネットワーク」で決まります。
-
リージョンの選択:
ユーザーに最も近いリージョンを選択することで、ネットワークのレイテンシを減らし、関数の実行時間を短縮します。これはパフォーマンス向上とコスト削減の両方に繋がります。 -
メモリサイズの最適化:
関数には必要十分なメモリを割り当てます。過剰なメモリはコストの無駄になります。まずは最小限で試し、ログを見ながら調整していくのが良いでしょう。 -
処理の軽量化:
重たい処理(画像の圧縮など)は、可能であればクライアントサイド(Flutterアプリ側)で行い、Functionsの処理時間を短く保ちます。
4.3. Cloud Storage
Cloud Storageは「ストレージ容量」「オペレーション回数」「ネットワーク帯域」で課金されます。 画像や動画を扱う上で非常に重要です。
-
クライアントサイドでの画像圧縮:
これが最も効果的です。ユーザーが画像をアップロードする際に、Flutter側で画像を適切なサイズにリサイズ・圧縮してからCloud Storageに送信します。これにより、ストレージ容量とアップロード時のネットワーク帯域を大幅に節約できます。 -
オブジェクトのライフサイクル管理:
例えば、ユーザーが退会したら関連する画像を自動で削除したり、古い画像を低コストなストレージクラス(NearlineやColdline)に移動させたりするルールを設定します。これにより、不要なストレージコストを削減できます。
4.4. 全体を通して
-
無料枠を最大限活用:
Google CloudとFirebaseには寛大な無料枠が用意されています。 初期段階では、この無料枠の範囲内で運用することを目指します。 -
予算アラートの設定は必須!:
想定外のコストが発生して青ざめる...という事態を防ぐために、Cloud Billingで必ず「予算アラート」を設定しておきましょう。 設定した予算を超えそうになったらメールで通知してくれます。(これだけは絶対に忘れてはいけない。お守りです。)
5. 設計
ここまでの検討を踏まえた、基本的な設計案です。
5.1. 構成
5.2. 方針
-
API設計方針:
Cloud Functionsをエンドポイントとして、シンプルなREST APIを構築します。各Functionは、ユーザー作成、メッセージ送信などの単一責任を持つようにします。 -
データベーススキーマ概要 (Firestore):
-
users/{userId}
: プロフィール情報(名前、アイコンURLなど) -
chat_rooms/{roomId}
: チャットルーム情報(参加メンバー、最終メッセージなど) -
chat_rooms/{roomId}/messages/{messageId}
: チャットメッセージ本体(送信者ID, テキスト, タイムスタンプなど)
-
-
クライアントアーキテクチャ (Flutter):
- 状態管理: Riverpodを採用します。 Providerの後継であり、よりテストしやすく、依存関係の注入がシンプルになる点が魅力です。 (今から始めるならモダンなRiverpodを学んでおきたい、という気持ちもあります。)
6. まとめ
今回は、Google CloudとFlutterを用いたチャットアプリ開発の技術検討、特にアーキテクチャ選定とコスト最適化に焦点を当ててまとめてみました。比較検討を通して、今回のプロジェクトの目的である「低コストでの学習と迅速な開発」に最も適した構成として、Flutter + フルサーバーレス (Cloud Functions / Firestore) を選定しました。
もちろん、実際に開発を進めていく中で、予期せぬ問題や新たな発見がきっとあるはずです。(きっとどこかで詰まるし、設計通りにいかない部分も出てくると思いますが、それも含めて学習!)
これから実際に手を動かして(生成AIを使用しつつ)開発を進めていきます。進捗はまたQiitaで報告できればと思いますので、応援していただけると嬉しいです!