デスクトップ開発出身者が 無料のサービスだけ使って Web 開発に触れてみる

## はじめに

私は C#, WPF, .NET など Windows のデスクトップアプリ開発からスタートした人間で、Web 開発は体系的に学べておらず、実務で開発する機会があっても場当たり的な対応になりがちでした。部分的に携わるだけでは Web 開発の全体像が掴めず、いまいち理解が深まりません。この状態から脱却すべく、ネットにあふれる情報を手掛かりに、改めて Web 開発に薄く広く触れてみた際の備忘録をまとめます。

同じような境遇にある方がおられましたら、環境構築やサービス選定の参考にして頂けますと幸いです。

### ご留意

この記事にはデスクトップアプリ開発と Web 開発の間に優劣を付ける意図はございません。あくまでも自学の記録です。
また、誰に教わるわけでもなく進めていたため、その分野の常識や定石から逸脱した部分もあるかと思います。どうかご容赦ください。今もなお勉強中です。

## 本文

### やってみたかったこと

全部ふわふわしていますが、

- 無料のサービスだけで頑張る（ドケチ）
- モダンな開発環境を構築する
- 利用者の多い言語とフレームワークで開発する
- フロントエンドとバックエンド両方に触れる
- リリース作業を自動化する
- システムの運用監視をする

などがありました。
仕事ではないので何をもって達成かなどは考えず、答え合わせもしていません。モチベーションを保てるよう、気の赴くまま進めます。

### つくったもの

フロントエンドとバックエンドに分けて、API 呼び出しによる「ユーザ認証」「DB アクセス」「メール送信」など、業務アプリ開発でよくある機能の実装例を用意し、自分用のスケルトンコードを作りました。これ自体は何かのシステムにはなっていませんが、今後何かを作る際のベースとして利用していければと目論んでいます。

- 画面キャプチャ
![サインアップ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561457/54685891-f46e-bd34-dd8a-bfe8b34d22d6.jpeg)
![ホーム](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561457/6509af12-4612-febd-8c57-6e0de15071ea.jpeg)
![アカウント管理](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561457/f3a71c7c-8c49-f54b-84b0-9f08669da041.jpeg)

- ソースコード

https://github.com/kawasawa/hello-client

https://github.com/kawasawa/hello-server

- API 仕様書

https://kawasawa.github.io/hello-server/openapi.html

※ 画面キャプチャにあるような[Webサイト](https://kawasawa.github.io/hello-client/signin)もデプロイされていますが、API サーバが無料プランで運用されている都合で休止している場合がございます。

### 概要

TypeScript と React / Express フレームワークによる Web アプリケーション開発のためのスケルトンコードです。フロントエンド側の React から axios による API 呼び出しで、バックエンド側の Express に処理をリクエストしています。用意した処理として、ユーザ認証を伴うアプリケーションに必要な以下が提供されています。

- サインアップ
- サインイン
- サインアウト
- 退会
- メールアドレス認証
- パスワードの再設定

#### プラットフォーム

プラットフォームの構成は次の通りです。いずれのサービスも無料プランで利用しています。

| | サービス |
|:-:|:-:|
| リポジトリ | [GitHub](https://github.co.jp) |
| CI/CD | [GitHub Actions](https://github.co.jp/features/actions) |
| ホスティング (Web サイト) | [GitHub Pages](https://pages.github.com) |
| ホスティング (API サーバ) | [Heroku](https://jp.heroku.com) |
| データベース | [PostgreSQL (Heroku)](https://elements.heroku.com/addons/heroku-postgresql) |
| ログ監視 | [Papertrail (Heroku)](https://elements.heroku.com/addons/papertrail) |
| メトリクス監視 | [Librato (Heroku)](https://elements.heroku.com/addons/librato) |
| メール配信 | [SendGrid](https://sendgrid.com/) | 
| ドメイン | [Freenom](https://www.freenom.com/) |

大きく二つに分かれており、バックエンドのホスティング、データベース、監視サービスは丸ごと Heroku に任せています。Librato はメトリクスのモニタリングツールです。Papertrail はログを収集し、エラーログを検知すると Slack に発報してくれます。

もう一つの GitHub はフロントエンドのホスティングと CI/CD を担います。GitHub Actions は後述のリリースフローの肝です。Web サイトのホスティングには GitHub Pages を使用します。

![構成図](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561457/826450eb-272f-773c-02c2-2defa01d1c57.png)

#### パッケージ

プログラムで使用させて頂いたパッケージは以下になります。

（パッケージではないですが）プログラミング言語は TypeScript を選びました。フロントエンドとバックエンドの両方を同じ言語で記述できる利点は大きく感じます。そのほか、個人的に C# に触れている期間が長かったため、同じく Microsoft でメンテナンスされているという安心感もありました。

フレームワークは日本国内で見ると Vue、国外も含めれば React という情勢でしたが、React Native にも興味があったため React にしました。バックエンドは Express が優勢に見えました。

| フロントエンド | 使用技術 | | バックエンド | 使用技術 |
|:-:|:-:|:-:|:-:|:-:|
| プログラミング言語 | TypeScript || プログラミング言語 | Typescript |
| フレームワーク | React || フレームワーク | Express |
| テンプレートエンジン | Material-UI || クッキーパーサ | cookie-parser |
| ルーティング管理 | React Router || CORS 対応 | cors |
| ステート管理 | React Redux<br/>(Redux Toolkit) || ロギング | morgan |
| フォーム管理 | React Hook Form || トークン認証 | jsonwebtoken |
| バリデーション管理 | Yup || 暗号化 | bcrypt |
| 多言語対応 | React i18next || バリデーション | express-validator |
| HTTP クライアント | axios || エラーハンドリング | boom |
| 静的検証 | ESLint / Prettier || テンプレートエンジン | Mustache |
| ユニットテスト | React Testing Library / Jest || 静的検証 | ESLint / Prettier |
|||| ユニットテスト | Jest / Sinon |

### リリースフロー

リリースは GitHub Actions のワークフローで行います。
main ブランチへのマージをトリガーに下記の処理が順に実行されます。

0. 静的解析とユニットテストを実施する
0. カバレッジレポートを Codecov にアップロードする
0. API 仕様書を静的な Web ページとして GitHub Pages に展開する
0. Heroku のステージング環境にプログラムをデプロイする
0. 処理結果を Slack に通知する

![GitHub Actions](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561457/a5963187-d990-c18d-559c-42e2f65bf809.jpeg)

ここまででステージング環境へのデプロイは完了しますが、本番環境は変更されていません。ステージング環境での正常動作を確認してから反映します。
（※フロントエンドの場合は上記ユニットテスト後、すぐに GitHub Pages へ展開されます。）

0. Postman でステージング環境に対してテストを実施する
0. Slack からコマンドを送信し、本番環境に変更を適用する

![Postman](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561457/0db0a5cf-3bac-baf7-f7e2-02e4395c80ea.jpeg)
![Slack](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561457/c77ee1f3-6f8a-9e7f-f914-b3b11c6ac3c4.jpeg)

以上がリリースの流れです。
開発環境にある VSCode, Slack, Postman だけですべての手順が完了します。当初思い描いていたリリースの自動化がある程度形になりました。

| | サービス |
|:-:|:-:|
| チャットクライアント | [Slack](https://slack.com) |
| WebAPI クライアント | [Postman](https://www.postman.com) |
| カバレッジレポート | [Codecov](https://about.codecov.io) |
| API 仕様書変換 | [ReDoc](https://github.com/Redocly/redoc) |

![リリースフロー図](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561457/96d9ff3c-deae-2da3-cf93-d74346f2b1c3.png)

<details><summary><b>こちらを参考にさせて頂きました。</b></summary><div><ul>
<li>GitHub Actions による GitHub Pages への自動デプロイ
https://qiita.com/peaceiris/items/d401f2e5724fdcb0759d</li>
<li>GitHub Actions で Heroku に自動デプロイ
https://zenn.dev/shin_shin_01/articles/53009a81728e21</li>
<li>GitHub ActionsでSlack通知するなら8398a7/action-slackが機能豊富かつお手軽で超便利
https://zenn.dev/shonansurvivors/articles/fab832f7bfa8cebac24f</li>
<li>Heroku ChatOps (Slack 統合)
https://devcenter.heroku.com/ja/articles/chatops</li>
</ul></div></details>

### 開発環境

私自身は Windows で開発していますが、Web 開発といえば Mac という空気があります。それもあって、開発ツールには Mac 向けにも提供されているものを選びました。
作業の大半は VSCode で行いますが、API 仕様書の作成は Stoplight Studio、API のテストには Postman を使っています。DB は作ったり壊したりを繰り返すので Docker で動かし、DBeaver で接続していました。

| | ツール |
|:-:|:-:|
| コードエディタ | [Visual Studio Code](https://code.visualstudio.com) |
| API 仕様書作成 | [Stoplight Studio](https://stoplight.io/studio) |
| 仮想環境 | [Docker Desktop](https://www.docker.com/products/docker-desktop) |
| DB クライアント | [DBeaver](https://dbeaver.io) |
| Git クライアント | [Sourcetree](https://www.sourcetreeapp.com) |

![開発環境](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561457/d5b755ef-ab4f-946b-fc4c-4f2799ad0e0e.png)

（[やってみたかったこと](#やってみたかったこと) で「モダンな開発環境を構築する」と目標にしていましたが、はたしてこれがモダンなのかどうかはよく分かりません...）

## 余談

### 認証処理の難しさ

:::note warn
実装しておいて難ですが、今回のプログラムは自分用かつ勉強を兼ねたものであって、実際には認証処理は自前で実装せず、既存のサービスを使うべきであると考えられます。
:::

API による認証処理と状態維持が全くイメージできず、開発初期は非常に難航していました。既存の Web サービスやアプリはどのようにして実現されているのか記事を見て回り、今回は JWT とアクセストークン、リフレッシュトークンの考えを取り入れることにしました。

https://qiita.com/yosshi4486/items/7ec5fdd53577c8663603

https://qiita.com/Hiro-mi/items/18e00060a0f8654f49d6

現行の処理は以下のようになっています。
ただし、正直どの仕組みが正解なのか、それをどうやって実装に落とし込むのか、まだ完全に理解できていません。今後も内容の見直しが必要だと感じています。

1.サインイン

- フロントエンド
  - リクエストにメールアドレスとパスワードを付与する
  - リクエストを送信する
- バックエンド
  - メールアドレスとパスワードを検証する
  - リフレッシュトークンをクッキーに付与する (httpOnly, secure, path)
  - アクセストークンを返却する

2.リクエスト

- フロントエンド
  - アクセストークンをリクエストヘッダーに付与する
  - リクエストにパラメータを付与する
  - リクエストを送信する
- バックエンド
  - アクセストークンを検証する
  - レスポンスを返却する

3.リフレッシュ

- フロントエンド
  - リクエストを送信する
- バックエンド
  - リフレッシュトークンを検証する
  - 新しいリフレッシュトークンをクッキーに付与する (httpOnly, secure, path)
  - アクセストークンを返却する

![認証フロー](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561457/f05fab7c-4561-2ec1-ac83-2711e2a07209.png)

### 無料プランの限界

[やってみたかったこと](#やってみたかったこと) に挙げていた「システムの運用監視をする」が心残りでした。Papertrail でログ起点のエラー検知はできるものの、Librato によるメトリクス起点の発報は無料プランではできませんでした。そのほか、外形監視サービスの Pingdom などそもそも無料プランが用意されていないものも存在します。これだけ使っておいておこがましいですが、この辺りまでが無料プランの限界だと感じました。

## おわりに

デスクトップアプリ開発は、手持ちの PC 内で作業が完結しリリースにも費用が掛からず、自学しやすい分進んで取り組んでいました。（Microsoft の開発者ライセンスは必要ですが実質払いきりなので。）
一方で Web 開発は、ランニングコストが気がかりで、深入りせず二の足を踏んでいる状態でした。実務においても基幹業務システム開発はデスクトップアプリが多くを占めており、これまでの知見が十分活かせる恵まれた環境にいたため、正直それに胡坐をかいていました。

あらゆるサービスが Web 化されていく昨今、いずれの開発であっても、対向システム等で Web サービスや API が登場する機会は多く、これらの知見の不足を日々痛感しておりました。私のような人間はとにかく自分で手を動かさないと身に着かないため、実際にコードを書き、既存のサービスを駆使してそれを稼働させることにしたのが今回の記事の内容です。この過程で苦しんで得た知見を公私で活かし、今後もブラッシュアップしていきたいです。

以上です。
ここまで読んでくださりありがとうございました。
何かの参考になれば大変嬉しく思います。