初心者向け　-AWSのS3を使用して画面表示してみる-


# はじめに
今回、これまで実務でAWSを触った経験はあるものの、実際にAWSで何かを作業したことがないと感じたため、今回はAWSの機能を活用して画面作成に挑戦し、備忘録として残すことにしました。

今回行ったのは、Amazon S3にバケットを作成し、HTMLファイルをアップロードして、そのコンテンツを表示する画面を作成する作業です。作成した画面は、週次予定表を表示するシンプルなWebページです。
まずは、今回使用するS3とバケットについて簡単に説明します。
# S3とは
AWSが提供するストレージサービスであり、低コストで容量を気にせずデータを保存でき、柔軟なデータ管理が可能です。データのバックアップ先として利用されることはもちろん、機械学習などでも頻繁に活用されています。また、既存のAWSサービスと連携することで、幅広い用途に対応できます。

# バケットとは
バケットとは、データを保存するための「箱」です。複数のオブジェクト（ファイル）を保存でき、1アカウントで最大100個作成可能です。オブジェクトはファイルシステムで使われる通常のファイルとは違って、サイズや種類に関係なく、大きなデータも効率的に保存できます。また、バケットはアクセス制御ができ、誰がデータを見られるかを管理することができます。


# 全体の流れ
1.HTMLファイルの作成
2.バケットを作成し、作成したHTMLをアップロード
3.バケットのアクセス権限の設定し、画面表示
4.CSSファイルやJSファイルをアップロードして、画面レイアウトを変更




# 1.HTMLファイルの作成
メモ帳や統合開発環境（IDE）でHTMLファイルを作成し、「index.html」という名前で保存します。
メモ帳を使用する場合は、文字コードを「UTF-8」で保存しましょう。
```java:index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>週次予定表</title>
</head>
<body>
    <h1>週次予定表</h1>
    <table id="schedule">
       <tbody>
            <tr>
                <td>月曜日</td>
            </tr>
            <tr>
                <td>火曜日</td>
            </tr>
            <tr>
                <td>水曜日</td>
            </tr>
            <tr>
                <td>木曜日</td>
            </tr>
            <tr>
                <td>金曜日</td>
            </tr>
            <tr>
                <td>土曜日</td>
            </tr>
            <tr>
                <td>日曜日</td>
            </tr>
       </tbody>
    </table>
</body>
</html>
```


# 2.バケットを作成し、HTMLファイルをAWS上に保存
AWSにログインしたら画面上部の検索ボックスに「S3」と入力し検索し、「バケットを作成」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3951326/0efaaf34-8675-f68c-e433-022a2974fd39.png)

バケット名を入力し、バケットを作成
※バケット名はAWS全体で一意である必要があるので、他のユーザーが既に使用している名前は使用できません。ユニークな名前を付けるようにしてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3951326/848084d9-ad54-9d48-ef36-edc1cebfe6e2.png)

バケットを作成したら、さっそく先ほど作成したHTMLファイルをS3にアップロードします。

作成したバケットを選択し、「ファイルを追加」からHTMLファイルを選択してアップロードしましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3951326/6e3c2dbb-f426-8b96-fc2e-71b281ba610a.png)

これでHTMLファイルのアップロード完了しました！

# 3.バケットのアクセス権限の設定し、画面表示

では、この状態で画面表示してみよう！と思い、URLを叩いてみてもエラー...
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3951326/c87b7912-6215-691f-6e9d-b224c1954470.png)
調べてみると、S3バケットにアクセスしようとした際に、リソースに対する許可が不足しているためアクセスが拒否されていることが分かりました。

そこで、アクセス権限を付与しましょう。
まず、バケットのアクセス許可設定で「ブロックパブリックアクセス（バケット設定）」をオフにします。
※この設定をオフにすると、バケット内のファイルがインターネット上で誰でもアクセスできるようになるため、慎重に設定を行う必要があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3951326/7ae9859c-088d-19e5-8cc3-598e4c4236cb.png)

次にバケットポリシーの編集で下記を入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3951326/712f85f0-edaa-32b3-a426-3c2ec28f52de.png)

Resourceのs3:::の後は作成したバケット名に置き換えてください


```
{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Sid": "PublicReadGetObject",
			"Effect": "Allow",
			"Principal": "*",
			"Action": "s3:GetObject",
			"Resource": "arn:aws:s3:::my-lambda-counter/*"
		}
	]
}
```
これで設定は完了です！URLにアクセスして動作を確認します。

URLは、HTMLファイルのプロパティにある「オブジェクトURL」を使用します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3951326/14b4320a-84d8-5a58-aad5-4d29b4e48ca0.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3951326/06bd425a-6f2e-8185-8c0c-d4dbb258048c.png)

このように表示されていればOKです！

# 4.CSSファイルやJSファイルをアップロードして、画面レイアウトを変更
S3にはHTMLファイルだけでなく、JSファイルやCSSファイルももちろんアップロードできます。せっかくなので今回、これらを活用して画面のレイアウトをカスタマイズしました。

具体的には、各曜日ごとに枠を追加し、その下に「追加」ボタンを配置しました。「追加」ボタンをクリックするとダイアログが表示され、項目を入力して保存すると、該当する曜日の枠内にその内容が表示される仕組みです。

```index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>週次予定表</title>
    <link rel="stylesheet" href="style.css"> 
</head>
<body>
    <h1 class="schedule-title">週次予定表</h1>
    
    <table id="schedule">
       <tbody>
            <tr>
                <td>月曜日</td>
                <td class="task-box"></td>
            </tr>
            <tr>
                <td>火曜日</td>
                <td class="task-box"></td>
            </tr>
            <tr>
                <td>水曜日</td>
                <td class="task-box"></td>
            </tr>
            <tr>
                <td>木曜日</td>
                <td class="task-box"></td>
            </tr>
            <tr>
                <td>金曜日</td>
                <td class="task-box"></td>
            </tr>
            <tr>
                <td>土曜日</td>
                <td class="task-box"></td>
            </tr>
            <tr>
                <td>日曜日</td>
                <td class="task-box"></td>
            </tr>
       </tbody>
    </table>

    <!-- 追加ボタン -->
    <div class="buttons">
        <button class="add-btn" id="addBtn">追加</button>
    </div>

    <!-- ダイアログ -->
    <div id="addTaskDialog" class="dialog">
        <div class="dialog-content">
            <h2>予定を追加</h2>
            <form id="addTaskForm">
                <label for="day">曜日:</label>
                <select id="day" required>
                    <option value="月曜日">月曜日</option>
                    <option value="火曜日">火曜日</option>
                    <option value="水曜日">水曜日</option>
                    <option value="木曜日">木曜日</option>
                    <option value="金曜日">金曜日</option>
                    <option value="土曜日">土曜日</option>
                    <option value="日曜日">日曜日</option>
                </select><br><br>

                <label for="title">タイトル:</label>
                <input type="text" id="title" required><br><br>

                <label for="content">本文:</label>
                <textarea id="content" required></textarea><br><br>

                <button type="submit">保存</button>
                <button type="button" id="closeBtn">キャンセル</button>
            </form>
        </div>
    </div>

    <script src="script.js"></script>
</body>
</html>
```

```style.css
/* タイトル部分のスタイル */
.schedule-title {
    font-family: 'Arial', sans-serif;
    font-size: 48px;
    color: #ffffff;
    background: linear-gradient(to right, #2196F3, #64B5F6); /* 青いグラデーション背景 */
    padding: 20px;
    border-radius: 10px;
    text-align: center;
}

/* 画面いっぱいに広げるために設定 */
body {
    display: flex;
    justify-content: center; /* 画面中央に配置 */
    align-items: center;
    flex-direction: column; /* コンテンツを縦に並べる */
    min-height: 100vh; /* 画面の高さに合わせる */

}

/* テーブル全体のスタイル */
table {
    width: 60%; /* テーブル幅を60%に設定 */
    border-collapse: collapse;
    background-color: white;
    box-shadow: 0 8px 15px rgba(0, 0, 0, 0.1); /* 影を追加 */
    text-align: center;
}

/* 曜日のセル */
td:first-child {
    width: 20%; /* 曜日の枠の幅を小さく設定 */
    border: 1px solid #ccc;
    padding: 15px;
    font-size: 16px;
    background-color: #f9f9f9;
}

/* タスクボックスのセル */
td:last-child {
    width: 80%; /* 横の枠（タスクボックス）の幅を大きく設定 */
    border: 1px solid #ccc;
    padding: 15px;
    font-size: 14px;
    background-color: #ffffff;
}

/* 追加ボタン */
.buttons {
    display: flex;
    justify-content: center;
    gap: 15px;
    margin-top: 30px;
    width: auto; /* ボタンの幅を自動に設定 */
}

/* ダイアログのスタイル */
.dialog {
    display: none; /* 初めは非表示 */
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.5);
    justify-content: center;
    align-items: center;
}

.dialog-content {
    background: white;
    padding: 30px;
    border-radius: 10px;
    width: 300px;
    box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
}

form {
    display: flex;
    flex-direction: column;
}

label {
    margin-bottom: 5px;
}

input, select, textarea {
    margin-bottom: 15px;
    padding: 10px;
    border: 1px solid #ccc;
    border-radius: 5px;
}

button {
    padding: 10px 20px;
    background-color: #4CAF50;
    color: white;
    border: none;
    border-radius: 5px;
    cursor: pointer;
}

button:hover {
    background-color: #45a049;
}

/* キャンセルボタン */
#closeBtn {
    background-color: #f44336;
}

#closeBtn:hover {
    background-color: #d32f2f;
}
```

```script.js
// 追加ボタンのクリックイベント
document.getElementById('addBtn').addEventListener('click', function() {
    document.getElementById('addTaskDialog').style.display = 'flex'; // ダイアログを表示
});

// キャンセルボタンのクリックイベント
document.getElementById('closeBtn').addEventListener('click', function() {
    document.getElementById('addTaskDialog').style.display = 'none'; // ダイアログを閉じる
});

// フォーム送信の処理
document.getElementById('addTaskForm').addEventListener('submit', function(event) {
    event.preventDefault(); // フォーム送信を防止

    // ユーザーが入力した値を取得
    const day = document.getElementById('day').value;
    const title = document.getElementById('title').value;
    const content = document.getElementById('content').value;

    // 対応する曜日のタスクボックスにタイトルと内容を追加
    const dayCells = document.querySelectorAll('#schedule td');
    dayCells.forEach(cell => {
        if (cell.textContent === day && cell.nextElementSibling) {
            // 曜日の隣のタスクボックスにタイトルと内容を追加
            cell.nextElementSibling.innerHTML = `<strong>${title}</strong><br>${content}`;
        }
    });

    // フォームをリセットし、ダイアログを閉じる
    document.getElementById('addTaskForm').reset();
    document.getElementById('addTaskDialog').style.display = 'none';
});
```


前述の3つのファイル（HTML、JS、CSS）を同様にS3にアップロードし、URLにアクセスして動作を確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3952109/606d444d-7282-f728-5724-5230ab3fd884.png)

このような画面が表示されました。「追加」ボタンをクリックするとダイアログが表示され、入力内容を記入する仕組みです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3952109/f2174e0a-2ca7-dad3-cdcf-333cd4380972.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3952109/0bc84e60-00c4-aaa6-294f-884fed250956.png)

今回、S3でバケットを作成し、ファイルのアップロードから画面表示まで初めて行いましたが、思っていたよりも簡単に操作できました。

ご参考にしていただければ幸いです。