はじめに
AWS を使ったアンケートアプリの開発を通じて、CloudFront、S3、API Gateway、Lambda、DynamoDB、Cognito、Route 53 といったサービスを組み合わせてサーバーレスアーキテクチャを構築しました。
初めて AWS を触ったとき、個々のサービスの名前は知っていても「なぜこのサービスが必要なのか」「他のサービスとどう繋がっているのか」がなかなか掴めませんでした。この記事では、実際のプロジェクトで使った構成を題材に、各サービスの役割・選定理由・サービス間の連携 を初学者目線で整理します。
対象読者
- AWS のサービス名はなんとなく聞いたことがある
- サーバーレスアーキテクチャの全体像を掴みたい
- 「なぜそのサービスを使うのか」を理解したい
構成図で見る全体像
今回のアプリは以下のような構成です。
ざっくりと2つの流れがあります。
1. ユーザーがアンケートに回答する流れ
画面表示:
回答送信:
2. 管理者がダッシュボードを見る流れ
ログイン:
集計取得:
これだけ見ると集計結果なんかは「集計結果取得したいだけなのに間に2つも挟む必要があるのか?」なんて思ってしまいました。しかし、それぞれのサービスには明確な役割があり、1つずつ見ていくと「なるほど、だからこれが必要なのか」と納得できるはずです。
各サービスの役割と「なぜ使うのか」
S3 — ファイルの置き場所
何をしてくれるか: ビルドした HTML、CSS、JavaScript、画像ファイルなどを保存するストレージです。
なぜ使うのか: Web アプリのフロントエンドは、最終的にはただのファイル(HTML/CSS/JS)です。これらのファイルをどこかに置いて、ユーザーがアクセスしたときに返してあげる必要があります。S3 はファイルの保管に特化したサービスで、容量を気にせず安価に保存できます。
注意点: S3 単体でも静的サイトのホスティングはできますが、後述する CloudFront と組み合わせるのが一般的です。S3 だけだと HTTPS が使えない、配信速度が遅いといった課題に加え、アクセス制御にも問題があります。S3 の静的ホスティングではバケットをパブリック公開する必要があるため、http://バケット名.s3-website-ap-northeast-1.amazonaws.com/ のような URL で誰でも直接アクセスできてしまいます。CloudFront + OAC を使えば S3 バケット自体は非公開のまま、CloudFront 経由のアクセスだけを許可できます。
CloudFront — ユーザーに一番近いところから配信する
何をしてくれるか: S3 に保存したファイルを、世界中のエッジサーバー(ユーザーに近いサーバー)からキャッシュして高速配信する CDN(Content Delivery Network)です。
なぜ使うのか: S3 は東京リージョンなど特定の場所にあります。ユーザーが遠い場所からアクセスすると遅くなります。CloudFront を前に置くと、一度アクセスされたコンテンツがエッジサーバーにキャッシュされ、次回以降は最寄りのサーバーから高速に返されます。
それだけでなく、今回のプロジェクトでは CloudFront に以下の役割も担わせています。
- HTTPS の提供: S3 単体では HTTPS を使えないが、CloudFront を通すことで自動的に HTTPS になる
- API Gateway との統合: フロントエンドと API を同じドメインから配信することで、CORS(クロスオリジン)の問題を回避できる
- アクセス制御: OAC(Origin Access Control)を設定することで、S3 への直接アクセスを禁止し、必ず CloudFront 経由でアクセスさせる
- Basic 認証: ステージング・本番環境で CloudFront Function を使って Basic 認証をかけている
CloudFront は「ただのキャッシュ」ではなく、アプリケーションの入り口(フロントドア)として多くの役割を果たしています。
API Gateway — リクエストの受付窓口
何をしてくれるか: HTTP リクエストを受け付けて、適切な Lambda 関数にルーティングしてくれるサービスです。
なぜ使うのか: 「Lambda に直接リクエストを送ればいいのでは?」と思うかもしれません。実は Lambda Function URL という機能を使えば、API Gateway なしでも HTTP リクエストを受け取ることは可能です。しかし API Gateway を間に挟むことで、以下のような機能が使えるようになります。
-
ルーティング:
POST /surveyはアンケート保存、GET /survey/statsは集計取得、のように URL パスとHTTPメソッドに応じて適切な Lambda を呼び分ける - 認証: Lambda Authorizer や Cognito Authorizer を設定して、リクエストの認証を API Gateway のレイヤーで行える
- スロットリング: 大量のリクエストが来たときに自動的に制限をかけてくれる
今回は HTTP API を使っています。API Gateway にはもう1つ REST API というタイプもありますが、HTTP API のほうが安価でシンプルなため、今回のようなシンプルな API には HTTP API が適しています。
Lambda — サーバーを持たないバックエンド
何をしてくれるか: リクエストが来たときだけコードを実行してくれるサーバーレスの関数です。
なぜ使うのか: 従来のサーバー構成(EC2 など)では、リクエストがなくてもサーバーを起動しっぱなしにして待機させる必要があります。その間もコストがかかります。Lambda は「リクエストが来たときだけ起動して、処理が終わったら消える」ので、使った分だけの課金になります。
今回のプロジェクトでは3つの Lambda 関数を使っています。
| 関数 | 役割 |
|---|---|
| survey | アンケート回答を受け取って DynamoDB に保存する |
| stats | DynamoDB から全データを集計して返す |
| authorizer | CloudFront が付けたカスタムヘッダーを検証して、API Gateway への直接アクセスを弾く |
アンケートアプリのように「常にリクエストが来続けるわけではない」サービスでは、Lambda のコスト効率は非常に良いです。
DynamoDB — 柔軟な構造のデータベース
何をしてくれるか: AWS が提供するデータベースサービスです。一般的なデータベース(MySQL や PostgreSQL)とは異なるタイプのデータベースで、「NoSQL データベース」と呼ばれます。
SQL と NoSQL の違い
一般的なデータベース(SQL データベース)は Excel の表のようにカラム(列)が固定されています。たとえば「名前・メール・年齢」というカラムを最初に決めたら、すべてのデータがその形式に従います。代表的なものに MySQL や PostgreSQL があり、AWS ではこれらを動かすサービスとして RDS が用意されています。
一方、DynamoDB のような NoSQL データベースはカラムを事前に固定する必要がありません(これを「スキーマレス」と言います)。たとえばアンケートの設問数が途中で増えても、テーブルの構造を変更せずにそのまま保存できます。
なぜ使うのか: 「なぜ RDS(MySQL/PostgreSQL)ではなく DynamoDB なのか?」という疑問があるかもしれません。今回 DynamoDB を選んだ理由は以下の通りです。
- サーバーレスと相性が良い: RDS はデータベースサーバーを常時起動させておく必要があり、リクエストがなくてもコストがかかる。DynamoDB はリクエストごとの課金(PAY_PER_REQUEST)で、使わないときはほぼ無料
- Lambda との接続がシンプル: RDS のような SQL データベースは、接続数に上限があるため「接続を使い回す仕組み(コネクションプール)」が必要になる。しかし Lambda は毎回新しいインスタンスが起動する可能性があり(これを「コールドスタート」と言います)、起動のたびに新しい接続を張ると接続数があっという間に上限に達してしまう。DynamoDB は HTTP リクエストでアクセスするため、こうした接続管理の問題が発生しない
- 自動でスケーリング: アクセスが急に増えてもデータベース側が自動で処理能力を拡張してくれる。RDS の場合はスペックの変更を手動で行う必要がある
補足: 「サーバーレス = DynamoDB」ではない
上記の理由はあくまで「今回のアンケートアプリで DynamoDB を選んだ理由」です。サーバーレスだから必ず DynamoDB、サーバーありだから必ず RDS、というわけではありません。
- RDS が向いているケース: データ同士の関連が複雑(ユーザー → 注文 → 商品のように複数テーブルを結合して取得したい)、複雑な条件での集計・検索が必要、データの整合性が重要(送金処理のように「片方だけ成功」を許容できない場合)
- DynamoDB が向いているケース: キーで1件取得・1件保存のようなシンプルな操作が中心、大量アクセスへの対応が必要、データの形が変わりやすい
今回はアンケートの回答を保存して集計するだけのシンプルな構造で、かつサーバーレスとの相性も良かったため DynamoDB を選びました。
今回のテーブル設計はシンプルです。
PK(パーティションキー): userId — 回答者ごとにユニーク(UUID を自動生成)
SK(ソートキー): createdAt — 回答日時(ISO 8601 形式)
アンケートの各回答は q1, q2, q3... のように個別のカラムとして保存しています。これは後から CSV で出力しやすいようにするためです。
Cognito — 管理者のログイン認証
何をしてくれるか: ユーザー認証(サインアップ、ログイン、トークン管理)を丸ごと提供してくれるサービスです。
なぜ使うのか: 管理者用のダッシュボード(投票結果の集計ページ)があり、誰でも見られては困ります。認証機能を自前で実装するのはセキュリティリスクが高く大変ですが、Cognito を使えば Hosted UI(AWS が用意してくれるログイン画面)をそのまま利用できます。
今回のプロジェクトでは:
- 管理者のみがアカウントを作成できる設定(
allowAdminCreateUserOnly: true) - ログイン後のトークン取得には Authorization Code Flow + PKCE という方式を採用(詳しくは下の補足を参照)
- ダッシュボードページにアクセスすると Cognito のログイン画面にリダイレクトされる
Authorization Code Flow + PKCE の仕組み
ログイン成功時にトークンをそのまま返すと、URL に含まれたトークンが漏れるリスクがあります。そこで、まず一時的な「認証コード」を発行し、アプリ側でトークンに交換する方式を使います。
「認証コードが盗まれたら同じでは?」と思うかもしれませんが、それを防ぐのが PKCE(Proof Key for Code Exchange)です。
問題なのは トークンを渡すこと自体ではなく、リダイレクトの URL にトークンが載ること です。URL はブラウザの履歴やログに残るため漏れやすい一方、HTTPS の POST リクエストのレスポンスボディは URL に露出しません。そのためリダイレクトでは認証コードだけを返し、トークンの取得は安全な POST リクエストで行っています。
さらに、認証コードが万一漏れても code_verifier がなければトークンに交換できない — これが PKCE による二重の防御です。
Route 53 — ドメイン名の管理
何をしてくれるか: DNS(ドメインネームシステム)の管理サービスです。example.com のようなドメイン名と、CloudFront のような AWS リソースを紐付けてくれます。
なぜ使うのか: CloudFront のデフォルトでは d1234abcde.cloudfront.net のようなランダムな URL になります。本番環境では独自ドメインを使いたいので、Route 53 でドメインの DNS 設定を行い、CloudFront ディストリビューション(「どのドメインのリクエストを、どこから配信するか」をまとめた配信設定のこと)に紐付けています。
今回は本番環境(prod)のみ Route 53 でカスタムドメインを設定しています。開発・ステージング環境では CloudFront のデフォルト URL をそのまま使っています。
サービス間の連携 — データの流れを追う
個々のサービスを理解したところで、実際のデータの流れを追ってみましょう。
ユーザーがアンケートに回答する場合
最初に提示した構成図をもう少し詳しく見ていきます。
Step 1: DNS 解決
Step 2: 画面表示(静的ファイル配信)
Step 3: 回答送信(API リクエスト)
ポイントは Lambda Authorizer です。API Gateway の URL は公開されているため、誰でも直接リクエストを送れてしまいます。CloudFront がリクエスト時に付与するカスタムヘッダー(シークレット値)を Authorizer で検証することで、「CloudFront を経由していないリクエスト」を弾いています。
管理者がダッシュボードを見る場合
Step 1: 画面表示
Step 2: Cognito 認証
Step 3: 集計データの取得
まとめ
各サービスの役割を改めて一覧にすると:
| サービス | 一言で言うと | なぜ必要か |
|---|---|---|
| S3 | ファイル置き場 | ビルド済みのフロントエンドファイルを保管する |
| CloudFront | アプリの入り口 | HTTPS、キャッシュ、API 統合、アクセス制御を一手に担う |
| API Gateway | リクエストの振り分け | URL パスに応じて適切な Lambda を呼び出す |
| Lambda | バックエンド処理 | リクエストが来たときだけ動くので、サーバー管理不要・コスト効率が良い |
| DynamoDB | データベース | サーバーレスと相性が良く、自動スケーリングで運用が楽 |
| Cognito | 認証基盤 | ログイン機能を自前で作らなくて済む |
| Route 53 | ドメイン管理 | 独自ドメインを CloudFront に紐付ける |
最初は何が何だかわからないと思っていましたが、実際に手を動かしてみると、各サービスがそれぞれ1つの責任を持って連携している構造が見えてくると思います。
感想
転職して3ヶ月が経ち、初案件としてインフラ構築を担当しました。最初は「AWS のサービス多すぎるだろ…」と思っていましたが、実際に構築してみると、自分では考慮しきれないセキュリティリスクをカバーするためにそれぞれのサービスが存在していることがわかりました。
今回は AI を活用してインフラを構築しました。大部分はよしなにやってくれましたが、完璧ではありませんでした。たとえば、CloudFront を経由せず API Gateway の URL を直接叩く実装になっていたケースがありました。API Gateway の URL は公開されているため、CloudFront を経由しないと Lambda Authorizer によるアクセス制御が機能せず、誰でも API を叩けてしまいます。AIはゴリゴリ発達している世の中ですが人のレビューがあってよかったと思いましたw
理解を深めるためにこの記事を書きましたが、誰かの参考になれば幸いです。