AWSで「フォーム投稿 → 保存 → 一覧表示」Webアプリを構築する（Amplifyなし）

# 🧾 概要

本記事では、AWSの基本サービスだけを用いて、**「名前＋メッセージの投稿・保存・一覧表示」ができるWebアプリ**を構築した手順を紹介します。  
Amplifyは使用していません。
※この記事はGpt4oが書いたものを一部人間が編集しています。実際に構築した際の手順もGpt4o製です。
※はじめは、AWSが公式に出しているチュートリアルを実施しようとしましたが、Amplifyまわりでエラーが出たため実施方法を変更しました。

---

## ✅ 構成図

```
[ブラウザ]
   ↓ fetch
[CloudFront] ← index.html
   ↓
[S3 (静的ホスティング)]

   ↓ fetch
[API Gateway]
   ↓             ↓
[Lambda (Save)]  [Lambda (List)]
        ↓              ↓
    [DynamoDB (保存先)]
```

---

## 📦 使用サービス

- Amazon S3（HTML/JSのホスティング）
- Amazon CloudFront（CDN配信）
- Amazon API Gateway（REST APIルーティング）
- AWS Lambda（ビジネスロジック）
- Amazon DynamoDB（NoSQLデータ保存）

---

## 🛠 ステップごとの手順

### ① Lambda 関数の作成

- `SaveMessageFunction`（POST保存用）と `ListMessagesFunction`（GET一覧用）を作成
- 実行ロールは Lambda 作成時に自動生成され、後から `AmazonDynamoDBFullAccess` を追加

#### 🔹 SaveMessageFunction

```python
import json, boto3, uuid
from datetime import datetime

table = boto3.resource('dynamodb').Table('messages')

def lambda_handler(event, context):
    body = json.loads(event['body'])
    item = {
        'id': str(uuid.uuid4()),
        'name': body.get('name', '匿名'),
        'message': body.get('message', ''),
        'timestamp': datetime.utcnow().isoformat()
    }
    table.put_item(Item=item)
    return {
        'statusCode': 200,
        'headers': {
            'Access-Control-Allow-Origin': '*',
            'Access-Control-Allow-Headers': 'Content-Type'
        },
        'body': json.dumps({'message': '保存しました！'})
    }
```

#### 🔹 ListMessagesFunction

```python
import json, boto3

table = boto3.resource('dynamodb').Table('messages')

def lambda_handler(event, context):
    response = table.scan()
    return {
        'statusCode': 200,
        'headers': {'Access-Control-Allow-Origin': '*'},
        'body': json.dumps(response["Items"])
    }
```

---

### ② DynamoDB テーブル作成

- テーブル名：`messages`
- パーティションキー：`id`（String）

---

### ③ API Gateway の設定

- `/message` リソースを作成
- POST → `SaveMessageFunction` に統合
- GET → `ListMessagesFunction` に統合
- 両方に CORS 有効化
- Lambda プロキシ統合：**True**
- ステージ名 `dev` でデプロイ

---

### ④ S3バケット作成＋index.html アップロード

- 静的ウェブサイトホスティング有効化
- `index.html` をアップロード
- パブリックアクセスを許可（または OAI によるアクセス許可）

#### `index.html` 抜粋：

```javascript
const baseURL = "https://{your-api-id}.execute-api.ap-northeast-1.amazonaws.com/dev";

document.getElementById("msg-form").addEventListener("submit", async (e) => {
  e.preventDefault();
  const name = e.target.name.value;
  const message = e.target.message.value;
  await fetch(`${baseURL}/message`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ name, message })
  });
  e.target.reset();
  loadMessages();
});

async function loadMessages() {
  const res = await fetch(`${baseURL}/message`);
  const messages = await res.json();
  const list = document.getElementById("message-list");
  list.innerHTML = "";
  messages.forEach(msg => {
    const li = document.createElement("li");
    li.textContent = `${msg.name}: ${msg.message}`;
    list.appendChild(li);
  });
}

window.onload = loadMessages;
```

---

### ⑤ CloudFront作成（S3をオリジンに）

- OAI または OAC を設定して S3 バケットにアクセス許可
- キャッシュポリシー：`CachingDisabled`
- デフォルトルート：`index.html`

---
## 👩‍💻 画面
こんな感じで、投稿したメッセージを一覧で確認できる。

![Screenshot 0007-06-11 at 21.29.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4092611/886bfb11-cabf-420a-bf34-933491ba20ff.png)

## 🚧 トラブルシュートメモ

| 症状 | 対処 |
|------|------|
| `Missing Authentication Token` | fetch先URLが間違っている（例：`/message` vs `/messages`） |
| `NetworkError when attempting to fetch resource` | CORS未設定／Lambdaエラー |
| `messages.forEach is not a function` | `response['Items']` を返していない／JSで `.Items` 抜き忘れ |
| CloudWatchログに何も出ない | API Gateway に統合設定がされていない／デプロイ忘れ |

---

## ✨ 所感・学び

- Amplify を使わずに構成することで、各AWSサービスの役割を理解できた(所要時間2,3時間)
- タイポ（`/messages` と `/message`）に最後までハマった、、、(所要時間20分くらい)
- CloudWatch での `print(event)` `print(response)` デバッグが超重要

---

## 🔚 おまけ：今後の拡張アイデア

- Cognito 認証を追加（ユーザー別にメッセージ管理）
- AppSync + DynamoDB + Lambda に移行して GraphQL 化
- S3への画像アップロード機能追加