はじめに
はじめに自己紹介を少し。
業務はオンプレミス系のインフラ保守・試験がメインで、Web系開発もAWSも実務経験はゼロです。いわゆる「インフラ寄りのSE」です。
そんな自分がAWSのSAA(Solutions Architect Associate)を取得したものの、資格を取っただけでは実際に手を動かした感覚がありませんでした。
「ハンズオンで何か作りたい。せっかくなら自分が使えるものを。」
趣味はサーフィンです。波情報アプリは世の中に既にたくさんあります。正直それで十分です。ただ、自分で作るなら極力お金をかけたくない。低コストで実用的なものを作れるかを試してみたくなりました。
そこで生まれたのが波情報通知Webアプリ「WavePilot」です。
この記事では、実務未経験者がゼロから作った構成として、詰まったポイントを中心に共有します。同じように「資格は取ったけど何か作ってみたい」という方の参考になれば幸いです。
作ったもの
WavePilot:サーファー向けの波情報通知Webアプリ
- お気に入りのサーフポイントを登録
- 地図上で指定したポイントの波高・風速・潮位などの情報を表示
- お気に入りポイントの波情報をLINEで通知
GitHubリポジトリ:mugicha0141/WavePilot
アーキテクチャ
使用技術は以下の通りです。
| カテゴリ | 技術 |
|---|---|
| フロントエンド | React |
| バックエンド | Node.js / Lambda |
| データベース | DynamoDB |
| 認証 | Cognito |
| 配信 | CloudFront / S3 |
| API | API Gateway |
| IaC | Terraform |
| ローカル環境 | Docker Compose / LocalStack |
低コスト設計へのこだわり
このアプリの一番のコンセプトは「お金をかけない」ことです。
既存の波情報アプリで十分事足りる中で自分で作る意味を持たせるために、従量課金・サーバーレスを最大限活用する構成を意識しました。
| サービス | 選定理由 |
|---|---|
| Lambda | リクエスト時のみ課金、常時起動不要 |
| DynamoDB | オンデマンドモードで使った分だけ課金、RDSより低コスト |
| S3 + CloudFront | 静的ホスティングで安価、アクセスが少なければ数円程度 |
| Cognito | 月10,000 MAUまで無料(恒久的) |
| LocalStack | ローカル開発でAWSへのリクエストを削減 |
個人開発レベルのアクセス数であれば月100〜300円程度に収まる設計です。
技術選定の理由
フロントエンド:React
地図上のサーフポイントを選択すると、ページ遷移なしに波高グラフがその場で更新されるインタラクティブなUIを実現したかったためReactを選択しました。useStateで選択ポイントの状態を管理し、useEffectでデータ取得・グラフ更新をトリガーする設計にしています。
バックエンド:Lambda + Node.js
サーバーを常時起動させるコストを避けたかったためLambdaを選択。波情報の取得はAPIリクエスト時のみ発生するため、サーバーレスとの相性が良いと判断しました。
データベース:DynamoDB
低コスト設計のコンセプトに合わせてDynamoDBを選択しました。RDSは常時起動のため最低でも月数百円〜のコストが発生しますが、DynamoDBはオンデマンドモードで使った分だけの課金になります。個人開発レベルのアクセス数であれば月数円〜数十円程度に収まります。
認証:Cognito
当初は自前でJWTを実装して認証を行いました。自前JWTでも認証自体は実装できますが、一度発行したトークンの強制無効化が難しく、ユーザー管理・パスワードリセットなども自前で実装する必要がありました。トークンの強制無効化と実装の手間を考慮してCognitoに移行しました。
配信:CloudFront + S3
ReactのビルドファイルをS3に配置し、CloudFront経由で配信する構成にしました。低コストで高速なコンテンツ配信が実現できます。
IaC:Terraform
インフラをコードで管理することで、環境の再現性と破壊・再構築の容易さを実現しました。terraform apply一発でAWS環境が構築できる状態を目指しました。
ローカル環境:Docker Compose + LocalStack
AWSリソースをローカルでエミュレートするためLocalStackを採用。開発中のAWSコスト削減と、環境差異を最小化することが目的です。
実装で詰まったポイント
APIのURLが丸見え問題
友人にブラウザの開発者ツールを見せたところ、「APIのURLが丸見えだよ」 と指摘されたことがセキュリティ対策のきっかけでした。
URLが知られれば認証なしで誰でもAPIを叩ける状態だったため、まずJWTで一時トークンを発行する仕組みを実装しました。しかしリクエスト自体は開発者ツールで見えてしまうため、根本的な解決にはなりませんでした。
解決策:自前JWTからCognitoに移行しました。自前JWTでも認証は実装できますが、一度発行したトークンの強制無効化が難しく、ユーザー管理も自前でやる必要がありました。Cognitoに移行することでその手間を省きつつ、トークンの強制無効化も可能になりました。
なおURLとトークンが両方盗まれた場合は防げませんが、その対策はパスワード強度や二段階認証の領域になります。「リクエストを隠す」ではなく「ログインしていないと使えなくする」という方向での解決です。
波情報APIの無料枠が1日10回問題
使用している波情報APIの無料枠が1日10リクエストまでという制限がありました。地図をクリックするたびに1リクエスト消費するため、同じ場所を何度も確認するだけですぐに上限に達してしまいます。
解決策:1週間分の一括取得+お気に入り登録+キャッシュ機能の実装
まず1回のリクエストで1週間分のデータをまとめて取得するようにしました。よく行くサーフポイントをお気に入り登録すると、座標と取得した1週間分の波情報データをDynamoDBにキャッシュする仕組みにしました。次回以降はキャッシュからデータを表示するため、お気に入りポイントの確認ではAPIリクエストを消費しません。
ただし1週間を超えた場合は再度APIリクエストが必要になります。地図クリックで新しいポイントを探す時と期間外のデータが必要な時だけリクエストを消費し、よく行くポイントは基本的にキャッシュで賄う設計にすることで無料枠内で運用できるようになりました。
コスト制約がアーキテクチャの改善につながった好例だと思っています。
まとめ
「SAAを取ったからには実際に手を動かしたい」「どうせ作るなら低コストで実用的なものを」という動機から始まったWavePilotですが、サーバーレス構成はその目的に非常にマッチしていました。
開発で使ったツールについて
実務未経験ということもあり、わからないことだらけでした。そこでClaude(AI)をフル活用しました。
エラーの原因、設計の相談、コードレビューなど、詰まったときは片っ端から聞きまくりました。「AIを使うのはどうか」という意見もあると思いますが、設計の意図や技術選定の理由は自分で考えた上で、実装の壁をAIで乗り越えるというスタンスで開発しました。
次の記事では使用したAWSサービスの詳細や、React・Node.jsの実装について書く予定です。
今後の追加予定
- CloudWatch:Lambdaのエラーログ監視・APIリクエスト数の監視
- GitHub Actions:CI/CDパイプラインの構築



