地震速報・監視アプリケーション EQMonitor をアプリストアでstableリリースしました

[防災アプリ開発 Advent Calendar 2023] 1日目

はじめまして、もぐもぐ [@YumNumm] です。 よろしくお願いします。

:::note warn
公開当日に急いで書いたので、わかりにくい文章・誤字・脱字等 **ありますが** お許しください
:::


[防災アプリ開発 Advent Calendar 2023]: https://adventar.org/calendars/9301
[@YumNumm]: https://twitter.com/YumNumm


**本日、2023年12月1日に 地震速報・監視アプリケーション EQMonitorをアプリストアでリリース**しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489005/191f3dcf-96c0-4ccf-af2f-51f7daf4da19.png)

![iPad - 1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489005/2c78fcdb-0e8d-534c-2bfc-525bc8bbc954.png)

![iPad - 2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489005/9605015f-928f-eede-e857-107bc7dc6708.png)




:::note alert
リリースしました! と書きましたが、AppStoreでは公開済み・PlayStoreは現在審査待ちです
:::

**AppStoreではこちらで公開しています**

https://apps.apple.com/us/app/eqmonitor-%E5%9C%B0%E9%9C%87%E9%80%9F%E5%A0%B1/id6447546703


**アプリケーション本体のソースコードも公開しているので 興味がありましたらご覧ください**

https://github.com/YumNumm/EQMonitor

## 0. EQMonitorを開発するモチベーションと想い

### リリースまでのお話

このアプリケーションをリリースするまでの道のりは非常に長いものでした。
**私が、このアプリケーションを開発し始めたのは、2022年2月(当時高校2年冬)です。**

2022年3月に開発を始めてから、7月頃の大型リファクタリング(コードをすべて消して組み直しました)を経て、2022年の11月にはアプリ甲子園に出場し、第3位+技術賞を受賞しました。
去年の[防災アプリ Advent Calendar 2022] 18日目には 「スマホ向け地震観測・速報アプリを開発しているお話」という記事を投稿しました。

https://qiita.com/YumNumm/items/ef71e6f5c98b6554028f

[防災アプリ Advent Calendar 2022]: https://adventar.org/calendars/7488

**このアプリケーションの開発で人生が変わったと言っても過言ではありません。**


当時、アプリ開発のアの字も、FlutterのFの字もわからないようなへっぽこでした。そこから、このアプリケーションの開発を通じて、Flutterの使い方やバックエンド・インフラの運用を学んで来ました。
こうして得た技術力は、現在エンジニアとして勤務する中でも大変役に立っていますし、このアプリケーションを作っていなかったら、高校卒業後エンジニアとして勤務できていなかったと思います。

https://qiita.com/YumNumm/items/fa6e7eec714eed0f0309

これからも、地震・災害関連の開発を続けていき、便利な情報を提供しつつ、様々な知見を得ていきたいと思います。


### どんなモチベで個人開発と向き合っているのか

第一に、**自分がこのアプリケーションを使いたいから**です。

もともと、[JQuake](https://jquake.net/)というデスクトップアプリケーションをモバイルでも使いたい という思いから開発を始めました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489005/4775ad43-6353-9986-661d-6caba245115d.png)

第一な理由がコレというだけであって、「個人開発をしてみたい」、「技術力向上への道しるべになれば良い」、「サーバ運用費くらいカバーできれば嬉しい」といった思いもあります。
(とはいえ、メンタルが死んでいる時には、「なんで開発しているんだっけ?」になりがちなので、強固な軸が欲しいものです)

今も、これからも、広告や課金による機能制限を設けるつもりはありません。
(ユーザが増えて、サーバが厳しくなってきたり、よりお金のかかる機能を提供したくなったら入れるかもしれませんが)

## 1. 技術スタック
### アプリケーション側

**Flutterを主に利用しています。** 
後で詳しく書きますが、一部MapLibreを利用して地図描画を行っています。

Flutterを選定した理由としては、開発を始めた当時Flutterに興味があったから というのもありますが、とりあえずiOS,Androidでリリースしてネイティブ実装が必要になってきたら 段階的に移行すれば良いかな〜 のお気持ちでした。
わりとツラミもあったので、こちらも後で触れます。

### サーバサイド

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489005/c6facea0-033b-9ae3-e6d3-3a0216941d60.png)
雑に構成図作っていたらすごいことになってしまいました。
順を追って見ていきます

#### 緊急地震速報・地震情報の受信

気象庁によって発表された地震情報は、気象業務支援センターを通じて、配信事業者に届けられます。
配信事業者になるためには、毎月5~6桁オーダーの金額を支払う必要があり、個人開発向きではありませんでした。

そこで、この地震情報をWebSocket・REST APIにて再配信している [DMDATA.JP (Project DM-D.S.S)](https://dmdata.jp/) を利用しています。月3000円程度で、リアルタイムに地震情報を取得できるため非常に便利です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489005/33eb845a-8d05-ef32-35e4-6cb006a12ddf.png)

#### 緊急地震速報・地震情報の処理

DMDATAを通じて取得した緊急地震速報と地震情報を処理していきます。具体的には以下のことをやっています。

- **DMDATAで取得したJSONデータの変換**
  重複している部分や、不要な部分を削除し配信しています。
  3~5割程度削減できています。(左→右)
  (現状JSONで配信していますが、今後はProtocol BufferとgRPCに移行していこうと思います)
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489005/c9108418-63a3-4ebd-d8dc-e8391ec5fae4.png)

- **Cloudflare D1 (SQLite3) へデータの保管**
  後で詳しく触れますが、Cloudflareのサービスである D1 へデータを送信します
- **Firebase Cloud Messaging でユーザへ情報配信**
  

#### REST API 

Cloudflare Workers, D1, KVで構成されています。

Cloudflareのエッジ環境で動く V8エンジン Cloudflare Workers を利用しています。
低レイテンシー・低価格で配信を行うことができ、非常に便利です。

Cloudflare Workersに付随して以下のサービスを利用しています。

- `Cloudflare D1`: エッジ環境で動作する SQLite3 データベース
  - 地震履歴の保管・クエリ実行
  - お知らせの保持・クエリ実行
- `Cloudflare KV`: 低遅延のKey-Valueストア
  - アップデート情報の保持・提供
- `Cloudflare Durable Objects`: 複数クライアントで同時に接続できるグローバルなメモリ状態永続オブジェクト
  - 地震情報配信WebSocketサーバのバックアップ (開発者のみの利用)

このように、REST APIはサーバレスで動作しています。
Cloudflareのサービス郡は、お財布にも優しく、
- [Advanced Certificate Manager](https://www.cloudflare.com/ja-jp/advanced-certificate-manager/) $10/month: `hoge.hoge.huge.com`といった複雑なドメインにおける SSL証明書の管理
- [Cloudflare Workers Paid](https://developers.cloudflare.com/workers/platform/pricing/) $5/month: 1000万req/month まで無料 (無料プランの無料枠は10万req/day), 3000万 ms/monthのCPU処理時間

ですべてのサービスを動かせています。

<details>
<summary>
Cloudflareのサービスをチョット紹介
</summary>

### [Workers](https://workers.cloudflare.com/)

Cloudflare Workers は、Cloudflare が提供するサーバーレスの実行環境です。
Cloudflareが有する、グローバルに展開されたエッジ実行環境でJavaScriptを実行することができます。
AWSの[Lambda@Edge], GCPの[Cloud Run]と似たようなコンセプトのサービスです。

Cloudflareの様々な開発者向けサービスをバインドして操作することができます。
Next.jsのSSG/SSRもWorkers×Pages(後述)で実現できます。

### [D1(Open Alpha)](https://developers.cloudflare.com/d1/)
Cloudflareのサーバレスデータベースです。
SQLite3がベースになっており、Workers経由でCRUD処理を行うことができます。

### [R2](https://developers.cloudflare.com/r2/)
[AWS S3](https://aws.amazon.com/jp/s3/)API 互換のオブジェクトストレージサービス
値段設定もS3に比較して安価な上に、リージョンの設定は不要で自動で最適化されます。
オブジェクトストレージの信頼性は<ruby>`99.999999999%`<rt>イレブンナイン</rt></ruby>と説明されています。<font color="grey">(いや わけわからんて!)</font>

### [Pages](https://pages.cloudflare.com/)
Webフロントエンドアプリケーションをインターネットで公開できます。
ビルド済みのアセットをアップロードするか、Git連携を行ってPages側でビルドして公開できます。

Vercel, Netlify, GitHub Pagesとは違って、無料プランの帯域幅制限がなく、Zero Trustを用いたアクセス制限も柔軟に行うことができます。


ここで紹介しなかったサービス(Images, KV, Queue, Stream, Pub/Sub .etc)も非常に興味深く、様々な利活用ができるものばかりです。ぜひ調べてみてください

</details>


[Lambda@Edge]: https://aws.amazon.com/jp/lambda/edge/
[Cloud Run]: https://cloud.google.com/run?hl=ja


#### WebSocket API (eqproxy-io)

アプリケーション起動中に、緊急地震速報や地震情報をいち早く受信するために、WebSocketを利用しています。
Oracle Cloud InfrastructureのTokyo, Osaka RegionにそれぞれUbuntu Serverインスタンスを建てて、WebSocketサーバとして運用しています。地理的冗長性を確保するために、TokyoとOsakaにそれぞれインスタンスを建てています。
この2つのインスタンスへは、Cloudflare Load Balancerを通じてアクセスされます。Cloudflare LBを通すことで、負荷分散を図るだけでなく、片方のサーバで何らかの異常が発生しサーバがダウンした際も、もう片方のインスタンスでサービスを提供し続けることができます。
Cloudflare LBは非常に優秀で、複数サーバの中のどれか1台がUnhealthyになった時に、全く別のサーバ郡に切り替える などの柔軟な対応をすることができます。
現在はOracle Cloud Infrastructure上ですが、マルチクラウド・マルチテナンシー構成にしていきたいです。

技術的には、TypeScriptで[Socket.IO](https://socket.io/)のサーバが建つようになっています。


WebSocketAPIは 各インスタンスが相互に接続し、死活監視を行っていますが これに加えて Better Stackを利用して外形監視を行っています。

https://betterstack.com

https://status.eqmonitor.app


### サービス類

ユーザへプッシュ通知で地震情報を伝えるために、Oracle Cloud Infrastructureのインスタンス上で、Firebase Cloud MessagingのAPIを叩いています。
これも、TypeScriptで構築してあります。


サービスのログは New Relicへ送信し、いつでも確認できるようにしてあります。
また、サーバに直接接続せずとも、GitHub Actions経由からデプロイできるようにしてあります。これにより、操作ミスによるサーバ停止・破壊を極力抑えています。(保守のために時々SSH接続しているので 同じっちゃ同じですが)

GitHub Self Hosted Runnerをインスタンス上でセットアップして、Runner上で`docker compose`コマンドを叩く感じです。
ワンクリックで本番環境へデプロイできるため非常に便利です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489005/5c8017f0-cea1-69bd-9558-aa72abf65f9e.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489005/98fbd5fa-d627-747c-5281-f0838f4e20ae.png)

---

## 2. 現段階のツラミ

このアプリケーションを正式にリリースする段階でのツラミを書いていきます

### 地図描画が相変わらず重い

地図描画がびっくりするくらい重いんですよ...........

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489005/904683f7-c470-5f45-b9b2-1b00c09ddf7f.png)


技術的な話は、このスライドに軽く書いてあるので興味のある方はご覧ください。

https://speakerdeck.com/yumnumm/flutterdemodi-tu-womiao-kitaiohua

ざっくりお話すると、地図データを解析して色んな図形を描いているのですが、これの描画が重くてユーザ体験が非常に悪くなっています。

Google MapやMapBox、MapLibreといったネイティブ実装された地図レンダリングエンジンを利用すれば良いのですが、ネイティブで描いた地図の上に 強震モニタの情報や緊急地震速報の情報を描画するのが非常に難しいため採用できていません。
どう難しいかというと、「比較的大容量のデータをやり取りする必要がある」・「高頻度で更新されるため速度を重視する必要がある」
地震履歴詳細画面の地図は、MapLibreを利用しており、震央や震度分布はすべてネイティブ側で描画しています。

ここらへんについては、近いうちに技術検証記事をしっかり書きます。


### 通知条件を設定したい

現在地や地域設定をして、揺れる可能性がある場合のみ通知するようにしたいです。

が、2つの理由からなかなか難しいんです....

#### 1. 緊急地震速報の場合は予想できない

気象庁により発表される緊急地震速報(予報・警報)では、震源が150km以深の場合でない限り予想最大震度が発表されますが、各地域の予想震度はその予想震度が4以上でない限り発表されません。

そのため、自分で震度を予想する必要があります。
しかし、これは[気象業務法 第三章 予報及び警報 第十七条 予報業務の許可](https://elaws.e-gov.go.jp/document?lawid=327AC0000000165#:~:text=%E7%AC%AC%E5%8D%81%E4%B8%83%E6%9D%A1%E3%80%80%E6%B0%97%E8%B1%A1%E5%BA%81%E4%BB%A5%E5%A4%96%E3%81%AE%E8%80%85%E3%81%8C%E6%B0%97%E8%B1%A1%E3%80%81%E5%9C%B0%E8%B1%A1%E3%80%81%E6%B4%A5%E6%B3%A2%E3%80%81%E9%AB%98%E6%BD%AE%E3%80%81%E6%B3%A2%E6%B5%AA%E5%8F%88%E3%81%AF%E6%B4%AA%E6%B0%B4%E3%81%AE%E4%BA%88%E5%A0%B1%E3%81%AE%E6%A5%AD%E5%8B%99%EF%BC%88%E4%BB%A5%E4%B8%8B%E3%80%8C%E4%BA%88%E5%A0%B1%E6%A5%AD%E5%8B%99%E3%80%8D%E3%81%A8%E3%81%84%E3%81%86%E3%80%82%EF%BC%89%E3%82%92%E8%A1%8C%E3%81%8A%E3%81%86%E3%81%A8%E3%81%99%E3%82%8B%E5%A0%B4%E5%90%88%E3%81%AF%E3%80%81%E6%B0%97%E8%B1%A1%E5%BA%81%E9%95%B7%E5%AE%98%E3%81%AE%E8%A8%B1%E5%8F%AF%E3%82%92%E5%8F%97%E3%81%91%E3%81%AA%E3%81%91%E3%82%8C%E3%81%B0%E3%81%AA%E3%82%89%E3%81%AA%E3%81%84%E3%80%82)により、事前の許可を得ない限り、予報を第三者に提供することができません。

> 第十七条　気象庁以外の者が気象、地象、津波、高潮、波浪又は洪水の予報の業務（以下「予報業務」という。）を行おうとする場合は、気象庁長官の許可を受けなければならない。

じゃあ、許可を取ればOKという話になりますが、許可を得るためには10万円程度必要となるため、収益を目的としないこのアプリケーションにはキツイものがあります。

1年以内に許可を取るための動きを始めたいお気持ちです。


#### 2. 通知配信システムを制作する必要がある

ユーザごとに設定した値に応じて、通知配信可否を判断する必要があるため、設定情報をサーバで保持し、地震発生時には**高速に**処理する必要があります。

このシステムを制作していかなくてはなりません。

MessageQueue (Ex. [RabbitMQ](https://www.rabbitmq.com/))やデータベース(ポスグレ使いたいおじさん)、API(デバイス設定を管理するため、JWT認証等が必要そう) を用意しなくてはならず、これの実現にはもう少し時間がかかりそうです。


---

## まとめ

Qiita記事公開予定日当日の夕方から書き始める という非常に計画性のないことをしてしまい申し訳.....
あ、23:58だまずい公開しなきゃ