【Rails+React】アプリをサーバレスデプロイしたい

# はじめに
これまでに投稿した記事の続きです。フロントエンドとバックエンドを別にしてデプロイしてきましたが、最終的にまとめてデプロイしました。詰まったことも併せて簡単にまとめてみます。

https://qiita.com/does_not_exist/items/b51009611bebb13aeb2f

https://qiita.com/does_not_exist/items/a130a157a92fe26a3981

# 詰まったポイント
## ①フロントとAPI Gatewayの疎通
PostmanからAPI Gatewayにパラメータを送信してデータ操作ができることは確認済みなので、あとはフロントとAPI Gatewayの疎通を確認します。

そのためにまずはフロントでaxiosのアクセス先がAPI Gatewayのエンドポイントになっていることを確認します。
```tsx:

  const getTodos = async () => {
    try {
      const res = await axios.get('https://hogehoge/dev/todos');
      const backendResponse: Response = res.data;
      if (backendResponse.status === 200) {
        setTodos(backendResponse.todos);
      } else {
        console.error('Failed to get todos:', backendResponse);
      }
    } catch (error) {
      console.error('Failed to get todos:', error);
    }
  };

```

## ②バックエンドのcors設定

```ruby:lambda.rb
        headers.merge!({
          'Access-Control-Allow-Origin': 'https://hogehoge.cloudfront.net',
          'Access-Control-Allow-Credentials': true,
          'Access-Control-Expose-Headers': 'Content-Disposition,X-Suggested-Filename',
          'Content-Security-Policy': "default-src 'self'"
        })
```

## ③serverless.ymlのcors設定

```yaml:
functions:
  app:
    events:
      - http:
          path: /
          method: ANY
          cors: &cors
            origin: https://hogehoge.cloudfront.net
```

## ④デプロイ前にフロントはビルドする
これを忘れていてしばらく時間を費やしました。axios箇所にエンドポイントを指定してもFailed to load resource: net::ERR_CONNECTION_REFUSEDというエラーが発生します。これはビルドしていないために変更点が反映されずlocalhostにアクセスしているということが原因でした…。　

# 動作確認

画面にアクセスしたとき

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2889192/f07e5bf1-1d2b-92d6-f3b6-6a4ceecb1ac1.png)

適当にToDoをつくります

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2889192/052cde49-d519-2449-23c3-276fc046f990.png)

画面でデータ操作できることが確認できました！
DynamoDBにも操作が反映されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2889192/decc547f-1684-1b96-24ba-a17222badfc6.png)



# 最終的なserverless.yml
```yaml:
service: qiita-test

frameworkVersion: '3'

provider:
  name: aws
  runtime: ruby2.7
  timeout: 300

  region: ap-northeast-1
  deploymentBucket:
    name: qiita-test-bucket
  iamRoleStatements:
    - Effect: Allow
      Action:
        - dynamodb:*
      Resource:
        - arn:aws:dynamodb:ap-northeast-1:${aws:accountId}:table/*
    - Effect: 'Allow'
      Action:
        - s3:GetObject
        - s3:PutObject
        - s3:ListBucket
        - s3:DeleteObject
        - s3:GetObjectTagging
        - s3:PutBucketPolicy
      Resource:
        - arn:aws:s3:::${Bucket}
        - arn:aws:s3:::${Bucket}/*

  ecr:
    scanOnPush: true
    images:
      lambda_docker:
        path: ./backend

custom:
  deploymentBucket:
    blockPublicAccess: true
  s3Sync:
    - bucketName: ${self:service}-dev
      localDir: reactapp/build

functions:
  app:
    timeout: 30
    image:
      name: lambda_docker
    events:
      - http:
          path: /
          method: ANY
          cors: &cors
            origin: https://hogehoge.cloudfront.net
            headers:
              - Content-Type
              - X-Amz-Date
              - Authorization
              - X-Api-Key
              - X-Amz-Security-Token
              - X-Amz-User-Agent
              - Access-Control-Allow-Headers
              - Access-Control-Allow-Origin
              - Time-Zone
              - Cache-Control
            allowCredentials: false

      - http:
          path: /{proxy+}
          method: ANY
          cors: *cors

resources:
  Resources:
    Bucket:
      Type: AWS::S3::Bucket
      Properties:
        BucketName: ${self:service}-dev
        WebsiteConfiguration:
          IndexDocument: index.html
        PublicAccessBlockConfiguration:
          BlockPublicAcls: true
          BlockPublicPolicy: true
          IgnorePublicAcls: true
          RestrictPublicBuckets: true
        CorsConfiguration:
          CorsRules:
            - AllowedMethods:
                - GET
                - HEAD
              AllowedOrigins:
                - '*'
    BucketPolicy:
      Type: AWS::S3::BucketPolicy
      Properties:
        Bucket: !Ref Bucket
        PolicyDocument:
          Statement:
            - Action:
                - s3:GetObject
              Effect: Allow
              Resource: !Sub arn:aws:s3:::${Bucket}/*
              Principal:
                Service: cloudfront.amazonaws.com
              Condition:
                StringEquals:
                  AWS:SourceArn: !Sub arn:aws:cloudfront::${aws:accountId}:distribution/${Distribution}    
    Distribution:
      Type: AWS::CloudFront::Distribution
      Properties:
        DistributionConfig:
          DefaultCacheBehavior:
            TargetOriginId: ${self:service}-dev
            ForwardedValues:
              QueryString: false
            ViewerProtocolPolicy: https-only
          Enabled: true
          DefaultRootObject: index.html
          Origins:
            -  Id: ${self:service}-dev
               DomainName: !GetAtt Bucket.RegionalDomainName
               S3OriginConfig:
                OriginAccessIdentity: ''
               OriginAccessControlId: !GetAtt CloudFrontOriginAccessControl.Id

    CloudFrontOriginAccessControl:
      Type: AWS::CloudFront::OriginAccessControl
      Properties: 
        OriginAccessControlConfig:
          Description: Default Origin Access Control
          Name: !Ref AWS::StackName
          OriginAccessControlOriginType: s3
          SigningBehavior: always
          SigningProtocol: sigv4
  Outputs:
    URL:
      Value: !Sub "https://${Distribution.DomainName}"
    DistributionId:
      Value: !Ref Distribution

plugins:
  - serverless-deployment-bucket
  - serverless-s3-sync
```

# おわりに
ようやく一区切りつきました。今回は予めタスクを作って時間を測り、再現性を高める試みをしていたのですが、デプロイ周りで色々詰まってしまいました。次はアプリの内容にこだわってみたいです。まだまだ学ぶことが多いので頑張ります。