React.js + AWSでプログラミング無しで自動ブラウザテストが出来るサーバレスWebアプリを作ったお話

<b>敵「リリースした後に既存のコンテンツがおかしくなってないか"目視"でチェックしてね」</b>

<b>ぼく「はい (やりたくねぇ〜〜〜〜〜大体どんなパターン網羅して、どこまでチェックしたらいいんだ？ 大体人間が目視でチェックしてもそのうち精度が落ちて結局見落としggg) 」</b>

というのが発端で、以下のように**リリース前後のWebページのスクリーンショットを取得・比較して変更点を視覚的に検出するブラウザテストWebアプリ**を作りました。

![appvisual.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312085/7dafc95f-57cb-7bca-5edb-f4318235cf49.png)


実装したアプリ「<a href="https://diffender.hassoubeat.com/" target="blank" >Diffender</a>」と利用した技術要素などについて紹介していきます。

# 作ったアプリ「Diffender」の紹介

## 何が出来るか？
Webページのスクリーンショットの取得、取得したスクリーンショット間の変更点の検出ができます。

![appvisual2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312085/a03b71ab-27b8-78a4-5784-e3531a93038d.png)
こんな感じで2つスクリーンショットを取得した後に差分を取得することで、リリース前後で表示内容に想定外の変更がないことのチェックが行えます。

一度テスト設定を作成した後は、ワンクリックで繰り返し同じ内容のテストを行えます。

ここ見てもらうのが一番はやいかもです
https://diffender.hassoubeat.com/about

## コンセプト
<b>「エンジニア以外の人も簡単に自動ブラウザテスト」</b>

従来の自動ブラウザテストは<a href="https://github.com/SeleniumHQ/selenium" target="blank">Selenium</a>とか<a href="https://github.com/puppeteer/puppeteer" target="blank">Puppeteer</a>などを利用してブラウザ操作(クリック、テキスト入力)をプログラミングする必要があるため、エンジニア以外の人にはハードルが高いです。


そのハードルを下げるべく、<b>プログラミングをしなくてもブラウザ操作</b>が行えて、<b>テスト結果がスクリーンショットとして出力</b>されるため誰でも結果の正否が判断が行えるブラウザテストアプリを実装しました。

目視でデグレチェックという辛い作業からサヨナラ。

## なんで作ったの？

#### ① 非エンジニアのディレクターさんやステークホルダーの方でも見て分かるデグレ検証結果が欲しかった
品質の担保としてはユニットテストとかでもいいんですけど、そのユニットテストの実施内容が分からない非エンジニアの方でも**実際に表示される画面のスクリーンショットの差分**という形でリリース前後のテスト結果が見れると安心できるようにしたいなーと思ったのが一つ目です。

※ 勿論ユニットテストの代わりになるものではないです

#### ② ブラウザテストの度プログラムを書きたくない、すぐ実行できるようにしたい
すでに触れていますが、従来のブラウザテストはSelenium、Puppeteerなどを利用してプログラミングを行う必要がありました。ハードルが高すぎる。。。

しまいにはプログラムを書いてもテスト大量のページが沢山あると、全ページのテスト完了までにすごい時間がかかります。
そこらへんを並列でササッと実行してくれるやつが欲しかったのが2つ目です。

#### ③ サーバレスSPAを実装する経験値が欲しかった

サーバレスSPAをゼロから構築する経験がなかったので、実際に設計・実装を通して経験値を積みたい！というのが3つ目。~~(理由の8割くらい)~~

最終的には形になるものが実装できましたが、効率の良い開発環境はもう少し改善の余地がありそうでした。
※ 主にAWSのローカルモック周りの最適解がよくわからない...

# アーキテクチャ
Diffenderのアーキテクチャはこんな感じです。

![arch.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312085/75feb017-568a-97a8-b809-4808d8990f66.png)

フロントエンドはReact.js、バックエンドはAWS関連サービスで実装しています。
言語はフロント、バックエンド両方JavaScript(Node.js)です。

## フロントエンド

### JavaScript
<img width="100" alt="js.png" src="https://cdn4.iconfinder.com/data/icons/logos-and-brands/512/187_Js_logo_logos-128.png">
今回はプレーンなJavaScriptで実装しました。
TypeScriptはまた今度...。

### React.js
<img width="100" alt="react.png" src="https://cdn4.iconfinder.com/data/icons/logos-3/600/React.js_logo-128.png">
UI構築用のJavaScriptフレームワーク。
Vue.jsと悩んだんですけど、JavaScriptのレベルアップにはReact.jsがいいみたいな記事をどっかで読んで選んでみました。
確かにJavaScriptの理解が深まった気がします。
(でも終始create react appの優しさに抱かれていた気が...自前でビルド設定のカスタマイズとかメンテナンスするのしんどそう)

次はVue.jsでチャレンジしたいです。

### Redux
<img width="100" alt="redux.png" src="https://cdn4.iconfinder.com/data/icons/logos-brands-5/24/redux-128.png">
アプリケーション全体の状態管理ライブラリ。
一番設計が難しかった気がする。
どこにビジネスロジックを集約するのがいいのか未だに最適解が分からない...。

### AWS Amplify
<img width="100" alt="amplify.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312085/fa643cfa-e80c-0806-8824-c908674efcda.png">

バックエンドリソースへのアクセス用ライブラリとして利用しました。
再考するとaxiosとかでも十分だったかもしれない。。。

## バックエンド

### AWS Lambda
<img width="100" alt="lambda.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312085/80c97df5-2fa5-e0a0-1090-8d948a41cf3c.png">

イベントトリガーで好きなコードを実行できるAWSサービス。
ランタイムはフロントと揃えてJavaScript(Node.js)

サーバサイドの処理は全部Lambdaで実装しています。サーバレスバンザイ。
Provisioned Concurrencyはケチってコールドスタートなので、初回アクセス時はちょっと時間かかります。。。

後述するSQSと組み合わせて、スクリーンショットの取得や差分の取得といった時間のかかる処理は並列処理にしています。

### AWS API Gateway
<img width="100" alt="apigateway.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312085/23acd0a7-115a-35f5-1a36-10c34a9b43e3.png">

簡単にREST APIの構築ができるようにするフルマネージドなAWSサービス。
フロントエンドからコールしたいLambdaのコードと連携してREST APIにしています。

### AWS SQS
<img width="100" alt="sqs.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312085/638339b1-3344-d823-3171-48b3c3f50323.png">


AWSのフルマネージドなメッセージキューイングサービス。
Lambdaで時間のかかる処理を非同期で並列処理するために採用しました。
この構成お手軽なのに強力すぎる...。

### AWS DynamoDB
<img width="100" alt="dynamodb.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312085/6f083e68-ee3f-6b3a-437f-6bf9bd3500c7.png">

フルマネージドなNoSQLデータベースを提供するAWSサービス。
LambdaのRDS Proxyを使って使い慣れてるRDSを使うという方法も検討したんですが、RDS Proxyが高かったんでDynamoDBにしました(貧困)
RDBMSと違って独特の制約が多くて設計に苦労しました。GSIサイッキョ！

でも後から取得したいクエリの種類が増えると、GSIの貼り直しが必要になったりしてつらみ。
早い段階での要件の洗い出しが大事だ。。。

### AWS Cognito
<img width="100" alt="cognito.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312085/257f113b-d301-627b-2be3-c406552a0749.png">

モバイル・Webアプリケーションでのユーザ作成・認証・管理機能をフルマネージドで提供するAWSサービス。
面倒なユーザ管理周りをスクラッチしたくなくて使いました。
分かったら便利だけど、やっぱり使い方が分かるまでのハードルが高いよ...
でもAPI Gatewayと認証の連携が簡単にできるのはAWSサービスで統一する良さを実感できました。

### AWS S3
<img width="100" alt="s3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312085/4b7a6c4a-a1ae-b29b-caa1-b3e1c9b3d125.png">

容量無制限のデータ保存ができるオブジェクト型ストレージを提供するサービス。
撮影したスクリーンショットを格納先として利用してます。

他にもWebページそのもののデプロイ先としてもAWS CloudFrontとセットで活用してます。

## その他

### AWS SAM
<img width="100" alt="awssam.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312085/e98a0dba-c90c-10f9-9a49-6ae483eb3d9b.jpeg">

テンプレートファイルに記載された通りにAWSリソースをデプロイするInfrastructure as Codeを実現してくれるサービス。
AWSマネジメントコンソールでポチポチしてデプロイするよりも遥かにデプロイとリソース管理が楽になったけれど、
最終的には800行ぐらいの巨大ファイルになってビルド・デプロイに時間がかかるのがストレスフルでした。

後発のAWS CDKを使ったらストレスフリーになったりするのかな。

# 作成期間
学習期間込みで大体週35〜40時間くらいで3ヶ月ちょっとくらい...。

# 課題
**・(UI・UXが)哀れ**
必要なクリック数、入力項目が多い。。。
実装する前にちゃんとしたワイヤーフレームを引いて、メインストリームの機能を利用するまでのクリック数などを検討すべきでした。

**・AWSサービスを絡めた開発のススメ方**
開発当初は毎回コードをAWSにデプロイしてた。。。(時間がかかりすぎる)
単体テストコードを書いてローカルで実行するのが最速？
localstackみたいなサービス公式から出して...
AWS SAMのテンプレートの管理がヤバい。800行ぐらい行ってる。AWS CDK使うべき。。。

他にも色々ありますが、キリがないので割愛。。。

# 最後に
やっぱり一つのサービスを全て一人で作るのは大変だけど、得るものも多い。。。
始める前はサーバレスSPAの実装ってまったくイメージがついてなかったんですが、今となっては動くものが作れてどこが良くなかったかという観点の振り返りまで出来るようになりました。
ただ有識者に聞いたら一発なところも手探りで進めていたのは非効率だなぁと思わずには居られない場面も多々ありました。
(React.js, AWSサービスの仕様・制約、効率的な開発環境などなど)

気軽に聞けるエンジニア仲間みたいな人がいたら、もっと詰まらずにいいものが出来てたんじゃないかと強く思ったので
今年の抱負として、**"えんじにあともだちをふやす"**  を掲げたいと思います。まる。

<font color="gray">その前に新しいお仕事が見つかるといいなぁ...(切実)</font>