【初心者向け】初めてのチャットアプリ作成で詰まったこと

##はじめに
AWSのサービスとGAS(Google Apps Script)を使用して以下のようなチャットアプリを作成しました！
（詳細については[【初心者向け】初めてのチャットアプリ作成〜AWSを使ったサバーレスアーキテクチャ〜](https://qiita.com/UpAllNight/items/6bd9886a892fe495fe8b)を参照してください）

[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)

例に漏れず、問題（そり立つ壁）にぶつかったのでメモとして残していきます。。

##目次
1.全体像について
2.問題点について
3.どうやって解決したか
4.まとめ

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

詳しい処理の説明は別の記事に記載しているため省きますが、各サービスの役割について簡単にまとめておきます。

　・GAS：画面から入力された日本語を英語に翻訳し、変換する
　・DynamoDB：画面から入力されたコメントを部屋の名前をキーとして保存する
　・Lambda：画面から入力されたコメントをDynamoDBへ保存する
　　　　　　　DynamoDBのコメントを画面に返却する
　・API Gateway：エンドポイントとして画面とLambdaをつなぐ
　・S3：静的Webサイトをホスティングする

以前作成したWebサイト「[Man in the Mirror](https://s3-ap-northeast-1.amazonaws.com/upallnightfirstproduct/inputName.html)」で使用したサービスはS3だけでしたが、
今回は複数のサービスを使用したため、サービス間の接続でいくつか問題が発生しました...
（エラーが発生しすぎてサ◯マ◯クカフェで発狂したのも今ではいいおもひで（泣））


次章ではその問題についてどう対応したのかを説明します。

##2.問題点について
特に以下の処理で苦労しました。
######(1)axiosによるGAS・Lambda呼び出しについて
　今回のアプリではGASとLmabdaを呼び出して以下のような処理をしています。
　①画面入力された日本語をGASに連携し、英語に翻訳する
　②GASから返却された英語をDynamoDBに登録する
　③DynamoDBに登録されているコメント履歴を取得し、画面に表示する

　以下はイメージ図
![imege1-Page-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378268/b035b98c-6245-8464-2c36-18d4b9c212bd.png)

　
　ですので以下のようにプログラミングしました。

```javascript:main.js

  // Lambda呼び出し用のAxiosオブジェクト
  const requestAws = axios.create({});

  // GAS呼び出し用のAxiosオブジェクト
  const requestGas = axios.create({});

　// 画面から入力されたコメント
　let comment = "";

　// GASにて翻訳したコメント
　let translatedComment = "";

  // 画面に表示するトーク履歴
  let commentList = new Array();

  // コメントNo（Lambda呼び出し時点でこのNoよりも最新のコメントを全て取得する）
  let commentNo = 0;

  // ルーム名（DynamoDBに格納するコメントリストのキーとして使用）
　let roomName = "[ルーム名]";

  /** Google Apps ScriptのAPI呼び出し処理(Get)
   *   リクエスト
   *     ・text: 画面入力されたコメント（comment）
   *     ・source: 編訳対象のコメントの言語（日本語の場合は"ja"）
   *     ・target: 翻訳語のコメントの言語（英語の場合は"en"）
   *   リザルト
   *     ・text: targetの言語に翻訳されたtext
   **/
   requestGas
    .get("[GASのURL]" + "?text=" + comment + "&source=ja&target=en")
    .then(function(response) {
      // リザルトのtextを格納する
      translatedComment = response.data;
　　});

  /** AWS LambdaのAPI呼び出し処理(Post)
   *   リクエスト
   *     ・roomName: 画面入力されたルーム名
   *     ・comment: 英語翻訳されたコメント（translatedComment）
   *     ・commentNo: 現在表示されているコメントの最新No
   *   リザルト
   *     ・commentList: ルーム名に紐付くコメントリスト
   *      （commemtNo以降のコメントリスト）
   **/
   requestAws
    .post("[API Gatewayのエンドポイント]", {
      roomName: roomName,
      comment: translatedComment,
      commentNo: commentNo
    })
    .then(function(response) {
      // リザルトのcommentListを格納する
      commentList = response.data;
      // コメントリストの画面表示処理呼び出し
      showCommentList(commentList);
   });

```

　各サービスの呼び出し処理は`axios`を使用しています。
　`axios`の説明、使い方については以下のサイトのご説明を参考とさせていただきました。
　　・[[フロントエンド] axiosライブラリを使って、柔軟にHTTP通信を行う](https://www.yoheim.net/blog.php?q=20170801)
　　・[[axios] axios の導入と簡単な使い方](https://qiita.com/ksh-fthr/items/2daaaf3a15c4c11956e9)

　まず、画面から入力されたコメントを`comment`に格納し、GASにて英語に翻訳します。

　次に、GASの処理で翻訳したコメントを`translatedComment`に格納し、
　`roomName`・`commentNo`と共にLambdaに連携します。

　そして、Lambdaにて連携された`translatedComment`を`roomName`をキーとして
　DynamoDBに保存し、`commentNo`より最新のコメントを`commentList`に格納し、
　返却します。

　最後に、返却された`commentList`を画面に表示します。

　その実行結果がこちらです！！

![スクリーンショット 2019-06-11 21.48.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378268/fc6e7952-915f-f839-a6c1-9f119e0471af.png)
　
　**何も表示されないやん・・・**

　こちらの問題は`axios`について理解が不足していたことが原因でした...
　詳しくは次章で説明します。
######(2)S3・API GatewayのCORS設定について
　今回のチャットアプリは、前回作成したWebサイトと同様に
　S3のホスティング機能を使用しました。

　AWSのサービス間のイメージとしては以下の通り

![imege2-Page-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378268/3b3e3fee-f4e4-533e-4cf6-7b1fe70754a7.png)

　LambdaやGASを含めた全ての実装が完了し、ローカル環境でテストを実施して
　問題ないことが確認できたので、いよいよS3にアップロード！！

　無事アップロードが完了したため、S3から呼び出してみたところ
　「access-control-allow-origin」に関連するエラーが複数発生していた...
  

##3.どうやって解決したか

######(1)axiosによるGAS・Lambda呼び出し の解決について

原因はaxiosについての理解不足でした。

axiosはPromiseベースです。
Promiseについては以下のサイトをご参照願います。

[Promiseによる非同期処理[resolve][reject][Promise.all]](https://noumenon-th.net/programming/2018/12/14/promise1/)

axiosでは処理（ここではGAS・Lambda呼び出し処理）の結果が正常に終了した場合、`then`以降の処理が実行されます。

つまり
①GAS呼び出し　が正常に終了した後に　②Lambda呼び出し　を実行する必要があるため、
①GAS呼び出しの`then`メソッド内で　②Lambda呼び出し　を実行すれば良いと考えました。

そして、修正した処理がこちらです。

```javascript:main.js

  // Lambda呼び出し用のAxiosオブジェクト
  const requestAws = axios.create({});

  // GAS呼び出し用のAxiosオブジェクト
  const requestGas = axios.create({});

　// 画面から入力されたコメント
　let comment = "";

　// GASにて翻訳したコメント
　let translatedComment = "";

  // 画面に表示するトーク履歴
  let commentList = new Array();

  // コメントNo（Lambda呼び出し時点でこのNoよりも最新のコメントを全て取得する）
  let commentNo = 0;

  // ルーム名（DynamoDBに格納するコメントリストのキーとして使用）
　let roomName = "[ルーム名]";

  /** Google Apps ScriptのAPI呼び出し処理(Get)
   *   リクエスト
   *     ・text: 画面入力されたコメント（comment）
   *     ・source: 編訳対象のコメントの言語（日本語の場合は"ja"）
   *     ・target: 翻訳語のコメントの言語（英語の場合は"en"）
   *   リザルト
   *     ・text: targetの言語に翻訳されたtext
   **/
   requestGas
    .get("[GASのURL]" + "?text=" + comment + "&source=ja&target=en")
    .then(function(response) {
      // リザルトのtextを格納する
      translatedComment = response.data;

      /** AWS LambdaのAPI呼び出し処理(Post)
       *   リクエスト
       *     ・roomName: 画面入力されたルーム名
       *     ・comment: 英語翻訳されたコメント（translatedComment）
       *     ・commentNo: 現在表示されているコメントの最新No
       *   リザルト
       *     ・commentList: ルーム名に紐付くコメントリスト
       *      （commemtNo以降のコメントリスト）
       **/
       requestAws
        .post("[API Gatewayのエンドポイント]", {
          roomName: roomName,
          comment: translatedComment,
          commentNo: commentNo
        })
        .then(function(response) {
          // リザルトのcommentListを格納する
          commentList = response.data;
          // コメントリストの画面表示処理呼び出し
          showCommentList(commentList);
       });
   });
```

そして、実行した結果！！
![スクリーンショット 2019-06-13 8.39.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378268/10e6a6eb-3a8d-41ca-a920-9850f52e373a.png)

**画面から入力した「こんにちは」を英語に翻訳し、「HELLO」と表示できました！！！**

######(2)S3・API GatewayのCORS設定 の解決について

すでに表題で書いていますが、S3とAPI GatewayのCORS設定がされていなかったことが原因でした。

CORSについては以下の記事にて詳細に記載されております。
[CORSまとめ](https://qiita.com/tomoyukilabs/items/81698edd5812ff6acb34)

今回はS3にHTML、Javascriptをアップロードしています。
そのため、S3以外へのHTTP通信（Lambda等の他のWebサービス呼び出す）は
同一オリジンポリシーに反するため、エラーが発生していました。

この問題を解決するためにCORSの設定が必要となります。

CORSの設定は以下の通り。

***S3***
![スクリーンショット 2019-06-13 21.50.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378268/3295f604-9a83-d238-b714-7ea619097c16.png)

設定値については以下の通り

**・AllowedOrigin**
　クロスドメインリクエストを許可するオリジンを指定します ( http://www.example.com など)
　今回は`*`を指定して、Lambda等からクロスオリジンリクエストを送信できるようにしました。

**・AllowedMethod**
　どのHTTPリクエストメソッドを許可するのかを設定できます。
　今回はGET通信とPOST通信を使用するため、それぞれ設定しています。

**・AllowedHeader**
　どのHTTPリクエストヘッダを許可するのかを設定できます。
　こちらで設定されているヘッダのみをレスポンス時に設定できます。
　今回は`*`を設定しました。

***API Gateway***
![スクリーンショット 2019-06-13 22.40.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378268/33da8170-6f0b-7926-d284-ee428d212003.png)

詳しい設定方法は[公式ドキュメント](https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/how-to-cors.html)をご参照ください。

設定値については以下の通り(全てデフォルトの設定です)

**・Access-Control-Allow-Methods**
　リソースへアクセスできるHTTPリクエストメソッドを指定します。

**・Access-Control-Allow-Headers**
　どのHTTPリクエストヘッダを許可するのかを設定できます。
　
**・Access-Control-Allow-Origin**
　どのHTTPリクエストオリジンを許可できるのかを設定できます。

**ちなみにAPI Gatewayの設定を行った後は必ずデプロイしてください..**
（私はデプロイするのを忘れていたため、1時間無駄にしました...）


##4.まとめ

今回も様々な問題にぶつかりましたが、なんとか2つ目の実装が完了しました。
また、今回の問題解決にはQuitaのたくさんの投稿者皆様の記事を参考にさせていただきました。
この場を借りてお礼申し上げます。