React PWA + Rails GraphQLで作ったポモドーロサービスに使った技術やその選定理由を書いてみた

先日、『[g4](https://www.g-g-g-g.games)』というポモドーロ+RPGなサービスをリリースしました。

そのサービスで使った技術について聞かれることがあったので残しておきます。

# どんなサービス?

ポモドーロ・タイマーを使い25分間集中すると経験値をもらえ、その経験値でレベルが上がる。
って言う感じのやつです。

https://www.g-g-g-g.games

[![g4](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51402/6c1fef60-9f24-2233-821b-1b4b16575e21.png)](https://www.g-g-g-g.games)

## こんな特徴があります。
- ポモドーロ・タイマーやRPG的なUIはリッチで動きがある
- 現在のステータスをOGP画像にしてシェアできる
- 上昇する能力値や覚えるスキルは登録した文章を解析して決まる

# 構成はこんな感じ
![g4構成](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51402/1f7797cd-59e5-b556-2afe-b0a964acd0ac.png)

## フロントエンドの選定理由

フロントエンドはSSRしたReactアプリをfly.ioにおいています

### [React]

自分は過去に仕事でNuxt.jsや生Vue.jsを使ったことがあり、個人ではExpoやNext.jsでReactにも触っていました。

今回Reactを選択した理由は以下です。

- 型が欲しかった。Typescriptを使いたかった
- Reactのほうが書いていて楽しい（個人の感想です）
    - React Hooksめっちゃいい
- Apolloを使うならVueよりReactのほうが色々揃っている

### [[fly.io](https://fly.io)]
SSRするためにNode.jsをホスティングするやつが欲しかったので選定。
[mizchiさんのブログ](https://mizchi.hatenablog.com/entry/2019/02/21/235403)で知り、面白そうだなと思ったのがきっかけ。
べつにfirebase hostingでもよかったですが、以下が決め手

- firebase hosting同様初期導入費用がほぼ0
- CDN
- キャッシュコントロール
- svgを使ったOGP画像の生成ができる

特に、

> svgを使ったOGP画像の生成ができる

に有用性を感じて選定したんですが、現時点ではカスタムフォントと日本語への対応が十分でなく、別途Cloud Runを使うことになってしまっています（後述）
（サポートに聞いたらリポジトリ教えてもらったので（OSS）対応できるようなら自分で対応してなげたいなぁ）

##### キャッシュコントロールもよい

キャッシュの操作も面白くて、
g4の公開用ページではAPIからもらった結果をキャッシュして、キャッシュがあればそれをそのまま返すということをしています。

これによってAPIサーバーへの負荷はかなり低くなってると思います。
APIサーバー側からは、データ更新時にfly.ioのキャッシュを削除する操作をしていて、データが更新されるまではfly.ioはキャッシュを使い続けてます。

この辺のAPIが普通に使いやすくて簡単にかけるのでめっちゃ良きです。

##### fly.io良いよ

fly.io自体ははデプロイも早いし、導入コストも安いので[now](https://zeit.co)とかと並べて、手軽にNode.jsをホスティングするための選択肢にしてもいいんじゃないかなって思います。

### Next.jsを選ばなかった理由

SSRする予定ではありましたが、OGPだけ生成できればいいなというレベルだったので、通常のReactアプリで作りました。
なるべく継続開発するため、アップデートの手間と依存を減らしたかったのも少しある

### [PWA]を選定した理由

最初は、[Expo](https://expo.io)で作ろうかと思っていました。

もともとSPAでアプリっぽいUIを作る仕事を受けたときに、Nuxt.js + Firebaseで作り、マルチブラウザ対応が辛いと思った経験がありました。

RNならその辺いい感じに作れるかもと思って[みんなの成長](https://shwld.net/seicho-release/) というアプリでExpoを試しみて、使い勝手が良い事までは確認してました。

ただ今回はモバイルより、デスクトップアプリのほうが欲しいということに気づき、
Electronで作りやすかったり、PWAならそのままインストールもできちゃうのでWebでいいかなって思いPWAにしました。

なのでマルチブラウザ辛いは解決してないです。
ここは個人開発なので修行と割り切って本業に生かしていきます。
Safari対応が結構めんどいけど、ユーザー多くて無視できない...

マルチブラウザと言っても、現状Chrome、Safari以外はほぼ無視してまっす。

### [[Apollo Client (react-apollo)](https://www.apollographql.com)]

これもSPAでアプリっぽいUIを作る仕事で、Nuxt.js + Firebaseで作ったときに、Firebaseのデータベース（このときはrealtime database使ってた)をフロントに反映するのが辛くて、同じようにREST APIも辛いだろうなと思って、GraphQLに手をだしたのがきっかけでした。

GraphQLだけでもすごく良いのですが、Apollo Clientがいいのはキャッシュコントールで、
クエリ毎にキャッシュを優先するかサーバーを優先するかを選択できます。

また、キャッシュがあれば再取得しないなどを簡単に書けたり、とにかくこれはSPAのベストプラクティスだなって感じの実装をシンプルに書けます。

Mutationするとキャッシュが書き換わったりするのもめっちゃ良き

正直この辺のこと考えながらSPA実装するのってかなり大変じゃない?ってか大変だった
アプリライクなUIのSPAならApollo Client使おうぜ！

こいつのおかげでこれまたAPIサーバーの負荷はまあまあ抑えられてそうです。

## [Firebase Auth, Storage]について

バックエンドはRailsですが、APIサーバー作るのに正直Deviseとか使いたくないです（完）

Twitter連携やら諸々簡単なので選ばない理由がない。
Auth0は選択肢に入るけど今回GCPなのでGoogleでまとめられるし、そこまで込み入った認証必要ないしでこれにしました。

Storageは別に何でもよかったですが、Firebase Authの認証ユーザー単位での権限指定が楽なので使い勝手は良いです。

## デザインについて

g4のデザインはレスポンシブのヘルパーとして[bootstrap-grid](https://github.com/twbs/bootstrap/blob/master/dist/css/bootstrap-grid.css)だけ使わせてもらってます。
めっちゃ便利なのでおすすめ。

それ以外はcss全部自分で書いてます。

コンポーネントファーストで作成しており、Atomic Designの粒度で、[Storybook](https://storybook.js.org)作ってコンポーネントを作ってる感じです。
Storybookは割とぶっ壊れやすかったり、他のビルド設定と競合とか重複したり、辛みもありますが、[storyshots](https://storybook.js.org/docs/testing/structural-testing/)によるスナップショットの差分確認ができるのと、テストの安心感があるので、無いと無いで不安ではあります。

## バックエンドの選定理由

### [[graphql-ruby](https://graphql-ruby.org) & Ruby on Rails]
Apollo Clientの項で書きましたが、GraphQLを使うことは決まっていました。
GraphQLをマネージドでホスティングしてくれるやつを探したりしたんですが、普段Railsエンジニアなので慣れてるからテストとかも書きやすいしRailsでいっかで、graphql-rubyにしました。

Railsにするとランニングコストは割とかかるんですが、今回は個人開発のメインに据えて長く継続開発したいを目的にしてたので、
稼働してないものにお金払うみたいなことにならない想定（自分が使い続ける）で、コストかける覚悟はしました。

graphql-rubyは仕事でも使ってるので、今後も押していきたい存在。

### [GCP/App Engine]

App Engineについては[こちらの記事](https://qiita.com/shwld/items/e86ee3f642c7857dd56e)で書きました。

要約するとHerokuでも良かったけど、使ってみたかったのでGCPにした。です。

## [GCP/Natural Language API]

便利。Azureとかにもありますが、今回はGoogle統一で行きました。くらいの選定理由

## [GCP/Cloud Run]について

Cloud Runは今回使う予定は全くありませんでした。

今利用している理由としては、

- fly.ioでOGPを生成できるとふんでいたができなかった。
- とりあえず手っ取り早くカスタムしたDockerimageでfry.ioをあまりお金をかけずに使えるとこはないか。

からの、Cloud Run。

中身は日本語とフォントをインストールした普通のNodeイメージに
fly.ioのサーバーを単純に乗っけて起動しているだけです。（プレビルドして実行したいがやり方が分からずdevサーバー起動しているのでなんとかしたい。誰かやり方知りませんか。）

なので、いずれはfly.ioだけでできるようして、Cloud Runをやめたいです。

#### Cloud Run超便利
そんな理由で使い始めましたが、驚くくらい簡単かつ手軽にカスタムDockerイメージが使えるので、Cloud Runめっちゃ便利です。
今後活用していきたい。

## おわりに
割と構成的にはガチよりになりました。
自分の技術力総動員してる感じなのでテンション上がってめっちゃ楽しい！

g4の運用を支える技術についても今後書いていきたいです。

書きました -> [ポモドーロ+RPGな個人開発サービス開発を支える技術。を色々改善したい](https://qiita.com/shwld/items/df07a36d332f86f87463)

みんなでレベル上げしましょう！

https://www.g-g-g-g.games

[![g4](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51402/6c1fef60-9f24-2233-821b-1b4b16575e21.png)](https://www.g-g-g-g.games)