Reactで作成したアプリをAWS CDKで簡単にデプロイする方法

# はじめに

Reactで作成したアプリをAWS環境へデプロイする際にAWS CDKで簡単にデプロイする方法をご紹介します。
ほかのSPA（AngularやVueなど）においても同様の手順になります。


# 前提

- AWS CLI環境
- Reactで作成したアプリ（ビルド済み）



# 環境

- aws cdk:2.8.0
- aws-cdk-lib:2.8.0
- typescript:3.9.7


# 準備

### AWS CDKのインストール

AWS CDKをインストールします。
AWS CDKについては以下を参照してください。

https://aws.amazon.com/jp/cdk/

```
npm i -g aws-cdk
```

### プロジェクトを作成します。
```
mkdir react-deploy（任意のディレクトリ名）
cd cdk-deploy
cdk init app --language=typescript
```

ディレクトリ構成は以下のようになります。
`react`ディレクトリを作成し、Reactアプリをビルドし作成される`build`ディレクトリを配置します。
```
.
├── README.md
├──react
      └── build　//reactアプリでビルドしたディレクトリを配置
├── bin        
      └── react-deploy-stack.ts　//ここに構築したいリソースを記述
├── cdk.context.json 
├── cdk.json         
├── cdk.out          
├── jest.config.js   
├── lib              
├── node_modules
├── package-lock.json
├── package.json
├── test
└── tsconfig.json

```

# デプロイ用のコードを記述

`react-deploy-stack.ts `に作成するリソースを定義します。

```typescript:react-deploy-stack.ts
import { RemovalPolicy, Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { aws_s3,aws_s3_deployment} from 'aws-cdk-lib';
import {aws_cloudfront,aws_cloudfront_origins,Duration} from 'aws-cdk-lib';

export class ReactDeployStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

      const buildBucket = new aws_s3.Bucket(this, "BuildBucket",{
        removalPolicy: RemovalPolicy.DESTROY,
        autoDeleteObjects: true,
      });

      const distribution = new aws_cloudfront.Distribution(this, "Distribution", {
        defaultBehavior: {
          // /buildを格納したバケットを指定する
          origin: new aws_cloudfront_origins.S3Origin(buildBucket),
          // HTTPS へリダイレクト
          viewerProtocolPolicy: aws_cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
        },
        // ルートへのアクセスに対して返却するコンテンツを設定
        defaultRootObject: "index.html",

        errorResponses: [
          {
            httpStatus: 403,
            responseHttpStatus: 200,
            responsePagePath: "/index.html",
            ttl: Duration.minutes(5),
          },
        ],
    });
    //デプロイするフォルダを指定
    new aws_s3_deployment.BucketDeployment(this,'deployreactApp',{
      sources: [aws_s3_deployment.Source.asset('./react/build')],
      destinationBucket: buildBucket,
    });
  }
}
```


ビルドしたReactアプリのディレクトリを配置するS3 バケットを配置します。

### S3バケットの作成部分について

`react-deploy-stack.ts `からS3バケット作成部分について抜粋したコードが以下になります。

```typescript:react-deploy-stack.ts
      const buildBucket = new aws_s3.Bucket(this, "BuildBucket",{
        removalPolicy: RemovalPolicy.DESTROY,
        autoDeleteObjects: true,
      });
```

バケット作成時に指定しているオプションについて説明します。
-  removalPolicy
    -  デフォルトでは削除ポリシーが`Retain`となっており、CloudFormationのスタックを削除してもバケットが残ります。`removalPolicy: RemovalPolicy.DESTROY`を指定することで、`cdk destory`でスタックとバケットを削除することが可能になります。
-  autoDeleteObjects
    -  バケット削除時にオブジェクトを削除するように指定してます。

### CloudFrontのエラーページ設定について

`react-deploy-stack.ts `の`errorResponses`部分を抜粋したコードが以下になります。

```typescript:react-deploy-stack.ts
     errorResponses: [
          {
            httpStatus: 403,
            responseHttpStatus: 200,
            responsePagePath: "/index.html",
            ttl: Duration.minutes(5),
          },
        ]
```

CloudFront にはステータスコードによりカスタムエラーページを返す機能があり、4xx や 5xxのレスポンスコードを返した場合にカスタムエラーページを返すことができます。
この設定を`errorResponses`で設定することができます。
SPAでは、どの画面においても、`index.html`により表示を行うため、「/」以外のURLでリロードした場合や「/」以外のURLに直接アクセスする際に403(access denied)エラーとなります。
403エラーの際に、`index.html`を返す設定を追加します。
詳細については以下の記事を参考にしてください。

https://dev.classmethod.jp/articles/s3-cloudfront-spa-angular-403-access-denied/



# デプロイ

AWS CDKでデプロイする際、最初の1回だけ、`cdk bootstrap`を実行します。
AWS CDKで初めてデプロイするときは、「Bootstrapスタック」を構築する必要があるため、このコマンドを実行します。このコマンドが正常に終了すれば、アプリをデプロイすることができます。

```
cdk bootstrap
```

デプロイします。

```
cdk deploy
```

デプロイ後、cloudFrontのオリジンにアクセスすると以下のような画面が表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2004450/b55f1a48-0ba5-716a-eef3-e6bd4c28d76d.png)

## アップロードしたバケットを消す

以下のコマンドでCloudFormationのスタックとS3バケットを消すことが可能です。
```
cdk destory
```


# 参考

https://dev.classmethod.jp/articles/aws-cdk-deploy-react/