AWS CDKで作成したCloudFrontとS3にReactアプリケーションをデプロイしてみた

AWS CDKで作成したCloudFrontとS3にReactアプリケーションをデプロイしてみた

Clock Icon2025.05.04

こんにちは!製造ビジネステクノロジー部の小林です。

今回は、AWS CDKを使って、ReactアプリケーションをCloudFrontとS3を組み合わせたインフラにデプロイする方法をご紹介します。私は前職でAngularをEC2で使用していましたが、今回初めてS3でフロントエンドアプリケーションを実行するので楽しみです!

この構成は、静的Webサイトのホスティングとして人気ですね。EC2ではなくS3を使用することで、下記のようなメリットがあります。

  • サーバー管理が不要になり、運用コストの削減
  • CloudFrontと組み合わせることで、グローバルな高速配信
  • AWS WAFをアタッチして、悪意のある攻撃から保護

それでは早速やってみましょう!

ディレクトリ構成

下記のディレクトリ構成になります。

REACT-HOST-S3
├── infrastructure
│   ├── bin
│   │   └── infrastructure.ts
│   ├── cdk.json
│   ├── jest.config.js
│   ├── lib
│   │   └── infrastructure-stack.ts
│   ├── package-lock.json
│   ├── package.json
│   ├── test
│   └── tsconfig.json
└── react-app
    ├── public
    ├── src
    ├── .gitignore
    ├── package-lock.json
    ├── package.json
    └── tsconfig.json

Reactアプリケーションをクローン

まずは下記のリポジトリにある、完成したReactアプリケーションをローカルにクローンします。
https://github.com/shomakobayashi/react-host-s3

クローン後、下記のコマンドを実行します。

# react-appディレクトリに移動
cd react-app

# 依存パッケージをインストール
npm install

# ビルドを実行(本番用ファイルを生成)
npm run build

# 開発サーバーを起動し、ローカルでReactが起動するか確認します
npm run start

コマンド実行後、localhost:3000にアクセスするとReactのロゴが表示されます。綺麗なロゴですね。
ローカルでReactが起動することが確認できたら、次はインフラを構築します。
スクリーンショット 2025-05-04 0.07.32

AWS CDKでS3とCloudFrontを構築する

先ほどクローンしたリポジトリ内に、CDKでインフラを構築するための準備をします。

CDKプロジェクトの初期化

まずはCDKプロジェクト用のディレクトリを作成し、初期化します。

# インフラ用のディレクトリを作成
mkdir infrastructure

# 作成したディレクトリに移動
cd infrastructure

# CDKプロジェクトを初期化(TypeScript使用)
cdk init app --language typescript

S3バケットの構築

以下のパラメータでS3バケットを構築します。

  • スタック削除時にバケットも自動削除
  • バケット内のオブジェクトも自動削除
  • パブリックアクセスをブロック
lib/infrastructure.ts
// S3バケットの作成
    const websiteBucket = new s3.Bucket(this, 'react-s3-host', {
      // スタック削除時にバケットも削除
      removalPolicy: cdk.RemovalPolicy.DESTROY,
      // バケット内のオブジェクトも自動削除
      autoDeleteObjects: true,
      // 直接アクセスをブロック
      blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL
    });

CloudFrontディストリビューションの構築

次に、下記の設定でCloudFrontディストリビューションを構築します。

  • S3バケットをオリジンとして設定
  • HTTPSへのリダイレクトを強制
  • パフォーマンス最適化のためのキャッシュ設定
  • デフォルトルートをindex.htmlに設定
lib/infrastructure.ts
// CloudFrontディストリビューションの作成
    const distribution = new cloudfront.Distribution(this, 'Distribution', {
      defaultBehavior: {
        origin: new origins.S3Origin(websiteBucket),
        // HTTPSへのリダイレクト
        viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
        // キャッシュ設定
        cachePolicy: cloudfront.CachePolicy.CACHING_OPTIMIZED,
      },
      // デフォルトルート
      defaultRootObject: 'index.html'
    });

    // Reactアプリのビルドパスを指定
    const reactBuildPath = path.join(__dirname, '../../react-app/build');

    // S3バケットにReactアプリをデプロイ
    new s3deploy.BucketDeployment(this, 'DeployWebsite', {
      sources: [s3deploy.Source.asset(reactBuildPath)],
      destinationBucket: websiteBucket,
      distribution,
      distributionPaths: ['/*'],
    });

最終的なインフラ構成は下記のようになります

lib/infrastructure.ts
lib/infrastructure.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
import * as origins from 'aws-cdk-lib/aws-cloudfront-origins';
import * as s3deploy from 'aws-cdk-lib/aws-s3-deployment';
import * as path from 'path';

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

    // S3バケットの作成
    const websiteBucket = new s3.Bucket(this, 'react-s3-host', {
      // スタック削除時にバケットも削除
      removalPolicy: cdk.RemovalPolicy.DESTROY,
      autoDeleteObjects: true,
      // セキュリティのため直接アクセスをブロック
      blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL
    });

    // CloudFrontディストリビューションの作成
    const distribution = new cloudfront.Distribution(this, 'Distribution', {
      defaultBehavior: {
        origin: new origins.S3Origin(websiteBucket),
        // HTTPSへのリダイレクト
        viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
        // キャッシュ設定
        cachePolicy: cloudfront.CachePolicy.CACHING_OPTIMIZED,
      },
      // デフォルトルート
      defaultRootObject: 'index.html'
    });

    // Reactアプリのビルドパスを指定
    const reactBuildPath = path.join(__dirname, '../../react-app/build');

    // S3バケットにReactアプリをデプロイ
    new s3deploy.BucketDeployment(this, 'DeployWebsite', {
      sources: [s3deploy.Source.asset(reactBuildPath)],
      destinationBucket: websiteBucket,
      distribution,
      distributionPaths: ['/*'],
    });
  }
}

このソースを使用することで、S3バケットとCloudFrontディストリビューションが連携し、Reactアプリケーションを配信できる環境が構築されます。それでは下記のコマンドでデプロイします。

cdk deploy

デプロイが問題なく実行できたら、構築したリソースのパラメータをAWSコンソールで確認してみます。

構築したリソースのパラメータを確認

S3バケットの確認

構築したS3バケット内には、Reactアプリケーションのビルド成果物が配置されています。
スクリーンショット 2025-05-04 1.11.02

バケットポリシーの確認

バケットポリシーを確認すると、CloudFrontからのアクセスのみを許可する設定が適切に反映されています。これにより、S3バケットへの直接アクセスはブロックしつつ、CloudFrontを経由したアクセスのみを許可する構成が実現できています。
スクリーンショット 2025-05-04 1.15.15
スクリーンショット 2025-05-04 1.16.18
このポリシーにより、CloudFrontのオリジンアクセスアイデンティティ(OAI)または新しいオリジンアクセスコントロール(OAC)のみがS3バケット内のオブジェクトを取得できるようになっています。AWS CDKはこの設定を自動でやってくれるのでとてもありがたいです。

CloudFrontディストリビューションの確認

CloudFrontディストリビューションが作成されていることが確認できます。
スクリーンショット 2025-05-04 1.19.04

設定通り、デフォルトルートオブジェクトがindex.htmlに設定されています。これにより、ドメインのルートにアクセスした際に自動的にReactアプリのエントリーポイントが表示されます。
スクリーンショット 2025-05-04 1.20.25

セキュリティ設定
今回のデプロイではWAF(Web Application Firewall)は設定していないため、無効化されています。
スクリーンショット 2025-05-04 1.22.22

オリジン設定
オリジンタイプがS3に正しく設定されており、CloudFrontがS3バケットからコンテンツを取得する構成になっています。
スクリーンショット 2025-05-04 1.23.07

キャッシュと配信設定
その他の設定も、CDKで指定した通りに構成されています。
スクリーンショット 2025-05-04 1.25.06

https://docs.aws.amazon.com/ja_jp/prescriptive-guidance/latest/patterns/deploy-a-react-based-single-page-application-to-amazon-s3-and-cloudfront.html#deploy-a-react-based-single-page-application-to-amazon-s3-and-cloudfront-additional

スクリーンショット 2025-05-04 1.25.46

Reactアプリケーションにアクセスしてみます

最後に、実際にデプロイされたReactアプリケーションにアクセスして動作を確認します。CloudFrontのディストリビューションドメイン名をブラウザのURL欄に入力してアクセスします。
スクリーンショット 2025-05-04 1.38.44

アクセスすると、無事にReactアプリケーションが表示されました!回転するReactのロゴが表示されています。私はこのロゴのアニメーションがとても好きです。

余談ですが、IT業界に入ってから、この業界のロゴはおしゃれで遊び心があって魅力的だなあと感じています。
スクリーンショット 2025-05-04 1.40.14

おわりに

今回はAWS CDKを利用して、S3とCloudFrontを組み合わせたインフラにReactアプリケーションをデプロイする方法を紹介しました。特に便利だなと感じたのは、S3へのReactアプリケーションのデプロイを自動化できる点です。コード数行で完結するこの便利さは、実際に使ってみると実感できました。

また、このハンズオンを準備するにあたり、最初は手動でAWSコンソールから全て構築してみました。しかし、その過程では多くの選択項目があり、同じ環境を再現するのに苦労しました。。

S3で静的アプリケーションのホスティングを検討されている方の参考になれば幸いです!

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.