チャットアプリを作ってみた
はじめに
最近チャットアプリを作成した(作成途中)ので、その機能や特徴について紹介したいと思います。 実装したリポジトリはこちらです
アプリの概要
このチャットアプリは、簡単に言うとDiscordのようなものです。UIなどもかなりDiscordに似せています。 動画を貼れるのが一番良いのですが、まだ動画貼り付けがこのサイトではできないので画像で紹介します。もし動いているものが見たい場合は、直接サイトにアクセスしてみてください。
主な機能
主な機能として以下です。
- サーバーの作成・参加・招待
- チャンネル、カテゴリの作成
- リアルタイムでのメッセージ送受信
- アイコン画像のアップロード・表示
画像での簡単な紹介です。
下が一番メインとなるチャット画面です。Discordを知っていればどこに何が表示しているかがわかると思います。

左のサイドバーでチャンネルの作成を選択するとこのような画面がでて、チャンネルであったりカテゴリの作成ができます。

サーバーへの招待も行うことができて、期限であったり、使用回数も制限できます。発行した招待リンクを踏むことでサーバーに参加できます。

技術スタック
このアプリは主に以下の技術を使っています
フロントエンド
- TypeScript: 開発言語
- ParkUI: Panda CSSをベースにしたUIライブラリ
- WebSocket: チャットのリアルタイム通信に使用
- Orval: OpenAPIからReactのHooksを生成
バックエンド
- Go: 開発言語
- gRPC Gateway: protoからGatewayのコードを一部生成
- WebSocket: チャットのリアルタイム通信に使用
- PostgreSQL: データベース
- Sqlc: SQLからデータベースを操作するコードを生成
- Protocol Buffers: API定義
- chi: API GatewayのJWT認証用のミドルウェアを定義する際に使用
基本的にバックエンドを力を入れて開発していたのでバックエンド多めです。
その他
- Grafana: メトリクスの可視化
- Prometheus: メトリクスの収集
- Tempo: トレーシングツール
- Loki: ログ収集ツール
- Alloy: メトリクスを監視するツール。データの保存はしない。
忘れているだけで、他にもいろいろ使っていると思いますが、基本的なスタックはこんな感じです。
アーキテクチャ
アーキテクチャ図を書いてみたかったので一通り現状を書いてみました。図が複雑になりそうだったので書いていませんが、各サービス間はgRPCで通信しています。また一部はRedisのキャッシュを効かせていたりしています。

各サービスでやっていることを簡単に紹介します。
アプリケーション
- API Gateway: JWTの検証を行ったり、HTTPリクエストを受け付けて、各サービスにgRPCでリクエストを送信します。
- User Service: ユーザー登録・管理、JWTの発行を行っています。
- Guild Service: サーバーの作成、メンバの管理、チャンネル・カテゴリの管理、招待リンクの発行などを行っています。
- Message Service: メッセージの作成などを行っています。作成したメッセージはRedisにPublishするだけで、リアルタイムにはここでは配信しません。RestAPIでのメッセージの取得などはここで行えます。
- Realtime Service: Message ServiceからのメッセージをSubscribeし、WebSocketでクライアントに配信します。
- Media Service: 署名付きURLの発行などを行います。配信自体はMinioなどのオブジェクトストレージ自体で行うので、現状だとすることは署名のみです。時間があれば画像のリサイズなどもできるといいなと思っています。
モニタリング
ちょっとまえにMinecraftサーバーをGrafanaでモニタリングするのをやって、そのときにかなりダッシュボードは気に入っていたので、今回作ったアプリでもやってみたいということで導入しました。 特にk6などの負荷テストで遊んでいるときに、各サービスの推移が見れて楽しかったです。一番監視しててびっくりしたのが、User Serviceが想像以上にCPUを使っていることです。いろいろ原因を探していると、JWTの生成にかなりCPUを使っていたのが原因で、負荷テストをしてみて初めて知ることも多く面白かったです。
- Prometheus: 各サービスやDB、Redisなどからメトリクスを収集します。
- Alloy: こちらもメトリクスの収集ですが、TempoやLokiなどに渡すようのログやトレーシング情報を収集します。
- Tempo: トレーシング情報を保存します。
- Loki: 各サービスのログを保存します。
- Grafana: 上記で収集した情報の可視化を行います。ダッシュボードがかっこいいです。
CD
現状だとCloudflare Tunnels経由で自宅サーバーにSSHして、そこでComposeを立ち上げるようにしています。はじめてCDを構築したので結構苦労しました。
大変だったこと
一番大変だったのはやはり初めのバックエンドの設計でした。Goでの開発もそこまで経験もなく、gRPCを使ったマイクロサービスの設計も初めてだったので、どのレベルでサービスを切り分けるか、DBはサービスごとに分けるかなど、いろいろ悩んだ記憶があります。そして、フレームワークやライブラリの選定などもなかなか迷うことが多く、決まらないときには最終的には自分の思想というか、直感を信じて選定しました。特にORMを使うか迷ったのですが、最終的にはSqlcというライブラリを使うことにしました。型安全にDBを操作して、かつボイラーテンプレートコードを自動生成できてかなり開発体験がよかったです。
また、WebSocketでの認証の方式もどうしようか迷いました。URLにJWTを付与して送信する方法などはよくありますが、URLにできればトークンは含めたくないと思っていたので、なにかいい方法がないかなといろいろ探した記憶があります。WebSocketの初回接続時のHTTPのヘッダーにはAutorizationヘッダーを含めることができないので、最終的にはWebSocket接続確立時の初回メッセージでJWTを送信して、一定時間以内に認証が成功しなければ接続を切断するという方法を取りました。
そしてかなり長いこと開発をしているのでモチベの維持も大変でした。開発の腕が走り始めると楽しくてずっとできるのですが、初めて触る技術などではあまり開発が進まないことも多く、途中でモチベが下がってしまい、しばらく開発が止まってしまう時期などもありました。もともと夏休み中にMVPは完成させたいと思っていて、なんとかそれまでに最低限は完成して、形となって動いたことでかなりモチベが復活したのがよかったと思っています。
今後やりたいこと
現状はDocker Coomposeですべて起動していますが、Kubernetesで動かせたらいいなと思っています。また、CDも現状はただActionsでビルドして自宅サーバーにSSHし、そこでイメージをPullしてdocker compose upしているだけですが、ArgoCDなど本格的なGitOpsでの運用もやってみたいと思っています。
また、最近だとバックエンドではRustの人気が高くなってきているのかなと思っているので、試しにどこかのサービスをRustで書き直して負荷テストして結果を比較したりしてみるのも面白いかなとか思ったりしています。
追記
無事Kubernetes上にデプロイし、ArgoCDでのGitOpsも導入しました。また記事にまとめたいと思います。
おわりに
以上、簡単な紹介でした。今回はこのチャットアプリを紹介するページが急遽ほしくなったので簡単に書いていますが、ほんとはもっと詳しく紹介したいので今後時間があるときにポイントごとに分けて記事を書きたいと思っています。