個人開発こそAmplify Gen2で爆速開発しよう！~自分用の家計管理レシートスキャンアプリを作成した話~

# 「誰が何を払った？ いまいくら借りている？」問題を一発解決！
みなさん、こんにちは！
今回は、個人開発でサクッと作った「ReceiptSplit」を紹介します【Amplify Gen2活用】

**作成したサービスはこちら！ → [https://www.receipt-split.com](https://www.receipt-split.com)**


- 「この牛乳って誰の分だったっけ？」
- 「あの鶏むね肉はオレが払ったと思うけど、記録どこ行った…？」

共同生活(同棲、シェアハウス etc ...)をしていると、こんな“モヤモヤ”したお金の管理、意外とありますよね。
そんな悩みを超絶ラクにしてくれる個人開発アプリ「ReceiptSplit」を作ってみました！

最大の特長は、AWS Amplify (Gen2) を活用した超スピード開発＆簡易運用。さらに、AI(Gemini)でレシート読み取りを自動化！「やってられん！」と思っていた手動管理が超スマートに。

この記事では、開発背景から技術選定、Amplifyを用いるメリットをお見せします。
Amplifyや個人開発に興味がある方は必見！

# なぜ作った？ ～開発の背景～
私事ですが今年の8月から、共同生活を始めました。がとある問題が発生しました。
例えば、食材や生活用品を誰が買ったのか、どれくらい使ったのか。単純に割り勘にすると「自分はあまり使ってないのに多く払っている気がする…」といった不公平感が出てしまうことも。
そういったように、共同生活者の食費や生活費のバランスが崩れ、それをレシートと照らし合わせて手作業で、「誰がいつ何を買ったか」をGoogleスプレッドシートで必死に管理していました。
でも、手入力が面倒＆雑になる、レシートが山積みになる、割り勘が不公平…とストレスMAX。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2005049/cfcab5ee-5ca9-a4e1-b1e7-9ecf9e8cc830.png)

そこで、**「もう自分専用ツールをパパッと作ろう！」** と思い立ち開発したのが「ReceiptSplit」です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2005049/cfb510df-d996-6320-38bc-ad366c3a8dc8.png)


# ReceiptSplitって何ができるの？
- レシート自動スキャン (AI解析): スマホでレシートをパシャッ → AIで品目自動抽出
- 誰が何を買ったか選択: 品物ごとに誰の分なのかをワンタップで選択
- ラクラク履歴管理: 過去の支払い履歴や合計の貸し借り金額を表示

つまり、共同生活中の「費用管理モヤモヤ」を完全クリア！
不公平感ゼロのスッキリな金銭コミュニケーションが実現します。

# 技術選定のキモ
「スピード最優先＆慣れた技術」をテーマに選びました。
- バックエンド: Amplify Gen2
- フロントエンド: React + MUI

React + MUIでサクッと美しいUIを構築。
Amplify Gen2でデータモデル定義からAPI、認証、デプロイまで一気通貫で楽々。
結果、爆速でプロトタイプ→実用レベルまで到達できました。

## なぜAmplify Gen2？
- 面倒なバックエンド構築を一掃: DB接続やGraphQL API定義がほぼ自動
- フルスタック開発の時短: フロントとバックがシームレスにつながる
- 楽ちんデプロイ&ホスティング: GitHub連携で自動ビルド→即公開

「個人開発 = 面倒なサーバー周り構築で疲弊」というあるあるを回避できたのがデカい！

Amplifyのおかげで、プロトタイプの段階からサクサクと開発を進められ、個人開発のスピード感と自由度を最大限活かすことができました。

### スキーマ定義のサンプル
Amplifyでは、下記のようなTypeScriptコードでDynamoDBスキーマを簡単に定義できます。  
モデル同士のリレーションも楽々！個人開発者にとってこの手軽さは衝撃的でした。
以下が、今回のサービスで用いたスキーマー定義です。
```typescript
const UserType = {
  TARO: "TARO",
  JIRO: "JIRO",
} as const;

const ConsumerType = {
  ...UserType,
  SPLIT: "SPLIT",
} as const;

// スキーマ定義例
const schema = a.schema({
  Item: a
    .model({
      name: a.string().required(),
      price: a.integer().required(),
      discount: a.integer().required(),
      quantity: a.integer().required(),
      consumer: a.enum(Object.values(ConsumerType)),
      receiptId: a.id().required(),
      receipt: a.belongsTo("Receipt", "receiptId"),
    })
    .authorization((allow) => [allow.guest()]),
  Receipt: a
    .model({
      shopName: a.string(),
      shopAddress: a.string(),
      shopPhone: a.string(),
      date: a.string().required(),
      time: a.string().required(),
      total: a.integer(),
      payer: a.enum(Object.values(UserType)),
      approved: a.boolean().default(false),
      items: a.hasMany("Item", "receiptId"),
      debts: a.hasOne("Debt", "receiptId"),
    })
    .authorization((allow) => [allow.guest()]),
  Debt: a
    .model({
      from: a.enum(Object.values(UserType)),
      to: a.enum(Object.values(UserType)),
      amount: a.integer(),
      isTaxIncluded: a.boolean().default(true),
      receiptId: a.id().required(),
      receipt: a.belongsTo("Receipt", "receiptId"),
    })
    .identifier(["receiptId"])
    .authorization((allow) => [allow.guest()]),

  geminiAnalyzeReceiptFromBase64: a
    .query()
    .arguments({ base64: a.string(), mime: a.string() })
    .returns(a.json())
    .handler(a.handler.function(geminiAnalyzeReceiptFromBase64))
    .authorization((allow) => [allow.guest()]),
});
```
ポイント:
- Receipt ↔ Item は1対多
- Receipt ↔ Debt は1対1
- 型とリレーションが直感的に定義でき、フロントもそのまま型を享受可能

### フロントエンドとバックエンドのシームレスな連携
Amplifyのおかげで、バックエンドで定義したスキーマが自動的にフロントで使えるように。
Receipt作成はこんなにシンプル！

```typescript
import type { Schema } from "../../amplify/data/resource";
import { generateClient } from "aws-amplify/data";
const amplifyClient = generateClient<Schema>();

await amplifyClient.models.Receipt.create({
  shopName: data.shopName,
  shopAddress: data.shopAddress,
  shopPhone: data.shopPhone,
  date: data.date,
  time: data.time,
});
```
これにより、バックエンド側の細かいロジックを意識せずに、フロントエンドから必要なデータ処理が可能です。

### デプロイ＆ホスティングもおまかせ！
AmplifyでGitHub連携すれば、ブランチプッシュで自動ビルド・自動デプロイが可能。
サーバーレスでスケールも心配なし。「インフラ整備？なにそれ？」な感じで本来の開発に集中できます。

# アーキテクチャ概要
```
(React) -> (Amplify: GraphQL API) -> (DynamoDB)
                          |
                          v
                        (Lambda) -> (Gemini AI for receipt analysis)
```
- **フロントエンド (React)**: ユーザーインターフェースを提供し、Amplifyが生成したGraphQL APIを通じてバックエンドと通信します
- **Amplify (GraphQL API, Auth, Storage)**: GraphQL APIを介してDynamoDBとやり取りし、ユーザー認証やアクセス制御もここで実装します
- **DynamoDB**: 購入アイテムやレシート情報などを保存するNoSQLデータストア
- **Gemini (AI解析)**: レシート画像やテキストデータを解析し、購入商品や金額を抽出します。AmplifyのLambda関数からGeminiを呼び出しています

結果、面倒な細部をAmplifyがカバーし爆速で個人開発アプリを構築できました！

## 実装上の工夫ポイント
1. スマホでの見やすさ重視: テーブル表示よりカードUIを採用し、小さい画面でも見やすく
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2005049/01cd3376-ad4f-0957-ab42-189ef9b241fc.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2005049/2c18216e-5855-017a-4c15-7d2f72f18974.png)

2. 公開範囲制限: Basic認証で限定公開。Amplifyのホスティング設定で簡単にパスワード保護可能。これで身内以外のユーザーからのアクセスを遮断！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2005049/f49c0461-242e-7b69-1e1d-752d25b79dff.png)

3. **AI活用のためのスキーマ設計**:  
  AI（Gemini）によるレシート解析機能を最大限に活用するため、バックエンドではデータのスキーマをフロントエンドでの表示や操作に最適化する形で設計しました。以下は、Receiptデータを定義したスキーマの例です。このスキーマでは、レシート全体の情報（店舗名、住所、日付など）や個々の購入アイテム情報を構造化しています。 
    ```typescript
    const createReceiptSchema = () => ({
      description: "Receipt data",
      type: SchemaType.OBJECT,
      properties: {
        shopName: { type: SchemaType.STRING },
        shopAddress: { type: SchemaType.STRING },
        shopPhone: { type: SchemaType.STRING },
        date: { type: SchemaType.STRING },
        time: { type: SchemaType.STRING },
        items: {
          type: SchemaType.ARRAY,
          items: {
            type: SchemaType.OBJECT,
            properties: {
              name: { type: SchemaType.STRING },
              price: { type: SchemaType.NUMBER },
              discount: { type: SchemaType.NUMBER },
              quantity: { type: SchemaType.NUMBER },
            },
            required: ["name", "price", "discount", "quantity"],
          },
        },
        total: { type: SchemaType.NUMBER },
      },
      required: [
        "shopName",
        "shopAddress",
        "shopPhone",
        "date",
        "time",
        "items",
        "total",
      ],
    });
    ```

# スクリーンショット＆機能紹介
- レシートスキャン機能: AIで自動解析し、購入アイテムを瞬時に記録
- 手入力機能: レシート以外のものも、手動でレシートとして追加可能
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2005049/58b23300-98cc-008e-3949-8dd9795f7253.png)
- スキャン済みレシート一覧: 過去の履歴を一覧で素早く確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2005049/ffd23d05-7b71-e134-0e8c-c7f9a356db73.png)
- レシート詳細表示: 各項目をワンタップで誰が支払い、誰の分なのかを選択、購入商品や支払い者をひと目で把握
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2005049/578529ac-d214-d8b5-34cf-a1f9da2ec8ca.png)

# 今後の展望 ～このアプリはまだ進化する！～
ReceiptSplitは現在、個人開発のプロトタイプ段階ですが、今後さらに便利で多くの人に使ってもらえるアプリに進化させたいと考えています。そのための展望を以下にまとめました。
1. ユーザー追加機能の強化
現在はユーザーを固定のメンバー（ハードコーディング）として登録しており、共同生活のような少人数利用に特化しています。しかし、将来的には自由にユーザーを追加可能にする仕組みを導入予定です。例えば、友人や家族を簡単に招待して共有できるようにしたり、ユーザーごとの権限（編集可能／閲覧のみなど）を設定することで、幅広いシーンでの利用が可能になります。
2. サービス公開への意気込み
このアプリは、共同生活の「出費管理」を手軽にしたいという思いから生まれました。今後は、共同生活を送るすべての人たちに役立つツールとして成長させることを目指しています。「これがあればもっと便利になる！」と感じてもらえるように、フィードバックを取り入れながら改良を重ねていきます。
また、利用シーンをさらに広げたいと考えています。
例えば、グループ旅行の費用管理で旅行先での食事やアクティビティの支払いをレシートでパシャっと記録し、簡単に精算できる機能を検討しています。旅行中に「誰がどれだけ払った？」というモヤモヤを解消するツールとしても活躍できるはずです。

## まとめ

このプロジェクトを通じて、AWS Amplifyを使ったフルスタック開発のスムーズさを実感しました。個人開発者にとって、Amplifyは強力な武器となります。興味があれば、ぜひ一度触れてみてください！

それでは、次回の記事でまたお会いしましょう！