あのLEADYOUが復活！コストを抑えてサービスをデプロイする秘策とは！？


3年ほど前に[GitHubのREADMEをサクッと高品質で書けるツール「LEADYOU」](https://qiita.com/Kyome/items/2112e9d1871ec0a367ea)を開発し記事を公開したところ、800を超える**いいね**をいただき良い感じのスタートを切れたのですが、公開から半年ほど経つとアクティブユーザーがほぼいなくなり、サーバー代金を払い続けるのが厄介になったため人知れずサービスを終了していました。

しかし、せっかくのアイデアや実装が勿体無いと思い、なんとか**ランニングコスト**と**メンテナンスコスト**を抑えて配信できないかを考えました。その結果、３連休を生贄にして最高の配信構成を組むことができたので方法をまとめます。

https://hacknock.github.io/leadyou

## TL;DR

- Webサイトホスティングは`GitHub Pages`を使用
  - webpackによるReact × TypeScript構成のビルドは`GitHub Actions`で行う
- サーバーサイド（データベース）はJSONファイルを読み込むように置き換え、`GitHub Actions`で直接リポジトリのファイルを定期的に書き換える
- サーバーサイド（API）はツールの特性上`GitHub REST API`で事足りた

※ LEADYOUは商用ではなく、あくまで開発者を支援するツールなのでこの構成が成立している側面はあります。

## LEADYOUの機能要件

- トップページ
  - 入力されたURLがPublicなGitHubリポジトリのURLであるかを判別する
  - GitHubリポジトリのURLであった場合、オーナー名とリポジトリ名を抽出する
  - 実際にツールを利用して作成され掲載されているREADMEの一覧を表示する
  - フッターでこれまでに作成されたREADMEの累計数を表示する
- フォーム入力ページ
  - 定義されている構成に従ってフォームを自動形成する
  - オーナー名とリポジトリ名の情報を用いてフォームに入力すべき情報を取得し自動穴埋めをする
  - フォームの入力内容からREADMEのMarkdownを構成し、プレビュー表示したりダウンロード用のZipを生成する
- ドキュメントページ
  - Markdownで書かれたドキュメントをHTML化して表示する

上記の機能要件のうち、READMEの一覧表示、累計数の表示、フォームの自動穴埋めはREST APIサーバーを用意して行なっていました。また、デプロイも同じAPIサーバーで行なっていたためファイルサーバーも兼任させていました。

また、READMEの一覧表示と累計数の表示に関しては、過去にREADMEを生成したリポジトリの情報をデータベースで持っておき、フロントに要求されたら最新N件のURLリストと累計数を返すようにしていました。

その他はフロントエンドで処理していました。

### 機能要件実現の課題

GitHub REST APIを認証なしで叩く場合、１時間あたり６０回という制限があります。そのためユーザーが認証なしで使えるツールとして提供するには、なるべくサーバーサイドで認証ありでAPIを叩く必要がありました。

GitHub APIは、入力されたリポジトリの存在チェック、フォームの自動穴埋め用データ取得、過去にREADMEを生成したリポジトリ一覧の更新で必要です。一つ目に関しては、当初APIを叩かずに実際のリポジトリページへアクセス可能かどうかで判断しようとしていたのですが、CORSに阻まれて敢えなく断念しました。二つ目と三つ目に関してはAPIを用いないと取得できない情報ですが、大量にAPIを叩く必要があるため認証ありにせざるを得ませんでした。

## 元々の技術構成

- フロントエンド
  - Pure JavaScript
  - [Web Components](https://developer.mozilla.org/ja/docs/Web/API/Web_components)
  - [Marked](https://github.com/markedjs/marked)
- バックエンド
  - [Node.js](https://nodejs.org/en)
  - [Apache2.4](https://httpd.apache.org/docs/2.4/ja/)
  - [MariaDB](https://mariadb.org/)
  - [Docker](https://www.docker.com/ja-jp/)
  - [GCP](https://cloud.google.com/free/) or [AWS EC2 (Amazon Linux 2)](https://aws.amazon.com/jp/amazon-linux-2/)

### フロントエンドについての補足

当時２人で開発していたのですが、そのメンバーが学習コストなしでフロントとバック両方書けるようにメインで書く言語はJavaScriptだけで済むようにしていました。

また、READMEに書く項目を手軽に変更可能なようにしようというアイデア実現するために、各項目の入力フォームの特徴をJSONで定義しておき、それを元にフォームを自動形成する仕組みを作りました。具体的な実装は@AkiraKashiharaのこだわりによりWeb Componentsを用いました（詳しくは[こちらの記事](https://qiita.com/AkiraKashihara/items/139318ac01fdbfab3342)を読んでください）。

さらに、Markdownに関するツールを作るというテーマからとことんMarkdownを使ってやろうという思いで、ドキュメントは全てMarkdownで書いておき、そのリソースを読み込んでHTMLに変換して表示することにしました。

### バックエンドについての補足

なるべく安くor無料で使えるものを利用しようということでデータベースにはMariaDBを用いました。

また、開発環境やデプロイ環境の差分を吸収するためにDockerでNode.jsが動く環境を用意しました（実際に私の開発機はMacで相方の開発機はWindowsの時もありました）。

デプロイサーバーに関しては、最初は相方が得意なGCPを使っていたのですが、途中から私が得意なAWSに切り替えて運用していました。ランニングコストは月額1500円くらいでした。

## 本題　技術構成の見直し

冒頭にも書いたように、**ランニングコスト**と**メンテナンスコスト**の削減を目的として技術構成を見直しました。

### ランニングコスト面

普通サーバーを運用するにはお金がかかります。WebサイトのホスティングとAPIサーバーの可動の両方を実現するためにはVPSやレンタルサーバーの利用が避けられないように思えますが（自前サーバー過激勢は除外）、我々には`GitHub Pages`と`GitHub Actions`という強い味方がいます。

`GitHub Pages`は個人ユーザーやOrganizationがWebサイトをGitHub上のリポジトリから直接ホスティングできる仕組みです。しかもPublicなリポジトリであれば**無料**です。もちろん商用利用禁止などの[制限事項](https://docs.github.com/ja/pages/getting-started-with-github-pages/about-github-pages#github-pages%E3%81%AE%E5%88%A9%E7%94%A8%E4%B8%8A%E3%81%AE%E5%88%B6%E9%99%90)はありますが、簡単にWebサイトをホスティングしてくれるのですから非常にありがたいです。

`GitHub Actions`はビルド、テスト、デプロイのパイプラインを自動化できるCI/CDの仕組みです。リポジトリに対するPushやPRへのイベントをトリガーにして事前に定義した処理を実行するワークフローを作成できます。嬉しいことに、ワークフローの中ではGitHub APIを叩くのに必要なトークンがデフォルトで用意されていたり、ワークフローを定期実行することができたり、リポジトリに直接差分をCommitできたり、`GitHub Pages`のデプロイ方法を細かく調整できたりします。しかもPublicなリポジトリなら**無料**です。つまり無敵です。

`GitHub Actions`を使って定期的にGitHub APIを叩き、LEADYOU製のREADMEが掲載されているリポジトリの検索を行い、そのリストをJSONファイルとして書き出してリポジトリに上書きすることができます。そのため、定期的なデータ更新が行われるデータベースとして活用することができます。リアルタイム性には欠けますが、本ツールの要件は十分に満たせます。なお、APIサーバーとして活用するには`GitHub Actions`は向いていません。~~ので、別のサービスでその要件がある場合、GASを使えばなんかできるんじゃないかと邪推しました。~~

### メンテナンスコスト面

まず、JavaScriptは静的型言語ではないので、黒魔術とか錬金術みたいな感じでメンテナンス性の悪い実装が容易にできてしまうのがよろしくないと思っていました。なのでTypeScriptを選択しました。

また、Web ComponentsはWebの標準技術であるため特別な環境導入が不要であるというメリットはあるのですが、おそらくReactやVue.jsなどと比較すると普及しておらず、筆者も不慣れに感じていました。今回は宣言的UIでメンテナンス性の高いデータフローを高速に実装したかったので、筆者が比較的手慣れているReactを採用しました。（普段はSwiftUIを書いているので、Reactは馴染みやすいです。なお、Web Componentsで宣言的UIを実装できる[Lit](https://zenn.dev/ken7253/articles/lit-overview)なるものもあるそうです。）Pure JavaScriptでのDOM操作をしなくて良いので、パッとコードを眺めただけでもどんな画面が構成されるのかがわかります。これは素晴らしいことです。

React × TypeScript構成をビルドするのにwebpackとNode.jsを使います。（もっとモダンなやつもありそうですが不勉強です。）

バックエンドのメンテナンスコスト性に関しても`GitHub Pages`と`GitHub Actions`は最高のパフォーマンスを与えてくれます。VPSにSSHで入って複雑な操作を行う必要もありませんし、GitHub Pagesは勝手にHTTPS対応してくれますし、GitHub ActionsでのデプロイもチョチョイとYAMLを書くだけ（webpackビルドのためのNode.js環境セットアップなら[actions/setup-node](https://github.com/actions/setup-node)を使うだけ）です。なんといっても、アクセスするプラットフォームがGitHubだけですから、スイッチングコストが全くありません。

唯一の問題点は、**GitHubが落ちたらサービスも完全に止まる**ことだけです笑
一蓮托生ってやつですよ。

 ## 新しい技術構成

ということで以下のような構成になりました。

- フロントエンド
  - TypeScript
  - React
  - Marked & react-markdown
  - webpack
  - Node.js
- バックエンド
  - GitHub Pages
  - GitHub Actions
  - Node.js

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/67153/db70a66c-4e8f-6bd8-4639-7c0965f30db2.png" width="500px"/>

いかがでしょうか？結構いけてると思いませんか？

元となる実装があったとはいえ、3日でリプレイス作業ができたのもGitHubの力が大きかったです。本記事をきっかけに、ぜひ皆さんも`GitHub Pages`と`GitHub Actions`を活用してみてください！

あと、[LEADYOU](https://hacknock.github.io/leadyou)を使ってみたら感想やコメントをぜひください！