フリマアプリ「LOVEBAIT(ラブベイト)」を、AI(Claude)の助けも借りながら作り、App StoreとGoogle Playに出しました。構成と、詰まったところ・直したところをまとめます。うまくいった話だけでなく、審査で一度却下された話や、本番に入れてはいけない物を入れていた話も書きます。
- サイト: https://lovebait.love-ap.net/app?utm_source=qiita&utm_medium=article&utm_campaign=devstory
- 作者: 鹿島公胤(KASSI)。運営は合同会社LOVE。
1. 全体の構成
| 層 | 使ったもの |
|---|---|
| Web本体 | Next.js(App Router)、Vercelでホスティング |
| iOS / Android | Expo(React Native)の薄い殻。中身は、WebViewでWeb本体を表示。カメラ・音声入力・プッシュ通知だけネイティブ連携 |
| 決済 | Stripe(Connect)。出品者の本人確認(KYC)はStripeの手続き |
| 画像・ファイル | Vercel Blob |
| メール | Resend |
| 認証 | 自前のセッション(jose)、Googleログイン |
| AI | 写真解析・音声の整理・英訳。Gemini(既定)とClaude(切替可)を、共通の口で呼ぶ |
| 多言語 | アプリの画面は9言語(日・英・中・独・仏・西・伊・韓・葡)。Webの紹介ページにはロシア語もあり |
2. なぜ「WebView殻」にしたか
1人+AIで、Web・iOS・Androidを同じ中身で、早く出したかったからです。Web本体を直せば、アプリも(ストアの再審査なしで)変わります。
一方で、ストアの審査では不利になることがあると、後で分かりました(後述)。
3. 写真から出品の案を作る
写真を撮ると、AIが「商品名・ブランド・カテゴリ・状態・参考価格・説明・タグ」をJSONで返します。型は zod で縛り、スキーマに合わない返答は捨てて、エラーにします。
const AnalysisSchema = z.object({
title: z.string(),
brand: z.string(),
condition: z.enum(["未使用", "新品", "美品", "良好", "やや傷あり"]),
suggestedPrice: z.number(),
description: z.string(),
tags: z.array(z.string()),
authenticityRisk: z.boolean(), // 価格の不自然さの簡易判定
authenticityRiskReason: z.string(),
});
- AIの案は、そのまま出品しない。出品者が、読んで直す前提の画面にしています。
- LLM呼び出しは、
generateStructured({ prompt, schema, imageUrl })の1つの口にまとめ、環境変数で Gemini↔Claude を切り替えられます(費用を測るため、使ったトークン数もログに残します)。 - 音声入力は、
expo-speech-recognitionで聞き取ったテキストを、AIに渡して項目に整理します。既存の入力を消さず、補う動きにしています。
4. 偽造品対策(完全ではない、が、やったこと)
フリマは、構造上、コピー品が紛れ込みます。決済のStripeにも、制限業種(偽造品など)があります。そこで:
- 出品時に、「本物であり、他人の権利を侵していない」への同意チェックを必須に(サーバー側でも二重に確認)
- AIが、ブランド名と価格の不自然さを参考として知らせる(出品は止めない。誤検知で、正規の掘り出し物を止めたくないため)
- 商品ページに通報ボタン(運営へメールで通知。保存と通知を二重化)
- 買った人の受け取り確認まで、代金を出品者に渡さない
「偽造品ゼロ」とは言えません。参考機能であり、真贋を保証するものではないと、画面と説明に書いています。
5. 審査で詰まったところ
(a) App Store: ガイドライン2.3.1(a)「説明にない隠れた機能」で却下
- 却下の理由は「バイナリの中に、出品・価格データ、審査時に有効化されなかったバックエンド接続のコードがある」という趣旨でした。
- WebView殻なので、審査員がアプリを触った限りでは、サーバーとの通信が見えにくかったのが一因だと考えています。
- 返信で、「WebView殻であること・全ユーザー同じ動作であること」を説明し、さらに「ブラウズ・検索・詳細表示の間の通信を確認できる手順」を送りました。その後、審査が完了し、公開されました。
(b) Google Play
- 初期は、Webを包む形(TWA)で出していました。のちに、Expoの殻に置き換えました。
- Expo SDK 52 → 54 に上げました。Google Playの16KBのメモリページサイズへの対応のためです。
6. 本番に「サンプル」を混ぜていた話(反省)
開発中に作った架空のサンプル商品(約100件)が、本番のホームとサイトマップに出ていました。検索にも載る状態で、実在の商品に見える危険がありました。さらに、決済の処理が、サンプル商品でも決済セッションを作る分岐を持っていました(実際に購入できる恐れ。本番では試していません)。
対応:
- 決済・入札・コメントを、サンプルではAPIで拒否
- 一覧はサーバー配信に切り替え、サンプルには「サンプル」表示を付けました
- ページは
noindex、サイトマップから除外
開発用のデータは、本番のコードから、はっきり分けておく。AIとの開発でも、レビューは人が見る、と実感しました。
7. AIと開発して、よかったこと・困ったこと
よかった
- 設計の相談から、コード、テスト、文面まで、一人で見る量を減らせた
- 規約や審査の返信文など、文章の下書きが早い
困った
- 長い会話が続くと、前提のずれが出る。メモ(決めたこと)を、ファイルに残すのが必須
- 実機でしか分からない問題(WindowsのPowerShellで、
eas-cliの対話入力に貼り付けるとクラッシュする、など)は、人が見つける - 決済・規約・審査の返信は、最終判断を人がする
8. 今後
- 出品を増やす(いまは、出品がまだ少ない段階です)
- アプリ内の言語宣言、検索まわり(ASO・SEO)の改善
ご意見・ご指摘を、お待ちしています。(お問い合わせ: info@love-ap.net)
LOVEBAIT(ラブベイト)は、App Storeと Google Play で公開中です。「ラブベイト」で検索してください。
この記事は、AIを使って下書きし、作者(鹿島公胤)が確認しています。内容は2026年10月時点です。売れる・稼げることを保証するものではありません。