[Vue.js×AWS] Vue.js製SPAをAWSにサーバレスなアプリケーションとしてデプロイする

# はじめに

自社で開発している「IoT.kyoto VIS」 というIoTデバイスのデータを可視化するWebアプリケーションをリニューアルするにあたり、アーキテクチャを思い切ってサーバレスにして、フロントサイドはVue.js(以下Vue)でSPAにしました。
ここでは、SPAをサーバレスアーキテクチャで構築した際の気をつけないといけないところを、私なりにまとめて書いていこうと思います。

## IoT.kyotoVISとは？

IoTデバイスのデータをリアルタイムでグラフ化するWebアプリです。
AWSのDynamoDBに蓄積されたデバイスデータをグラフに可視化し、一定の更新間隔でグラフを自動更新することができます。
デバイスのデータが蓄積されているAWSアカウントのDynamoDBRead権限が付与されたCredentialを登録することで、アプリケーションが立っているAWSアカウントから、アカウントをまたいでデータを取得します。

## 結果どんなアーキテクチャになったか

こういうアーキテクチャが

![image.png](https://qiita-image-store.s3.amazonaws.com/0/95875/8fb86dd0-6c21-3675-ee50-058aad1e237a.png)

こうなった！

![image.png](https://qiita-image-store.s3.amazonaws.com/0/95875/d3d10688-5fc5-d5d4-3a39-1eb5ecaab7c9.png)

## 何をしたか

1. VueでビルドしたアプリケーションをEC2ではなくてS3に配置した
2. ログイン関係をAmazonCognitoを利用するようにした
3. 今までサーバサイド(EC2)で行っていたNode.jsの処理をLambdaに移管し、APIGateway経由で処理するようにした

結果 → **サーバレスなアーキテクチャ** になった！！！

## 利用しているサービス

| サービス名 | 簡単な説明　              　|
|--------------------------|------------------------|
| Route53　　　　　　　　 | DNSのサービス。ドメインの向き先をCloudFrontに設定する |
| CloudFront　　　　　　　　　　　 | CDNのサービス。コンテンツをキャッシュするができるが、今回はキャッシュを全くせずに、SSL証明書を持たせるだけ |
| ACM | AWSが提供するSSL証明書 |
| S3 | かなり安いストレージを提供するサービス。ビルドしたソースコード置き場 |
| Cognito | 認証処理をサーバレスで提供してくれる |
| APIGateway | APIのエンドポイントを用意してくれるサービス。Lambdaなどのサービスに連携ができる。今回は主にLambdaを実行 |
| Lambda | 予めデプロイされた処理をキックされる度に実行できるサービス。便利屋さん |
| DynamoDB | NoSQLのデータベース。RDBと違って激しいInputに強いので、センサーデータやログデータを貯めるのに適している |

## 開発していて楽になったこと

### 1. jQueryからの開放

Vueを利用することで、jQueryを使わなくてもよくなりました。DOM操作を内部的に行ってくれるのでJS側で書くことなく、Vueの書き方でストレスなくDOM操作をすることができるようになります。これに関しては、[jQueryのDOM操作に疲れたあなた！そんなあなたにVue.js！](https://qiita.com/is_ryo/items/b918b936509c25a500fe)に書いてますので、ぜひ御覧ください！

### 2. アプリケーションのデプロイ

S3にビルドしたソースコードを置くだけで、アプリケーションをデプロイ(といっていいのか？)できるので、一番簡単にしようと思ったらAWS-CLIでポチってやればデプロイできる。

### 3. Node.jsでの処理のデプロイ

以前まではEC2上でNode.jsを動かしていたので、関係のないNode.jsの処理や、デプロイするとなるとその上で一緒に動いているアプリケーションまでデプロイがかかっていました。
ですが今回は[ServerlessFramework](https://serverless.com)を使ってデプロイするようにしたので、Lambda単位で、しかもコマンド一発でデプロイすることができるようになったので、Node.jsのデプロイ周りが楽になった。

## 注意点

### 画面のリロードへの対処

#### 問題

個人的にSPAは画面のリロードに弱いと感じています。
今回のアプリケーションでは、ログイン時にCognitoからtokenを取得して、Vuex.StoreにCommitして、APIGatewayにリクエストを送信する際にHeaderに埋め込んでいます。正しいtokenが渡ってきたときのみLambdaをキックするためです。
ただ画面のリロードをされてしまうと、Vuex.Storeの中身がリセットされてしまうため、tokenを取得し直さないといけません。
このようにVuex.Storeにリセットされると困る情報が入っている場合への対処が必要です。

#### 対処

Vuex.StoreではなくLocalStorageに保存するようにしました。LocalStorageに保存することで画面のリロードをされても保持し続けることができます。ただし、明示的にclearしないといけないので、その点に関しては注意が必要です。

### S3においたアプリケーションから403が返ってくる問題

#### 問題

SPAでは擬似的にpathを切って画面(Component)を切り替えます。
なので。 `hogehoge.com/` も `hogehoge.com/fuga` もS3に配置した同じ `index.html` を取得しないといけません。
ですが何も対処もしないと、 `hogehoge.com/` は `index.html` を取得しますが、 `hogehoge.com/fuga` は `fuga/index.html` を取得しようとします。もちろんそのようなファイルはないので403が返ってくるわけです。

#### 対処

S3の前段にいるCloudFrontで403の場合に `index.html` を取得するように設定することができます。

<img width="1155" alt="AWS CloudFront Management Console 2018-08-31 16-07-53.png" src="https://qiita-image-store.s3.amazonaws.com/0/95875/5440cd61-d105-7d3a-dfb0-4ffd5ab09e81.png">

これで、403または404がCloudFrontに帰ってきたときは、 `index.html` を取得して返すようになりました。
ただし、エラーページをVueRouterなどで用意して返してあげないといけないので注意が必要です。
例えば404になるはずの `hogehoge.com/foooo` でも `index.html` を取得して、routerを参照してComponentを切り替えようとするのでエラーになるはずです。その場合の処理を明示的に記述する必要があります。

```ts

Vue.use(Router);

const router = new Router({
  mode: "history",
  routes: [
    // routerにないpathを指定したらSignInのComponentに切り替える
    {
      path: "*",
      redirect: {
        name: "SignIn"
      }
    }
  ]
});
```

## さいごに

こうしてサーバレスなアプリケーションを構築することができました。
前の記事と合わせて、Webアプリケーションを開発するにあたって苦しかったところが、かなり改善され幸せを手に入れることができました。
VueはAngularやReactに比べると入門しやすいと言われているので、フロントサイドを開発されている方はぜひ一回触ってみるといいかもしれませんね。

ではまた！