【初心者向け】初めてのチャットアプリ作成〜AWSを使ったサーバーレスアーキテクチャ〜

##はじめに
AWSのサービスを使ったチャットアプリを作成したので、
やったことを備忘録として残します。

##作った内容
投稿した内容が英語に翻訳され、ネオンのアニメーションで順次表示されるアプリ

既存のチャットツールには利用面では勝てないと判断し、眺めて楽しむをメインに、
**SPA**・**サーバーレス**・**実質無料**
上記を考慮したサービスを作りました。

###全体像イメージ
![Untitled Diagram-4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378268/f1868ace-d767-a76c-2a82-ec3a410d785d.png)

###各機能の役割

####S3
クライアントからのリクエストを受けて、Webページの返却を行う(Webサーバの役割)

####HTML
チャットルームの選択画面・チャットルーム画面で構成されている。
※SPAで構成されているため、HTMLファイルとしては1つ

####JS
ユーザー入力などのイベントを受け、各種WebAPIのアクセス・DOMの操作を行う。
大体の機能は以下のようになっている。

1.チャットルームの選択画面上でルームが選択された場合、
DOMの操作を行い、チャットルーム画面を表示する。
2.APIGatewayにアクセスを行い、過去のチャットの投稿内容を取得・表示を行う。
3.ユーザーがチャットルーム画面でメッセージを投稿した場合、
GASにアクセスを行い、投稿内容の英語翻訳を行う。
また、APIGatewayにアクセスを行い、英語翻訳をした内容の投稿、最新のチャットの投稿内容を取得を行い、結果を画面表示する。

###GAS
英語翻訳を行うAPIを担当

#### APIGateway・lambda・DynamoDB
チャット投稿内容の保存・取得を行う

##作り方
以下の手順で順次開発を行う

1.Mockを使った画面イメージの作成
2.作りたい要件・仕様の決定
3.開発
　1).HTMLの作成
　2).JSの作成
　3).APIGateway・Lambdaの設定
　4).Lambda(Python3)のスクリプト作成
4.公開


###Mockを使った画面イメージの作成
とりあえずのHTMLとJSのみでイメージを作ってみる。

・実行前
![before.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378268/676588d1-cab7-862a-6f52-79578ce1023e.jpeg)
・実行後
![after.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378268/20dfaba4-93cd-17f9-23b2-2999c38c0efb.jpeg)

####見えてきた課題
1.文字が重なる
→表示場所・文字の大きさをランダムとしているため(一旦、許容にする)

2.2回目を押した場合、同一の投稿内容が表示される
→コメントの表示を何かしらのロジックを組んで制御する

3.画面表示中にボタンクリック可能
→DOM操作中にボタンの無効化をする

4.画面に表示しているコメント量が多いと画面が重くなる
→各コメントにinfinitのアニメーションを付与しているため、表示コメント量を制限する

###作りたい要件・仕様の決定
Mockを作るとある程度の完成予想が見えてくる。(同時に課題も。。。)
具体的に決める仕様はAPIとJS間のインターフェイスをしっかり考える


```仕様書1.txt
概要：ルーム検索画面でボタンが押された際に呼び出しされる。
　　　入力のルーム名を元に検索を行い、結果を返却する。

【インターフェイス】
・リクエスト
	1.ルーム名(roomName)
		概要：必須項目(JS側でチェックを行う)、ユーザー指定のルーム名、DBのキーになる

・リザルト
	1.コメントリスト(commentList)
		概要：ルーム名に紐付くコメントがリスト構造で返却される。
				  情報がない場合でも、0件のリスト構造で返却する。
				  レコードイメージは、"コメント番号:コメント"のフォーマットになっている
				  ex).{“1:こんにちは”,2:”元気?”}


【処理】
1.DBの読み込み
リクエスト.roomNameをキーにDynamoDBに検索を行う。

2.DB結果判定
DBの取得結果によって、以下の処理を行う。

	1).取得結果=0件の場合
	新規のルーム入室処理のパターン
	投稿コメントが存在しないため、空のリストを呼び出し元に返却する。
	※DBへのレコード登録は行わない。

	2).取得結果=1件の場合
	既存のルーム入室処理のパターン

		(1).取得情報整形
		取得結果.コメントのカラムは”,”(カンマ)で区切られた情報が入っている。
		そのため、”,”(カンマ)で情報を区切りリスト形式に変換する。
		※取得結果.コメントのイメージ
		“1:こんにちは,2:私は元気です,3:好きな食べ物は?”

		(2).結果返却
		上記(1)で作成したリストをを呼び出し元に返却する。
```

```仕様書2.txt
概要：トーク画面でボタンが押された際に呼び出しされる。
　　　情報の更新と入力のルーム名に紐づく結果の返却を行う。
		  また、投稿コメントが入力されていなかった場合は、
		  情報の更新は行わず、入力のルーム名に紐づく結果の返却を行う。

【インターフェイス】
・リクエスト
	1.ルーム名(roomName)
		概要：必須項目(JS側でチェックを行う)、ユーザー指定のルーム名、DBのキーになる

	2.投稿コメント(comment)
		概要：任意項目、ユーザーが入力したコメント(日本語)をGASによって英語翻訳された内容
	   		  最初のルーム入室処理、または投稿内容の取得のみを行う場合は空文字が入力される

	3.最新コメント番号(commentNo)
		概要：必須項目、クライアント上で表示している一番新しいコメント番号
			  画面上表示コメントがない場合"0"が渡される。

・リザルト
	1.コメントリスト(commentList)
		概要：リクエスト.最新コメント番号以降のコメントがリスト構造で返却される。
				  情報がない場合でも、0件のリスト構造で返却する。
				  レコードイメージは、"コメント番号:コメント"のフォーマットになっている
			 	  ex).{“1:こんにちは”,2:”元気?”}


【処理】
1.DBの読み込み
リクエスト.roomNameをキーにDynamoDBに検索を行う。

2.DB結果判定
DBの取得結果、リクエスト.commentの値によって、以下の処理を行う。

	1).取得結果=0件かつリクエスト.comment=“”(空文字)の場合
	新規ルームに対するコメント取得処理のパターン
	投稿コメントも存在しないため、処理は行わず空のリストを呼び出し元に返却する。
	※DBへのレコード作成処理は2)で行うため不要

	2).取得結果=0件かつリクエスト.comment=“”(空文字)以外の場合
	新規ルームに対するコメント投稿処理のパターン
	DBへレコードのインサート結果返却の処理を順に行う。

		(1).DBの登録(Insert)
		以下の値でDBの登録処理を行う。
		roomName：リクエスト.roomNameの値を設定
		comment：リクエスト.commentの値を整形して設定
			編集仕様：“1:”+リクエスト.comment

		(2).結果の返却
		上記(1)でDBに登録したcommentの内容(“1:”+リクエスト.comment)をリザルトのリストに設定し、呼び出し元に返却する。

	3).取得結果=1件かつリクエスト.comment=“”(空文字)の場合
	既存ルームに対するコメント取得処理のパターン
	
		(1).取得情報整形
		取得結果.コメントのカラムは”,”(カンマ)で区切られた情報が入っている。
		そのため、”,”(カンマ)で情報を区切りリスト形式に変換する。
		※取得結果.コメントのイメージ
		“1:こんにちは,2:私は元気です,3:好きな食べ物は?”

		(2).返却情報の選別
		上記(1)で作成したリスト(取得コメントリスト)に対し、画面に返却したい情報のみをリストの残す。
		そのため、結果格納用リストを生成し、以下をリストの件数分繰り返す。(繰り返し変数:i)

			a.取得コメント番号取得
			取得コメントリスト[i]は”:”(コロン)で区切られ、前方部に取得コメント番号が設定されている。
			その情報を一時変数.取得コメント番号に格納する
			ex)”1:こんにちは”
			
			b.必要情報判定
			一時変数.取得コメント番号 > リクエスト.最新コメント番号の場合、
			(2).結果格納用リストに取得コメントリスト[i]を追加する。

		(3).結果の返却
		上記(2)で生成した結果格納用リストを呼び出し元に返却する。
		※判定の結果が0件の場合でも、リストは生成済みのため、要件通り空のリストが返却される。
		

	4).取得結果=1件かつリクエスト.comment=“”(空文字)以外の場合
	既存ルームに対するコメント投稿処理のパターン

		(1).取得情報整形
		取得結果.コメントのカラムは”,”(カンマ)で区切られた情報が入っている。
		そのため、”,”(カンマ)で情報を区切りリスト形式に変換する。
		※取得結果.コメントのイメージ
		“1:こんにちは,2:私は元気です,3:好きな食べ物は?”

		(2).DBの更新
			
			a.最新コメント番号取得
			上記(1)で作成したリスト(取得コメントリスト)の末尾を取得し、
			”:”(コロン)で区切られた前方部の取得コメント番号を取得する。

			b.DB更新
			以下の値でDBの登録処理を行う。
			roomName：リクエスト.roomNameの値を設定
			comment：取得結果.コメントの値を整形して設定
			編集仕様：取得結果.コメント + “,” + 上記aで取得した最新コメント番号取得を1カウントアップした値 + “:” + リクエスト.comment
			※DBから取得した値にカンマ区切りの情報を付与するイメージ

			c.取得コメントリスト修正
			上記bで作成した以下の情報をリストに追加する。
			追加レコード値：
			上記aで取得した最新コメント番号取得を1カウントアップした値 + “:” + リクエスト.comment
		
		(3).返却情報の選別
		取得コメントリストに対し、画面に返却したい情報のみをリストの残す。
		そのため、結果格納用リストを生成し、以下をリストの件数分繰り返す。(繰り返し変数:i)

			a.取得コメント番号取得
			取得コメントリスト[i]は”:”(コロン)で区切られ、前方部に取得コメント番号が設定されている。
			その情報を一時変数.取得コメント番号に格納する
			ex)”1:こんにちは”
			
			b.必要情報判定
			一時変数.取得コメント番号 > リクエスト.最新コメント番号の場合、
			(3).結果格納用リストに取得コメントリスト[i]を追加する。

		(4).結果の返却
		上記(3)で生成した結果格納用リストを呼び出し元に返却する。
		※判定の結果が0件の場合でも、リストは生成済みのため、要件通り空のリストが返却される。
```

チームでの開発のため、こうしたメモ帳ベースの仕様書があるだけで認識の相違は大分減ります。
(ロジックの質の良し悪しは別として。。。)

###APIGateway・lambdaの設定
とりあえず、小さい機能から作るをモットーにとりあえず動くものを作るべし！
細かい設定は別記事へ
[【AWS】APIGateway・Lambda動作の流れをわかりやすく解説 - Qiita](https://qiita.com/UpAllNight/items/8a347be69db7653657d0)


###lambdaでの実装
PythonやDynamoDBの使用は初めてのため、大分苦戦しました。。。
上記同様調べたことは、内容が長くなるため別記事に。

[【初心者向け】JavaGoldが解説するPythonの基礎 パート１ - Qiita](https://qiita.com/UpAllNight/items/dea9543741622a03bb13)
[【初心者向け】JavaGoldが解説するPythonの基礎 パート2 - Qiita](https://qiita.com/UpAllNight/items/1b25dd008cd0755e934a)
[【初心者向け】DynamoDBを理解する〜Pythonを用いた取得パターン特化編〜 - Qiita](https://qiita.com/UpAllNight/items/a15367ca883ad4588c05)

##なんやかんやあって出来たもの
[Free Talk](https://s3-ap-northeast-1.amazonaws.com/upallnightsecondproduct/index.html)
![login.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378268/4cf80a49-1b94-e89f-4a6c-b8da7e729858.jpeg)

~~最初からわかっていたけど、実用性皆無。。。~~
##まとめ
とりあえず、AWSのAPIGateway・lambda・DynamoDBの使用は一通りできた。
エラー起こりまくったけど、**自分で手を動かすのってホント大事**