<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: minaduki</title>
    <description>The latest articles on DEV Community by minaduki (@_ad122393ca44f5a698b1).</description>
    <link>https://dev.to/_ad122393ca44f5a698b1</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4166302%2Fd2c46833-ad61-4324-aeb0-793988e86bf2.jpg</url>
      <title>DEV Community: minaduki</title>
      <link>https://dev.to/_ad122393ca44f5a698b1</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/_ad122393ca44f5a698b1"/>
    <language>en</language>
    <item>
      <title>【完全無料・サーバーレス】AWS Lambda FastAPI で作る「お出かけホテル検索マップ」の手順</title>
      <dc:creator>minaduki</dc:creator>
      <pubDate>Fri, 09 Oct 2026 04:59:31 +0000</pubDate>
      <link>https://dev.to/_ad122393ca44f5a698b1/wan-quan-wu-liao-sabaresu-aws-lambda-fastapi-dezuo-ruochu-kakehoterujian-suo-matupu-noshou-shun-bj3</link>
      <guid>https://dev.to/_ad122393ca44f5a698b1/wan-quan-wu-liao-sabaresu-aws-lambda-fastapi-dezuo-ruochu-kakehoterujian-suo-matupu-noshou-shun-bj3</guid>
      <description>&lt;p&gt;こんにちは！今回は、自宅の重いサーバーや有料のホスティング環境を使わず、AWS Lambdaの永年無料枠を活用して、ほぼ完全無料（サーバー代0円）で動くホテル検索マップアプリを個人開発したので、その仕組みと作り方をシェアしたいと思います。&lt;br&gt;
&lt;a href="https://kvar3quzrcgvz7loutkcftcoti0gppjv.lambda-url.ap-northeast-3.on.aws/" rel="noopener noreferrer"&gt;hotel map&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1re8o1piatwksx23u2i8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1re8o1piatwksx23u2i8.png" alt=" " width="800" height="544"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;フロントエンドの地図操作から、バックエンドのPython（FastAPI）、そして安全に公開するためのセキュリティ対策までギュッとまとめました。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;このアプリで実現したこと・アーキテクチャ
地図上で中心地を選ぶと、その周辺にあるホテルを自動で検索して一覧・マップ表示してくれるアプリです。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;フロントエンド: HTML / JavaScript （Google Mapsをインタラクティブに操作）&lt;/p&gt;

&lt;p&gt;バックエンド: Python 3.11 ＋ FastAPI&lt;/p&gt;

&lt;p&gt;インフラ（ホスティング）: AWS Lambda（関数URL）＋ Mangum&lt;/p&gt;

&lt;p&gt;外部API: SerpAPI（Google Hotels）&lt;/p&gt;

&lt;p&gt;「サーバーレス」構成にすることで、アクセスがないときの維持費は完全0円。個人開発のサービス公開やポートフォリオにぴったりの構成です。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;実装のポイントとハマりどころ
今回の開発でこだわったポイントや、実際に躓きやすいポイントをいくつか紹介します。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;① 重いSDKを排除し、requests 直叩きで軽量化&lt;br&gt;
AWS Lambdaで動かす際、パッケージサイズが大きすぎるとデプロイやコールドスタート（初回起動）のパフォーマンスに影響します。今回は余計な重いSDKを使わず、標準的な requests ライブラリだけでSerpAPIのエンドポイントを直接叩くシンプルな設計にしました。&lt;/p&gt;

&lt;p&gt;② Windows環境でのZIP地獄をPowerShellでスマートに解決&lt;br&gt;
Lambdaに外部ライブラリ（fastapi, requests など）を同梱してアップロードするためには、依存関係を含めたZIPファイルを作る必要があります。Windows環境ではPowerShellを使って以下のように一撃でビルド・圧縮する仕組みを整えました。&lt;/p&gt;

&lt;p&gt;PowerShell&lt;/p&gt;

&lt;h1&gt;
  
  
  依存関係のインストールとZIP圧縮のスクリプト例
&lt;/h1&gt;

&lt;p&gt;Remove-Item -ErrorAction SilentlyContinue deployment.zip&lt;br&gt;
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue package&lt;br&gt;
pip install --platform manylinux2014_x86_64 --target package --implementation cp --python-version 3.11 --only-binary=:all: fastapi uvicorn mangum pydantic requests&lt;br&gt;
New-Item -ItemType Directory -Force package | Out-Null&lt;br&gt;
Copy-Item main.py package/&lt;br&gt;
if (Test-Path templates) { Copy-Item -Recurse -Force templates package/ }&lt;br&gt;
Add-Type -AssemblyName System.IO.Compression.FileSystem&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;安全に公開するための「4つのセキュリティ対策」
個人でWebアプリを公開する場合、APIキーの流出や不正利用（予期せぬコスト発生）を防ぐ対策が不可欠です。今回は以下の万全な備えを行っています。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;APIキーの完全隠蔽&lt;/p&gt;

&lt;p&gt;Google MapsやSerpAPIのキーはソースコードに直書きせず、すべてAWS Lambdaの環境変数として安全に管理しています。&lt;/p&gt;

&lt;p&gt;Google Maps APIの「HTTPリファラー制限」&lt;/p&gt;

&lt;p&gt;万が一キーが露出しても他のサイトで勝手に使われないよう、Google Cloud Console側で「自分のLambda関数URL」からしかマップが読み込めないよう制限をかけています。&lt;/p&gt;

&lt;p&gt;アプリ側の簡易レートリミット（回数制限）&lt;/p&gt;

&lt;p&gt;不正なボット等による無限スクレイピングを防ぐため、IPアドレスごとに「1人あたり3回まで」の検索制限をコード内に実装しています。&lt;/p&gt;

&lt;p&gt;エラーメッセージのマスク処理&lt;/p&gt;

&lt;p&gt;バックエンドで万が一エラーが起きた際、内部のシステム詳細やパスが画面に露出しないよう、クライアント側には一律で Internal Server Error を返し、詳細な原因はAWSのCloudWatch Logsにだけ記録するようにしています。&lt;/p&gt;

&lt;p&gt;さらに、AWS Budgets（予算アラート）を設定して、万が一のコスト発生時にも即座に気づける体制にしています。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;サンプルコード（バックエンド: main.py）
実際にAWS Lambda（Mangum経由）で動かしているFastAPIのメインコードはこちらです。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Python&lt;/p&gt;

&lt;p&gt;import os&lt;br&gt;
import math&lt;br&gt;
from collections import defaultdict&lt;br&gt;
import requests&lt;br&gt;
from fastapi import FastAPI, HTTPException, Query, Request&lt;br&gt;
from fastapi.responses import HTMLResponse&lt;br&gt;
from mangum import Mangum&lt;/p&gt;

&lt;h1&gt;
  
  
  環境変数からAPIキーを取得
&lt;/h1&gt;

&lt;p&gt;GOOGLE_MAPS_API_KEY = os.getenv("GOOGLE_MAPS_API_KEY", "")&lt;br&gt;
SERPAPI_API_KEY = os.getenv("SERPAPI_API_KEY", "")&lt;/p&gt;

&lt;h1&gt;
  
  
  IPごとの簡易検索回数管理（3回まで）
&lt;/h1&gt;

&lt;p&gt;search_counts = defaultdict(int)&lt;br&gt;
MAX_SEARCH_LIMIT = 3&lt;/p&gt;

&lt;p&gt;def check_search_limit(request: Request):&lt;br&gt;
    client_ip = request.client.host if request.client else "unknown"&lt;br&gt;
    if search_counts[client_ip] &amp;gt;= MAX_SEARCH_LIMIT:&lt;br&gt;
        raise HTTPException(&lt;br&gt;
            status_code=429,&lt;br&gt;
            detail="無料体験の検索回数（3回）に達しました。ページを再読み込みしてください。"&lt;br&gt;
        )&lt;br&gt;
    search_counts[client_ip] += 1&lt;/p&gt;

&lt;p&gt;app = FastAPI()&lt;/p&gt;

&lt;p&gt;@app.get("/api/search-vacant-hotels")&lt;br&gt;
def search_vacant_hotels(request: Request, ...):&lt;br&gt;
    check_search_limit(request)&lt;br&gt;
    try:&lt;br&gt;
        # ホテル検索ロジック...&lt;br&gt;
        return {"hotels": []}&lt;br&gt;
    except HTTPException:&lt;br&gt;
        raise&lt;br&gt;
    except Exception as e:&lt;br&gt;
        print(f"[Backend Exception]: {e}")&lt;br&gt;
        raise HTTPException(status_code=500, detail="Internal Server Error")&lt;/p&gt;

&lt;p&gt;@app.get("/", response_class=HTMLResponse)&lt;br&gt;
def read_root():&lt;br&gt;
    # HTMLテンプレートを読み込んでAPIキーを埋め込む処理&lt;br&gt;
    ...&lt;/p&gt;

&lt;p&gt;handler = Mangum(app)&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;おわりに
サーバーレスと聞くと難しそうに感じるかもしれませんが、AWS Lambdaの関数URLとFastAPIを組み合わせれば、驚くほど手軽にモダンなWebアプリを公開できます。&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>api</category>
      <category>aws</category>
      <category>python</category>
      <category>serverless</category>
    </item>
    <item>
      <title>🎮 Flutterで “にこにこ九九ゲーム” を作った話 子どもが笑顔で学べるアプリを目指して</title>
      <dc:creator>minaduki</dc:creator>
      <pubDate>Thu, 08 Oct 2026 12:02:59 +0000</pubDate>
      <link>https://dev.to/_ad122393ca44f5a698b1/flutterde-nikonikojiu-jiu-gemu-wozuo-tutahua-zi-domogaxiao-yan-dexue-beruapuriwomu-zhi-site-124c</link>
      <guid>https://dev.to/_ad122393ca44f5a698b1/flutterde-nikonikojiu-jiu-gemu-wozuo-tutahua-zi-domogaxiao-yan-dexue-beruapuriwomu-zhi-site-124c</guid>
      <description>&lt;p&gt;&lt;a href="https://play.google.com/store/apps/details?id=com.junsa.kukuapp8&amp;amp;pli=1" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>【個人開発】米国向けに完全広告なしの有料地図アプリ「US Map Quiz」をリリースしました</title>
      <dc:creator>minaduki</dc:creator>
      <pubDate>Thu, 08 Oct 2026 03:40:49 +0000</pubDate>
      <link>https://dev.to/_ad122393ca44f5a698b1/ge-ren-kai-fa-mi-guo-xiang-keniwan-quan-guang-gao-nasinoyou-liao-di-tu-apurius-map-quiz-woririsusimasita-5b75</link>
      <guid>https://dev.to/_ad122393ca44f5a698b1/ge-ren-kai-fa-mi-guo-xiang-keniwan-quan-guang-gao-nasinoyou-liao-di-tu-apurius-map-quiz-woririsusimasita-5b75</guid>
      <description>&lt;p&gt;こんにちは！個人開発者の水無月です。&lt;/p&gt;

&lt;p&gt;先日、US（米国）市場をターゲットにした完全広告なしの教育アプリ「US Map Quiz」（パッケージ名: com.junsa.us_mapquiz）をGoogle Play Storeに無事リリースしました。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://play.google.com/store/apps/details?id=com.junsa.us_mapquiz" rel="noopener noreferrer"&gt;US Map Quiz &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/shorts/dFpxPCJbrCU" rel="noopener noreferrer"&gt;demo youtube&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;今回のアプリは、単なるクイズアプリではなく、「米国市場の購買力・為替を意識したプライシング」「徹底したノンアド（広告なし・追加課金なし）によるクリーンな学習体験」「最新のAndroidポリシー対応」など、個人開発者としていくつかの新しいチャレンジを盛り込んで制作しました。&lt;/p&gt;

&lt;p&gt;この記事では、企画からリリース、そして審査でつまずいたポイントまで、開発の舞台裏をすべてシェアします。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;なぜ「USマップクイズ」を作ったのか？
これまでも様々なアプリを開発してきましたが、今回は明確に「米国市場（US Market）」をターゲットに据えて企画をスタートしました。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;理由1：購買力とエコシステム 日本のマーケットだけでなく、英語圏の広大なユーザー層、そして適正な価格設定（1.99ドル〜2.99ドル付近の買い切り型）による収益化の検証をしたかったこと。&lt;/p&gt;

&lt;p&gt;理由2：「広告なし」の価値 世の中の無料学習アプリの多くは、画面下部にバッチリ広告が出たり、ポップアップ広告で子供が誤タップしたりとストレスフルです。「完全広告なし・安全・クリーン」という価値は、特にアメリカの親御さんやホームスクール（自宅学習）層に強く刺さると確信していました。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;技術スタックとこだわりの設計
開発には使い慣れたFlutterを採用し、クロスプラットフォームでスムーズに動作する軽量なアプリを目指しました。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;フロントエンド / UI: Flutter (Dart)&lt;/p&gt;

&lt;p&gt;マップデータ: クッキリと拡大・縮小できる美しいSVGベクターマップを活用し、スマホのどの画面サイズでも綺麗に描画されるように調整。&lt;/p&gt;

&lt;p&gt;ターゲット: US市場に特化するため、言語・UIは英語ネイティブが自然に使えるシンプルなデザインに徹底。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;リリースまでの「リアルな障壁」と解決策
順風満帆に見えたリリースですが、個人開発ならではの技術的ハードルやポリシーの壁にぶつかりました。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;① Android 15の「16KBメモリページサイズ」対応&lt;br&gt;
Androidの最新要件である16KBページサイズへのメモリ対応。ビルドやパッケージの構成を見直し、ネイティブレイヤーでの互換性をクリアするのに少し苦労しましたが、無事にクリア。&lt;/p&gt;

&lt;p&gt;② Google Playのメタデータポリシーで一発レッドカード&lt;br&gt;
ストアに初回申請を出した際、アプリ名（タイトル）に「US Map Quiz No Ads」と設定していたところ、審査でポリシー違反を食らいました。&lt;/p&gt;

&lt;p&gt;違反理由: ストアの掲載情報（タイトルやアイコンなど）に、価格やプロモーションに関する詳細（「セール」「無料」、そして「広告なし（No Ads）」などの宣伝文句）を含めてはならない。&lt;/p&gt;

&lt;p&gt;「広告なし」という最大の売り文句をタイトルに入れたい気持ちをぐっとこ堪え、タイトルは純粋な US Map Quiz に修正。「広告なし」の魅力はストアの「詳細説明文」で熱く語る方針へと切り替えました。この修正を経て、無事に審査を通過し、プロダクション公開へと漕ぎ着けました。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;これからのプロモーション戦略（ショート動画で勝負）
無事にストアに並んだ今、最大の課題は「どうやってユーザーに見つけてもらうか」です。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;今回は広告費をかけず、YouTube Shorts（ショート動画）を活用したグローバル展開に挑戦しています。 「あなたの子供は50州全部言える？」というフックから始まり、テンポよくクイズを解いていく様子を見せる15秒〜20秒の縦型動画を作成し、世界中の親御さんや学習層にアプローチ中。CapCutを使ってサクッと制作できるので、個人開発者にとっては最高の露出チャネルだと感じています。&lt;/p&gt;

&lt;p&gt;さいごに&lt;br&gt;
アイデアの構想から、技術的なトラブル、ポリシー違反の修正、そしてストア公開に至るまで、個人開発の醍醐味をぎゅっと凝縮したプロジェクトになりました。&lt;/p&gt;

&lt;p&gt;「広告なしの有料アプリで勝負してみたい」という開発者の方の参考になれば嬉しいです。&lt;/p&gt;

</description>
      <category>android</category>
      <category>mobile</category>
      <category>showdev</category>
      <category>startup</category>
    </item>
  </channel>
</rss>
