AWSでサーバーレスなWebアプリを作ってみた話
_アートボード-1.jpg)
すたあ
こんにちは、すたあです。
開発部で社内システムやAWSを触っています。よろしくお願いします。
皆さんは「サーバーレス」という言葉にあこがれはあるでしょうか?
私はありました。AWS SUMMITに行くと、皆サーバーレスで。うらやましくて……。
サーバーやインフラを運用していたら一度は夢に見るんじゃないでしょうか。サーバーレス。
皆サーバーレスってことは、皆この夢を実現しているんですね。すごいや。
サーバーレスWebアプリでは、サーバーの管理や運用を行わずにアプリケーションを構築できるといいます。AWSのフルマネージドサービスを多く採用することで、クラウドプロバイダーがサーバーやインフラストラクチャを管理してくれるのです。
もしそうなら、私たち設計者は機能開発そのものに集中できるようになります。
やはり夢があります。
そんな折、ちょうどログイン機能付きのWebアプリが必要になったので作成しました。せっかくなので勉強がてらサーバーレス構成にしました。私も夢を叶えたい!
初めての経験で調べながらの実装でしたがなんとか形になったのでブログで公開してみます。
たまには音声関連以外のTechブログの内容もあってよいのではないでしょうか。
この記事の流れは
みたいな感じです。よろしくお願いします。
構成について
構成図
さっそく作成したアーキテクチャの図です。

先にアプリについて触れておきます。
実は、私はIT業界に入ったころからAWSでインフラを担当しており、アプリ側の知見がほぼ0でした。ちょっとしたスクリプトや計算コードを書いた経験は多少あれど、GUI的なアプリ開発はしたことはありません。0からスタートです。
どのフレームワークが良いのかの調査から始めました。
今回のようなSPAに対応したフレームワークのうち主流なものはReactとVueだと思います。
2つとも簡単に触ってみて思った感想をまとめると以下になります。
| 条件 | React | Vue |
|---|---|---|
| 主流さ | 非常に主流 | かなり主流 |
| 初期構成 | 初期構成のパターンが多い | Vite + Vueでほぼ固定っぽい |
| 学習コスト | 情報はネットにいっぱいある | HTMLの拡張っぽくて良かった |
| 向いてる規模 | 大規模な方が向いてそう | 小~中規模が向いてそう |
| 柔軟性 | 高そう | 無くはなさそう |
今回選んだjavascriptフレームワークはVueです。
このアプリ要件は、簡単に言えばログインしてきたユーザーのデータをDynamoDBから取ってきて画面に表示する感じです。かなり単純な実装になる想定の為、Reactのような柔軟性は不要だと思いました。
あと、学習を進めていてVueはReactに比べてとっつきやすいと思いました。0からスタートなので優しそうな方を選びました。
ただ、どうにも情報を集めていくとReactのほうが人気が高そうでした。ネイティブアプリへの対応もReactの方がしやすそうですし、拡張性はReactに軍配があがるでしょう。
今後はReactも勉強したいと思います。
また、今回はサーバーレス構成が主役のお話なのでアプリ側の話はこれ以上ほぼしないです。ご了承ください。端的にお伝えすると、アプリ要件をLLMに投げたらソースコードを返してくれたので、それを使いました。
弊社には社内GPTがいます。セキュリティ対策を施してあって安心して使えます。この社内GPTは開発のオトモとしても使えますし、オフィスルールの質問にも答えてくれます。良い子です。
構成で工夫したところ
サーバーレス構成
とにもかくにもサーバーレス構成にしたかったのでしました。
実は、私はIT業界に入ったころからAWSでインフラを担当しており、アプリ側の知見がほぼ0でした。ちょっとしたスクリプトや計算コードを書いた経験は多少あれど、GUI的なアプリ開発はしたことはありません。0からスタートです。
ちなみにAWSでインフラ担当といっても、サーバーレスアーキテクチャについては触ったことがありませんでした。この図で言うとAPI Gateway、DynamoDB、Cognitoは触ったことがありません。ここも0からスタート。ひええ。
API Gatewayの位置について
最初に、API Gatewayの概要を簡単に説明します。
API Gatewayは、提供しているAPIのフロント(玄関口)になるサービスです。
既存のAPIはもちろん、AWSの各サービスやLambdaとも連携して、APIの管理をまとめて引き受けてくれます。
認証やルーティング、監視といった処理もここで行います。
一般的な構成と違う点は、CloudFrontの下にAPI Gatewayがいることでしょうか。
よくある構成はブラウザから直接API GatewayのURLを叩く形です。今回はそうではなく、CloudFrontを経由するルートになっています。
CloudFrontの下にAPI GatewayとアプリソースのS3がいるということは、APIとWebアプリが同じドメインになるということです。これによっていくつかの問題や沼を一気に解決できると思ったのでこの構成にしました。
- CORS
Webアプリ作っててこいつに悩まされることが一番多い気がします。S3とCloudFrontを触るだけでもCORSエラーを見ることがあるくらいです。
CloudFrontの下にAPI Gatewayを置くことでAPIへのアクセスがクロスオリジンではなくなるため、セキュアにCORSを設定できるようになります。
- CookieのSameSite属性
Cognitoのマネージドログインページでは認証トークンをCookieに保存します。このCookieをよりセキュアにしたいため、SameSite属性を指定してあげます。
このとき、CloudFrontの下にAPI Gatewayを置くことで同ドメインになるので、SameSite属性にNone以外を指定できるようになります。Cookieについては、とにかくセキュアにしたかったので他にもhttponly属性やSecure属性をつけています。
こんな感じで、あまり見ない構成かもしれませんが、CloudFrontの下にAPI Gatewayを置いた方が何かと楽になると考えました。ドメインの管理もCloudFrontだけで済みます。
【参考】https://dev.classmethod.jp/articles/access-api-gateway-via-cloudfront/
Cognitoマネージドログインについて
認証画面にはCognitoマネージドログインを使うつもりでしたが、不採用としました。
この機能が出たときは飛んで喜びました。
この機能以前までにCognitoが用意してくれていたログイン画面は、本番環境としてリリースするのは難しいデザインでした。なにせデザインほぼ変えられませんでしたからね。
これが、急にモダンなデザインで、しかもカスタムもそこそこにできるようになったのです。すごくうれしかったです。やったー!ログイン機能実装しなくていいんだー!
……ですが、「ログイン画面から別ページにジャンプさせたい」「ここには指定の文字を入れたい」といった、マネージドログイン機能ではどうしようもない要件が出てきてしまいました。
結局、ログイン機能は自作することになりました。おいしい話ってないんですね。
マネージドログイン機能は個人利用やPoC等なら非常に有効だと思います。今回はマネージドログインで本番まで行けると私が思ってしまっていたので、「マネージドログイン使えない!」というギャップが生まれてしまいました。
この結論は使ってみないと分からなかったことだったので、大変勉強になりました。
Cognito@Edgeについて
このシステムでは、CloudFrontのビヘイビアにLambda@Edgeを使用しています。ちゃんと認証されたアクセスかどうかのチェックを行う為です。
後述しますが、AWSLabs製の「Cognito@Edge」を改造したものを設定しています。
https://github.com/awslabs/cognito-at-edge
ここで、マネージドログイン機能でも自作ログイン画面でも、Cognito@Edgeを使用する際に注意点があります。
2025/06/27時点で、マネージドログインに対応していないと思われます。ですので、少々編集してあげないといけません。
Github上だとcognito-at-edge/src/index.tsの541行目にある、
const userPoolUrl = `https://${this._userPoolDomain}/authorize?redirect_uri=${oauthRedirectUri}&response_type=code&client_id=${this._userPoolAppId}&state=${state}`;↑の箇所がログイン画面のURLになるようです。
このURLがホストされたUI(クラシック)のものになっていて、マネージドログインのURLにはなっていません。
私は↓のような感じで修正しました。
// ホストされたUI(クラシック)のURLをコメントアウトする
//const userPoolUrl = `https://${this._userPoolDomain}/authorize?redirect_uri=${oauthRedirectUri}&response_type=code&client_id=${this._userPoolAppId}&state=${state}`;
// マネージドログインのURLを指定する場合(作業当時はこの形式でした)
const userPoolUrl = `https://${this._userPoolDomain}/oauth2/authorize?lang=ja&response_type=code&client_id=${this._userPoolAppId}&redirect_uri=${oauthRedirectUri}&state=${state}`;
// 自作のログインURLを指定する場合
const userPoolUrl = `https://hogehoge.amivoice.com/login/`ご参考になれば。
費用概算
とりあえず費用の概算をしてみます。
公開範囲が限られているため、しばらくは月50人くらいがアクセスするくらいでしょうか。だいたいそれくらいで見積もってみます。
無料枠はめんd簡略化のために「無料枠範囲内」で書いてしまいます。安くなるでしょうか。サーバーのランニング費よりは安くなって欲しいですね。
CloudFront
- インターネットへのデータ転送 (送信)(最初の 1,024 GB/月は無料)
- 無料枠範囲内
- オリジンへのデータ転送 (送信)(POST リクエストや PUT リクエストなど)
- 0.01 GB/月 x 0.06 USD ≒ 0.00 USD
- リクエストの数 (HTTPS)(最初の 1000 万リクエスト/月は無料)
- 無料枠範囲内
計:0.00 USD ※ごく少額
API Gateway
REST APIを利用しています。
API Gatewayの無料利用枠はAWS にサインアップした日から12か月間だそうです。今回デプロイしたアカウントは既に12か月以上たっているので無料枠はありません。
- リクエスト
- 月 4000回 x 0.00000425 USD = 0.02 USD
計:0.02 USD
Lambda
API用
- リクエスト数(毎月 1,000,000 件の無料リクエスト)
- 無料枠範囲内
- コンピューティング時間(毎秒 400,000 GB の無料利用枠)
- 無料枠範囲内
計:無料
Lambda@Edge用
- リクエスト数
- 月に100 リクエスト x 0.0000006 USD = 0.00 USD (1 か月のリクエスト料金)
- ページアクセス毎にLambda@Edgeが呼ばれますが、2ページしかないアプリなので
50人×2ページで換算しています。
- ページアクセス毎にLambda@Edgeが呼ばれますが、2ページしかないアプリなので
- 月に100 リクエスト x 0.0000006 USD = 0.00 USD (1 か月のリクエスト料金)
- コンピューティング時間
- 0.125 GBのメモリ x 100リクエスト x 各リクエストの実行時間0.1秒 x 0.0000006 USD = 0.00 USD (1 か月のリクエスト料金)
計:0.00 USD ※ごく少額
ちなみに、メモリ128MBで実行に0.1秒かかるLambda@Edgeの場合、月に10000リクエスト来ると0.02料金が0.02 USDになるようです。安いですね。
Cognito
- 月間アクティブユーザー数(月間アクティブユーザー数10,000までは無料)
- 無料枠範囲内
計:無料
DynamoDB
- データストレージ(月25GBまで無料)
- 無料枠範囲内
- オンデマンド書き込み設定
- 100件/月 x 0.000000715 USD ≒ 0.00 USD 書き込みリクエストのコスト ※ごく少額
- オンデマンド読み取り設定
- 7500件/月 x 0.5 結果整合性のある読み取りの読み取りリクエスト単位 x 0.0000001425 USD ≒ 0.00 USD 読み込みリクエストのコスト
計:0.00 USD ※ごく少額
S3
- 保存容量
- 2 MB(アプリケーション)0.0015974 GB x 0.025 USD ≒ 0.00 USD
計:0.00 USD ※ごく少額
概算費用の合計
| サービス | 概算費用 |
|---|---|
| CloudFront | 0.00 USD ※ごく少額 |
| API Gateway | 0.02 USD |
| Lambda | 無料 |
| Lambda@Edge | 0.00 USD ※ごく少額 |
| Cognito | 無料 |
| DynamoDB | 0.00 USD ※ごく少額 |
| S3 | 0.00 USD ※ごく少額 |
| 合計 | 0.02 USD |
月額0.02 USD + α。 1 USDが150円と換算して、月額3円。
え、本当に?
自分で概算していて何ですが安すぎます。合っているのか不安になってきました。概算には「AWS 料金見積りツール」を使いました。公式ツールなので合っていると信じたい……。
DynamoDBはもう少し高くなると思っていました。これのおかげでDBがほぼ無料ということになりました。RDSやAuroraもこれくらい安くなってくれると嬉しいのですが。
正直なところ、テーブル設計がうまくできたとは思えず、クエリが最適ではないためリクエストが多くなってしまっています。NoSQL難しいです。
また、APIバックエンドであるLambdaが無料範囲内なのも大きいです。この無料枠は太っ腹だと思います。いや、Lambdaに限らずAWSの無料枠は全体的に太っ腹です。Cognitoの無料枠もすごい。
この試算通りならユーザーが増えても大丈夫そうですね。このWebアプリの用途上、そこまでユーザー数が増えることは考えにくいのですが、10倍になったとしてもそこまで費用が膨らまない気がします。
レガシー構成との比較もしてみる
レガシー構成と比較してみます。WebサーバーとDBサーバー、ロードバランサーを用意しましょう。
最小構成を目指します。こんな感じ?
- Webサーバー
- EC2:t3a.small
- EBS:10 GB
- DBサーバー
- Aurora サーバーレスv2 PostgreSQL 互換
- ACU:0.5
- ストレージ:25 GB
- Aurora サーバーレスv2 PostgreSQL 互換
- ALB
- 計算簡略化のために固定費分のみ計算
| サービス | レガシー構成 概算費用 | 本システム 概算費用 |
|---|---|---|
| EC2+EBS | 18.85 USD | – |
| Aurora | 58.38 USD | – |
| ALB | 17.74 USD | – |
| 合計 | 94.97 USD | 0.02 USD |
合計で月額 94.97 USDです。約14,246円。
比率で言うと、先ほどの概算より4748倍以上かかります。
サーバー費だけでこの結果です。さらに、可用性や運用コストでいえばサーバーレスアーキテクチャの方に軍配が上がるでしょう。良いことしかないじゃないか。
……都合が良すぎる概算結果にかなり狼狽えています。何か落とし穴があるかもしれません。
実際の費用
しばらく運用したので、費用を見てみます。
Cost Explorerでリソース別費用を確認すると、過去14日までしか遡れません。このWebアプリをデプロイしたAWSアカウントでは他のシステムも動いているため、サービス別で費用を見てもこのWebアプリの料金が導出できないのです。
リソース別に確認していくしかありません。なので過去10日分の費用を見てみて、それを3倍して1ヵ月分としたいと思います。
※後々社内で聞いたらちゃんと費用管理用のタグが用意されていました。
結果はこちら↓です。
費用はリソース別に確認しましたが、表にまとめるにあたって各サービス別にまとめなおしました。費用は四捨五入しています。
| サービス | 費用 | 概算費用 |
|---|---|---|
| CloudFront | 0.000 USD 以下 | 0.00 USD ※ごく少額 |
| API Gateway | 0.003 USD | 0.02 USD |
| Lambda | 0.001 USD | 無料 |
| Lambda@Edge | 0.001 USD | 0.00 USD ※ごく少額 |
| Cognito | 0.000 USD | 無料 |
| DynamoDB | 0.000 USD 以下 | 0.00 USD ※ごく少額 |
| S3 | 0.001 USD | 0.00 USD ※ごく少額 |
| 合計 | 0.01 USD | 0.02 USD |
試算の半分くらいの費用になっていました。
API Gatewayの費用を見るに、アクセスが少ないタイミングだったようです。じゃあサンプルとして微妙だな……。
注目したいのは、Lambdaに費用がかかっていました。無料枠を超えているとは思えないので、費用が掛かる場所を見落としているようですね。あとで確認しておきます。
なお、執筆中に知りましたが、このWebアプリのアクセスに関して、2025年10月~11月ごろに多くなるイベントがあるとのことです。こういうアクセススパイクにも慌てなくて良いのがクラウドやサーバーレスの良いところです。
思えば、今回のコストデータも「使われていないときは安い」というサーバーレスの長所が良く表れているんですね。
思いつく問題点
学習コストと意識
サーバーレスアーキテクチャは、システムのうち大部分をAWSマネージドサービスに任せる構成になります。ここに抵抗を覚える人が多いのではないでしょうか。
「何がどうなってるか知らないものを使う」というのは、精神衛生上良いものではありません。実は今回も「何がどうなってるか知らないものは使いたくない」という理由でAmplifyを使用しませんでした。怖くて……。
サーバーレスアーキテクチャを実装する場合、安心してマネージドサービスを使えるくらいにはAWSサービスの理解が必要になります。また、アプリ側もReactやVueといったSPA対応のJavaScriptフレームワークに限られます。フロントエンドとバックエンドでちゃんと分離し、疎結合なアーキテクチャ設計が必要です。そして、サーバーサイドレンダリングな言語は使えません(Lambdaでごちゃごちゃすればできそうですが)。
つまり、アプリ側もインフラ側も「サーバーレスアーキテクチャでいくぞ!」という気合を求められるわけです。メンバー全員の意識のかじ取りをインフラ側ができるかと問われると……。これは難しいプロジェクトの方が多いような気がします。
あと、学習コストの話に戻ってしまいますが、やはりDynamoDBは扱いが難しいです。
費用面ではこれを採用したのは正解だと思っています。
ですがこれを採用したために、このシステムは「誰でも簡単に保守運用できる」というものではなくなっています。
- SQLを知っている人
- DynamoDBのAPIを知っている人
こう比べてしまうと、どうしたって前者の方が多いでしょうしハードルも低いです。
フルマネージドサービス≠管理しなくていい
最初に「クラウドプロバイダーがサーバーやインフラストラクチャを管理してくれる」と書きました。サーバーレスアーキテクチャでは、フルマネージドサービスに寄せることでクラウド側がインフラ管理を肩代わりしてくれます。よく聞く説明だと思いますし、多くの人はこの認識だと思います。実際AWSが多くのことを管理してくれています。
ですが、今回触ってみて感じたのは、「サーバーを直接管理しない」ということと「管理しなくていい」ということはまったく別だということです。
LambdaやAPI Gateway、DynamoDBなどは確かにフルマネージドサービスであり、スケーリングや可用性といった基盤部分はAWSが担ってくれます。しかし、その一方で、各サービスの設定・監視・セキュリティ設計・コスト管理といった運用は依然として必要です。
従来のアプリ開発で行っていた「サーバー構築・保守」からは解放されましたが、代わりに「AWSサービス同士をどう安全かつ効率的に連携させるか」という新しい種類の運用が発生します。
つまり、「サーバー運用から解放されて機能開発に集中できる」というよりも、「AWSの各サービスをどう管理して機能を実現するか」に集中するアーキテクチャだと感じました。
サーバーレスとは、サーバーを隠す仕組みではなく、クラウドサービスを理解し、適切に管理して活かす考え方だと思いました。
おわりに
ぶっちゃけた話、今の私のグループにはサーバーレスアーキテクチャに意欲がある人は多くありません。なぜなら、音声認識というタスクとサーバーレスアーキテクチャは相性がそんなによくないからです。
それでも、今回のようにWebページ部分をサーバーレスにしたり、あるいは運用タスクをAWSサービスで自動化したりして、開発運用保守といったタスクの効率化、ランニングコストの削減をしていけたら良いなと思います。
音声認識技術をお客様に届けるにあたって、このあたりミッションは絶対に無視できないものですので。
もし
- 既存システムをサーバーレス化するのが好き
- 運用は効率的にやりたい
- 自動化大好き
- なんなら「AWS・Azure好き」
といった方々。アドバンスト・メディアで働くことを一度検討して頂けると、私としては嬉しいです。お待ちしています。
本記事は以上になります。ここまで読んでくださり、ありがとうございました。
他のTechブログも面白いです。よかったらアーカイブ覗いてみてください。
この記事を書いた人
-

すたあ
転職勢。AWSをよく触る。
ゲームをする、本(小説も漫画も)を読む、アニメを見るなど活動は多岐にわたる。
人と会う前になぜかクレーンゲームをしてしまい、でかいぬいぐるみを抱えていったことがある。
よく見られている記事
新着記事
- AWSでサーバーレスなWebアプリを作ってみた話
- 「AWS Summit Japan 2025」に出展しました
- 【作ってみた!】音声認識+生成AIで便利ツール~AmiVoiceライブラリとPower Automateでノーコード作成~
