Zennを支える技術とサービス構成

更新:2020/09/23
公開:2020/09/08
5 min読了の目安(約3400字TECH技術記事

Zennという技術情報共有サービスを作りました。有益な知見をシェアした開発者が、その見返りを得られるようなサービスにしたいと思います。気合いを入れつつも、時間をたっぷりかけて地道に育てていきます。

このページでは、Zennを支えている技術やサービスを紹介します。

フロントエンド

Next.js

フロントエンドにはNext.js(React)を使っています。開発当初はNuxt.jsを使っていたのですが、TypeScriptとの相性を考えてNext.jsへ移行しました。

技術情報共有サービスなので、主要な流入元はいずれ検索エンジンに落ち着くと予想しています。そのため、検索エンジンにインデックスしてもらいたいページはサーバーサイドレンダリング(SSR)しています。

動的コンテンツもキャッシュ

Next.js 9.4からIncremental Static Regenerationという最高の機能を使えるようになりました。ページにリクエストがあったときに、背後でビルドを行い、次回以降のリクエストではエッジサーバーからキャッシュされたデータを返します。これにより、ユーザーが投稿するコンテンツに対しても、高速なレスポンスを実現できるというわけです。

ただし、ユーザーの認証が絡むコンテンツに対してもキャッシュが効いてしまうため、少し工夫は必要になります。この工夫については別の記事にまとめる予定です。

グローバルの状態管理はRecoil

グローバルの状態管理には、思い切ってRecoilを使ってみました。Recoilは2020年5月頃にリリースされたばかりのReactの状態管理ライブラリです。
学習コストが小さく、React Hooksと合わせて使いやすいため今のところ気に入っています。

その他のライブラリ

主に今後も継続してメンテナンスされそうな有名どころのライブラリを使用しています。

  • styled-components:毎回迷いつつもなんだかんだで採用することになるCSS in JS。今後パフォーマンスの問題があれば置き換えるかも。
  • twemoji:AppleのOS以外の絵文字がZennのテイストにマッチしなかったため使用。
  • markdown-it:マークダウンをHTMLへ変換するために使用。
    など

Reactはモーダル(react-modal)やセレクトボックス(react-select)、スライダー(rc-slider)などよく使う機能ごとに定番のライブラリがあるのが嬉しいです。

バックエンド

Rails(APIモード)

バックエンドには「個人的に慣れている」「速く実装できる」という理由からRuby on Railsを選びました。ただ、今更ながらZennではExpressやNest.jsを使うべきだったと後悔しています。
理由は、フロントエンドでTypeScriptを使っていること(バックエンドでもTypeScriptを使って型を共有したい)、手軽なデプロイ先の選択肢がNode.jsと比べると限られることなどです。

本番環境のデプロイ

アカウント管理が煩雑にならないように、できる限りGCPのサービスを使用するようにしました。

Cloud Runは断念

開発環境にはDockerを使っているため、はじめはCloudRun(フルマネージド)にまとめてデプロイしていました。デプロイ体験は素晴らしかったのですが、実際にアプリを操作していると、バックエンドのレスポンスがまれに15秒以上遅延する現象が発生してしまいました。

原因はRailsの起動に時間がかかることのようです。公式ドキュメントの開発のヒントにも書かれているように、Cloud Runではサービス(コンテナ)の起動時間がレスポンスのレイテンシに直結します。

一部のGemを起動後に読み込むように変更してみたりもしましたが、大きな効果はなく、泣く泣くCloud Runを諦めることにしました。

バックエンドはGAE

ひとまずバックエンドにはGoogle App Engine(GAE)を使うことにしました。
Kubernetes Engine(GKE)の利用も少し検討しましたが、現状の単純なアーキテクチャではKubernetesを採用するメリットは小さいこと、GKEだとコストがかかること等からGAEを選びました。

また、以下のGCPサービスを利用しています。

  • Cloud Build:CIツール。自動テストと自動デプロイを実行。
  • KMS:秘匿情報の管理。Cloud Buildでのビルド時に暗号化された秘匿情報を復号。
  • Cloud Storage:ユーザーがアップロードする画像の保存先。
  • Cloud SQL:DB。PostgreSQLを利用。
  • Google Analytics Reporting API:各コンテンツのPVや滞在時間などの統計情報を取得するため。

フロントエンドはVercel

フロントエンドのデプロイ先はNext.jsとの相性からVercelを選びました。無料の範囲で十分な性能と機能が利用できます。デプロイは簡単ですし、特に設定しなくても静的ファイルをエッジキャッシュしてくれます。

その他に利用しているサービスなど

また、以下のようなサービスを利用しています。

  • Stripe:決済のため。
  • SendGrid:メール配信のため。
  • GitHub Apps:ユーザーが連携したリポジトリからコンテンツを同期するため。
  • Cloudinary:OGP画像を動的に生成するため。

OGP画像の生成については、Cloudinaryだと表現が限られるため、そのうちvercel/og-imageのような手法に置き換えようと考えています。

今後、開発をしていく中で変更があれば、追記or書き直そうと思います。