はじめに
ReactアプリをAWS(Amazon Web Services)にデプロイする手順を詳しく解説します。今回は、ポケモンクイズアプリを例に、完全無料(AWS無料枠内)でインターネット上に公開する方法を紹介します。
今回デプロイするアプリ
- フレームワーク: React 18.2 + TypeScript 5.2
- ビルドツール: Vite 5.2
- アプリタイプ: SPA(Single Page Application)
- バックエンド: なし(完全にクライアントサイド)
目次
なぜS3 + CloudFront構成なのか
Reactアプリのデプロイ先として、AWS S3 + CloudFrontを選んだ理由は以下の通りです。
構成図
ユーザー
↓ HTTPS
CloudFront (CDN)
↓ OAC認証
S3 Bucket (静的ファイル)
選定理由
| メリット | 説明 |
|---|---|
| 低コスト | AWS無料枠内で運用可能。13ヶ月目以降も月数円〜数十円程度 |
| サーバー不要 | 静的ファイルをS3に置くだけ。サーバー管理が不要 |
| HTTPS標準 | CloudFrontで無料のSSL証明書を利用可能 |
| 高速配信 | 世界中のエッジロケーションでキャッシュ |
| 高可用性 | AWS側で自動的にスケール・冗長化 |
他の選択肢との比較
| サービス | メリット | デメリット | 判断 |
|---|---|---|---|
| S3 + CloudFront | 低コスト、シンプル | 初期設定がやや複雑 | ✅ 採用 |
| AWS Amplify | Git連携で自動デプロイ | ビルド時間で課金 | ❌ やや割高 |
| EC2 | 自由度が高い | サーバー管理必要 | ❌ 過剰 |
前提条件
必要なもの
- AWSアカウント(無料枠あり)
- AWS CLIのインストールと設定
- Node.js(18.0以上)
AWS CLIの確認
# バージョン確認
aws --version
# 認証情報の確認
aws sts get-caller-identity
正しく設定されていれば、以下のような出力が表示されます。
{
"UserId": "AIDAXXXXXXXXXXXXXXXXX",
"Account": "123456789012",
"Arn": "arn:aws:iam::123456789012:user/your-username"
}
デプロイ手順
ステップ1: アプリケーションのビルド
まず、本番用のビルドを作成します。
npm run build
実行内容:
- TypeScriptの型チェック
- Viteで最適化されたファイルを生成
- 出力先:
dist/フォルダ
生成されるファイル:
dist/
├── index.html
└── assets/
└── index-[hash].js
ステップ2: S3バケットの作成
静的ファイルを保管するS3バケットを作成します。
# バケット作成(バケット名は全世界で一意である必要があります)
aws s3 mb s3://your-app-name-2025 --region ap-northeast-1
コマンド解説:
s3 mb: “make bucket”の略(バケット作成)--region ap-northeast-1: 東京リージョンを指定
パブリックアクセスブロックの確認:
aws s3api get-public-access-block --bucket your-app-name-2025
すべてtrueになっていることを確認してください(セキュリティのため)。
ステップ3: ファイルのアップロード
aws s3 sync dist/ s3://your-app-name-2025 --delete
オプション解説:
sync: 差分のみアップロード(効率的)--delete: S3にあってローカルにないファイルを削除
ステップ4: OAC(Origin Access Control)の作成
CloudFrontからS3へ安全にアクセスするためのOACを作成します。
設定ファイル oac-config.json を作成:
{
"Name": "your-app-oac",
"Description": "OAC for your app S3 bucket",
"SigningProtocol": "sigv4",
"SigningBehavior": "always",
"OriginAccessControlOriginType": "s3"
}
OACを作成:
aws cloudfront create-origin-access-control --origin-access-control-config file://oac-config.json
出力されたId(例: E2EJX97TW43NVS)をメモしておきます。
ステップ5: CloudFrontディストリビューションの作成
設定ファイル cloudfront-config.json を作成:
{
"CallerReference": "your-app-2025",
"Comment": "Your App Distribution",
"DefaultRootObject": "index.html",
"Origins": {
"Quantity": 1,
"Items": [
{
"Id": "S3-your-app-name-2025",
"DomainName": "your-app-name-2025.s3.ap-northeast-1.amazonaws.com",
"OriginAccessControlId": "E2EJX97TW43NVS",
"S3OriginConfig": {
"OriginAccessIdentity": ""
}
}
]
},
"DefaultCacheBehavior": {
"TargetOriginId": "S3-your-app-name-2025",
"ViewerProtocolPolicy": "redirect-to-https",
"AllowedMethods": {
"Quantity": 2,
"Items": ["GET", "HEAD"],
"CachedMethods": {
"Quantity": 2,
"Items": ["GET", "HEAD"]
}
},
"Compress": true,
"CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6",
"TrustedSigners": {
"Enabled": false,
"Quantity": 0
},
"TrustedKeyGroups": {
"Enabled": false,
"Quantity": 0
}
},
"Enabled": true,
"PriceClass": "PriceClass_100",
"HttpVersion": "http2and3"
}
ディストリビューションを作成:
aws cloudfront create-distribution --distribution-config file://cloudfront-config.json
出力されたIdとDomainNameをメモします。
{
"Distribution": {
"Id": "E2YOGKTL016F74",
"DomainName": "d3nikxhjxcxmrr.cloudfront.net",
"Status": "InProgress"
}
}
ステップ6: S3バケットポリシーの設定
CloudFrontからS3へのアクセスを許可します。
ポリシーファイル s3-bucket-policy.json を作成:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "cloudfront.amazonaws.com"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::your-app-name-2025/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::123456789012:distribution/E2YOGKTL016F74"
}
}
}
]
}
123456789012は自分のAWSアカウント番号に、E2YOGKTL016F74は実際のディストリビューションIDに置き換えてください。ポリシーを適用:
aws s3api put-bucket-policy --bucket your-app-name-2025 --policy file://s3-bucket-policy.json
ステップ7: SPA用カスタムエラーレスポンス設定
React RouterなどのSPAルーティングを動作させるための重要な設定です。
なぜ必要?
SPAでは、/aboutや/scoresなどのルートがReact Router内にのみ存在します。ユーザーが直接これらのURLにアクセスすると、CloudFrontはS3に該当するファイルがないため404エラーを返します。
解決策: 404/403エラーが発生したら、常にindex.htmlを返すように設定します。
現在の設定を取得:
aws cloudfront get-distribution-config --id E2YOGKTL016F74 --query "DistributionConfig" --output json > current-config.json
ETagを取得:
aws cloudfront get-distribution-config --id E2YOGKTL016F74 --query "ETag" --output text
current-config.jsonを編集:
CustomErrorResponsesセクションを以下のように変更します。
"CustomErrorResponses": {
"Quantity": 2,
"Items": [
{
"ErrorCode": 403,
"ResponsePagePath": "/index.html",
"ResponseCode": "200",
"ErrorCachingMinTTL": 10
},
{
"ErrorCode": 404,
"ResponsePagePath": "/index.html",
"ResponseCode": "200",
"ErrorCachingMinTTL": 10
}
]
}
設定を更新:
aws cloudfront update-distribution \
--id E2YOGKTL016F74 \
--distribution-config file://current-config.json \
--if-match [先ほど取得したETag]
ステップ8: デプロイ完了の確認
aws cloudfront get-distribution --id E2YOGKTL016F74 --query "Distribution.Status" --output text
Deployedと表示されたら完了です(5-15分かかります)。
https://[CloudFrontのドメイン名].cloudfront.netでアクセスできます!コストについて
AWS無料枠(12ヶ月間)
| サービス | 無料枠 | このアプリの使用量 |
|---|---|---|
| S3 | 5GB保管、20,000 GET/月 | 数MB(完全に無料枠内) |
| CloudFront | 1TB転送、10,000,000リクエスト/月 | 低トラフィック(完全に無料枠内) |
| ACM(SSL証明書) | 永続無料 | 無料 |
13ヶ月目以降
- S3保管料: 数MB × $0.023/GB = 月$0.01未満
- CloudFront: 低トラフィックなら月$0.01〜$0.10程度
- 合計: 月$0.01〜$0.10程度(ほぼ無料)
更新方法
アプリを更新する際の手順です。
手動更新
# 1. ビルド
npm run build
# 2. S3にアップロード
aws s3 sync dist/ s3://your-app-name-2025 --delete
# 3. CloudFrontのキャッシュを無効化
aws cloudfront create-invalidation \
--distribution-id E2YOGKTL016F74 \
--paths "/index.html"
index.htmlのみ無効化すればOKです。デプロイスクリプトの作成
毎回コマンドを打つのは面倒なので、スクリプトを作成します。
deploy.sh:
#!/bin/bash
set -e
echo "Building..."
npm run build
echo "Uploading to S3..."
aws s3 sync dist/ s3://your-app-name-2025 --delete
echo "Creating invalidation..."
aws cloudfront create-invalidation \
--distribution-id E2YOGKTL016F74 \
--paths "/index.html"
echo "Deployment complete!"
実行:
chmod +x deploy.sh
./deploy.sh
GitHub Actionsで自動デプロイ
Git pushで自動的にデプロイされるように設定できます。
.github/workflows/deploy.yml:
name: Deploy to AWS
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ap-northeast-1
- name: Deploy to S3
run: aws s3 sync dist/ s3://your-app-name-2025 --delete
- name: Invalidate CloudFront cache
run: |
aws cloudfront create-invalidation \
--distribution-id E2YOGKTL016F74 \
--paths "/index.html"
必要なGitHub Secrets:
AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY
トラブルシューティング
問題1: 403エラーが表示される
原因: S3バケットポリシーが正しく設定されていない
解決策:
# ポリシーを確認
aws s3api get-bucket-policy --bucket your-app-name-2025
# ポリシーを再適用
aws s3api put-bucket-policy --bucket your-app-name-2025 --policy file://s3-bucket-policy.json
問題2: ルーティングが動作しない
原因: カスタムエラーレスポンスが設定されていない
解決策: ステップ7の設定を再確認してください。
問題3: 更新が反映されない
原因: CloudFrontがキャッシュしている
解決策:
aws cloudfront create-invalidation \
--distribution-id E2YOGKTL016F74 \
--paths "/*"
まとめ
この方法で、ReactアプリをAWSに低コスト(ほぼ無料)でデプロイできました。
この構成のメリット
- ✅ 低コスト: AWS無料枠内で運用可能
- ✅ シンプル: サーバー管理不要
- ✅ セキュア: HTTPS、OAC、パブリックアクセスブロック
- ✅ 高速: CloudFront CDNでグローバル配信
- ✅ スケーラブル: トラフィック増加に自動対応
応用
この手順は、React以外のSPA(Vue.js、Angularなど)でも同様に使えます。
質問やコメントがあれば、お気軽にどうぞ!

コメント