ワインの在庫管理アプリを生成AIで自作する方法と、実際に動作させた結果

生成AIに書かせたHTML1枚で作るワイン在庫管理アプリ

エクセルで飲み頃の判定を組むのは手間で、既製のアプリには欲しい列が無いこともあるため、今回は生成AIに在庫管理アプリのコードを書かせました。そのため、出力は画面の見た目と動きを1つにまとめられるHTMLファイル(ブラウザで開いて表示するWebページのファイル)1枚にしました。

今回作ったアプリの画面構成

上部の集計
登録本数・今が飲み頃・飲み頃を過ぎた・購入価格の合計を、数字で表示
登録フォーム
銘柄・生産者・ヴィンテージ・タイプ・本数・棚位置、飲み頃の開始年と終了年・購入価格の9つ
一覧・検索
銘柄と生産者の検索ボックスとタイプでの抽出。CSVへの出力ボタンもある
一覧表
登録した行と状態(飲み頃・寝かせる・過ぎた)、削除ボタン

生成AIの1回目のコードは手直しせずブラウザで動作

sakuttoAIでは本記事に掲載したプロンプトだけを、記事の本文を読ませずにClaude Sonnet 5(Anthropicの生成AI)へ入力しました。出力されたのは620行のHTMLファイル1枚で、1回目の出力を修正せずに保存しています。

このファイルをブラウザで開いてワインを登録すると、飲み頃の判定・集計・検索・削除まで動作しました。 他の生成AIに同じプロンプトを入力した場合は、今回と同じ結果になるとは限らないため、数本を登録して判定と集計をチェックしてから使います。

今回のアプリには集計・登録フォーム・検索・一覧表がある

プロンプトに書いた9つの列はすべて登録フォームに入り、棚位置の入力フィールドには「例:セラーA-2段目」という例も表示されていました。 ヴィンテージや飲み頃の年の入力フィールドにも「例:2018」「例:2024」と表示されていたので、年の書き方は入力する前にわかり、画面上部のタイトル「おうちワインセラー管理」の下にはデータがこの機器のブラウザ内に保存されるという注意書きも出ています。

プロンプトの中身は出力形式や保存方法など6つの要素

生成AIに入力するプロンプトは要素に分けて書くと、出力されたアプリと照らし合わせてチェックしやすくなります。 【】で区切った要素ごとの中身は次の図のとおりです。

プロンプトに書いた6つの要素

出力形式
外部のライブラリを使わないHTMLファイル1枚にまとめ、ダブルクリックで開けること
持たせる列
銘柄から購入価格までの9列
自動でやってほしいこと
飲み頃の行の色分けと「過ぎた」「寝かせる」の表示、上部の4つの集計
操作
検索・タイプでの抽出・削除・CSVの書き出し
保存方法
localStorageに保存し、ブラウザを閉じても消えないこと
表示
スマホの幅では1行をカードに組み替え、日本語で表示すること

HTMLファイル1枚にまとめると、デザインや動きの別ファイルは必要ない

【出力形式】にはHTMLファイル1枚で完結させること、外部のライブラリやCDN(ネット上の配布元から部品を読み込む仕組み)を使わないこと、ダブルクリックで開くだけで動作することを書き、今回の出力はデザインのCSSと動きのJavaScriptを同じファイルに入れて外部のファイルやURLを1つも読み込んでいませんでした。そのため、ブラウザで開けば使うことができ、導入の手間はありませんでした。

Claude Sonnet 5に与えたアプリ用プロンプト全文

ご自身の管理方法に合わせて列や判定の条件は書き換えられます。ただ、今回はこの文面を1文字も変えずに入力しました。

自宅のワインを管理するツールを作ってください。

【出力形式】
- HTMLファイル1枚で完結させる(CSSとJavaScriptも同じファイルに入れる)
- 外部ライブラリやCDNは使わない
- ファイルをダブルクリックしてブラウザで開くだけで動くこと

【持たせる列】
銘柄/生産者/ヴィンテージ(年)/タイプ(赤・白・ロゼ・スパークリング・その他)/
本数/棚位置/飲み頃の開始年/飲み頃の終了年/購入価格

【自動でやってほしいこと】
- 今年が「飲み頃の開始年〜終了年」の範囲に入っている行は背景色を変える
- 終了年が今年より前の行はグレーにして「過ぎた」と表示する
- 開始年が今年より後の行は「寝かせる」と表示する
- 画面上部に「登録本数」「今が飲み頃の本数」「飲み頃を過ぎた本数」
  「購入価格の合計」を数字で表示する

【操作】
- 銘柄・生産者での絞り込み検索
- タイプでの絞り込み
- 行の削除
- CSVでの書き出し(Excelで開いても文字化けしないようにする)

【保存方法】
- localStorage に保存し、ブラウザを閉じても消えないようにする

【表示】
- スマートフォンの幅では表が横スクロールしないよう、
  1行を縦積みのカードに組み替える
- 日本語で表示する

このプロンプトは購入価格が1本あたりの価格か行の合計かを書いていません。購入価格の書き漏れが集計にどう出たかは、あとの「購入価格の合計」で述べます。

拡張子.htmlで保存したファイルをブラウザで直接利用

生成AIが出力したコードをすべてコピーしてテキストエディタに貼り付け、「wine.html」のような名前で保存します。次に、sakuttoAIでもファイルをブラウザで直接開いてシャブリ3本を登録したところ、再読み込みしても登録した1行と集計は残り、JavaScriptのエラーは1件も出ませんでした。

5銘柄9本で飲み頃判定は使え、集計の修正が2つ必要

登録したのはボルドー赤2本・バローロ1本・ボジョレー・ヌーヴォー1本・シャブリ3本・カヴァ2本の5銘柄9本で、今年(2026年)を基準にすると飲み頃・寝かせる・過ぎたの3つの状態がすべて出る組み合わせです。そのうえで判定と集計を確認した結果は、次の図のとおりです。

5銘柄9本を登録したときの表示

登録本数
9本(正しい)
今が飲み頃
7本(ボルドー赤2本・シャブリ3本・カヴァ2本。正しい)
飲み頃を過ぎた
1本(ボジョレー・ヌーヴォー。正しい)
寝かせる
バローロ1本。上部の集計に表示は無く一覧表にだけ出る
購入価格の合計
¥21,500(本数を掛けると¥34,500)
スマホ幅375px
1銘柄ずつのカードになり、横スクロールは出ない

登録した9本の状態は飲み頃・寝かせる・過ぎたの3色で見分けられる

一覧表では行の背景が飲み頃なら黄色・寝かせるなら青・過ぎたならグレーになり、状態の列にも「飲み頃」「寝かせる」「過ぎた」と表示されました。ボルドー赤・シャブリ・カヴァの3銘柄が黄色の行です。

判定は終了年が今年より前の行を「過ぎた」とし、開始年が今年より後の行を「寝かせる」として残りを「飲み頃」にする書き方でした。 生成されたコードの該当部分は次のとおりです。

function getStatus(wine) {
  var currentYear = getCurrentYear();
  var start = wine.startYear;
  var end = wine.endYear;
  if (start === null || start === undefined || end === null || end === undefined) {
    return { key: 'unknown', label: '-' };
  }
  if (end < currentYear) return { key: 'passed', label: '過ぎた' };
  if (start > currentYear) return { key: 'aging', label: '寝かせる' };
  return { key: 'ready', label: '飲み頃' };
}

開始年か終了年のどちらかが空の行は、生成されたコードでは状態が「-」になります。この行の本数は登録本数には加算され、今が飲み頃と飲み頃を過ぎた本数には加算されません。

購入価格の合計は行ごとに合算しただけで本数を掛けていない

入力した価格を1本あたりとして本数を掛けると¥34,500ですが、集計の「購入価格の合計」は¥21,500でした。2本以上登録したボルドー赤・シャブリ・カヴァの2本目以降のぶんが、差の¥13,000です。

購入価格が1本あたりか行の合計かを、プロンプトに書いていなかったことが原因です。 1本あたりで入力するなら【持たせる列】か【自動でやってほしいこと】に「購入価格は1本あたり。合計は本数を掛けて出す」と追記して作り直し、合計が本数を掛けた値になるかを数本の登録でチェックします。

タイプを赤に限定しても上部の集計は全体の値から変わらない

タイプを「赤」に限定すると一覧表は赤の3行だけになりましたが、上部の集計は9本・7本・1本・¥21,500のままでした。検索ボックスに「生産者D」と入れると一覧表はシャブリの1行だけになり、抽出そのものは正しく動作しました。

赤ワインだけの本数を知りたいときに、この集計は使うことができません。 抽出した行だけで集計したいなら、プロンプトに「上部の集計は抽出後の行だけで出す」と追記します。

スマホ幅375pxでは表が1銘柄ずつのカードになり、横スクロールは出ない

カードでは表の先頭行が隠れる代わりに、各データの前に「銘柄」「生産者」などの名前が表示されます。コードは画面幅700px以下でカード表示に切り替える書き方で、375pxでもページの横幅ははみ出しませんでした。

ただしカード1枚の高さが約438pxあり、高さ812pxの画面にはカードが2枚入りきりません。 本数が増えてスマホで探しにくくなったら、カードに出す情報を銘柄・本数・棚位置・状態に限定するよう、プロンプトに追記する方法があります。

自作アプリは共有もラベル撮影の自動入力もできず、データが消える場合がある

今回のアプリは開いたブラウザの中にだけデータを保存するため、使う機器や人が増えると困る状況が出てきます。 共有・データが消える場合・ラベル撮影の3つは次の図のとおりです。

今回の自作アプリで困る共有・消失・ラベル撮影

共有
データはブラウザの保存領域に残り、別の機器や家族からは閲覧できない
消える場合
Chromeでサイトデータを削除したとき、プライベートブラウジングで最後のタブを閉じたとき
ラベル撮影
写真から銘柄を読み取る機能は、winecodeやShelvinなどの既製アプリが持つ

localStorageのデータは家族の機器と共有できない

今回のアプリは、データをlocalStorage(ブラウザの中に記録を残す仕組み)に保存していました。MDNはlocalStorageを、文書のオリジン(サイトの出どころ)にひもづく保存領域としています。

ブラウザを閉じても消えませんが、保存先はアプリにアクセスしたブラウザの中のため、家族の機器から同じ在庫(関連記事:ワイン管理アプリの在庫共有を比較、家族か店舗スタッフかで変わる選び方)は見られません。

localStorage は window インターフェイスの読み取り専用プロパティで、この Document の origin における Storage オブジェクトにアクセスできます。/格納されたデータは、ブラウザーのセッションを跨いで保存されます。 — localStorage の定義とデータ保持についての記述より
公式情報を見る →

Chromeでサイトデータを削除すると登録したワインは残らない

Chromeのヘルプによると、閲覧データの削除で「サイトデータ」を選んだ場合はWeb Storageのデータを含むストレージのデータが削除されるとのこと。プライベートブラウジングやプライバシーモードで開いた場合も、最後のプライベートタブを閉じた時点でlocalStorageのデータが消えるとMDNは書いています。

消えて困る記録は「CSVで書き出し」で控えを残すことができます。 今回保存したCSVには、Excelで開いても文字化けしないようBOMという印が先頭に含まれていました。

localStorage は sessionStorage によく似ていますが、 localStorage のデータには期限がないのに対し、 sessionStorage のデータはページセッションが終了したとき、すなわちページが閉じられたときにクリアされます。/「プライベートブラウジング」や「プライバシーモード」のセッションに読み込まれた文書の localStorage のデータは、最後の「プライベート」タブが閉じられたときにクリアされます。 — 前者は sessionStorage との違い(データの保持期間)、後者はプライベートブラウジング時の扱いの説明より
公式情報を見る →
サイトデータ: HTML5 対応の各種ストレージのデータ(アプリケーション キャッシュ、Web Storage のデータ、Web SQL Database のデータ、Indexed Database のデータなど)は削除されます。 — 削除されるデータの種類ごとの説明より
公式情報を見る →

ラベル撮影で銘柄を読み取る機能は、winecodeなど既製アプリにある

winecodeでは2024年10月22日から、ワインの写真からワイン名と生産年を生成AIで認識する機能を使うことができます。winecodeによると、ラベルが傷んだり見切れたりしていても、推測して高い精度で読み込めるそうです。 Shelvinもラベルを撮影するだけで、銘柄名・ヴィンテージ・ブドウ品種・産地を自動で解析します。

業務用ワイン管理アプリ「winecode」の新機能(Webアプリ・iOSアプリ・Androidアプリ)を2024年10月22日(火)から提供開始いたしました。 / ワイン(エチケット)の写真から、ワイン名・生産年を自動認識できます。 / ラベルが傷んでいたり、見切れたりしている場合でも、生成AIが推測することにより、高い精度で読み込みを行うことができます — 提供開始日・ラベル自動認識機能の説明より
公式情報を見る →
カメラでラベルを撮影するだけで、銘柄名やヴィンテージ、ブドウ品種、産地を自動解析。セラー登録もテイスティング記録も手入力不要です。 — 「主な機能」のAIラベルスキャンの説明より
公式情報を見る →

まとめ:自作は1人1台の管理向けで共有やラベル読み取りは既製アプリ

生成AIに6つの要素を書いたプロンプトを入力すると、ワインの在庫管理アプリがHTMLファイル1枚で出力されました。5銘柄9本の飲み頃の判定・本数の集計・スマホ幅のカード表示まで、Claude Sonnet 5の1回目の出力は手直しせずに動作しました。

購入価格の合計に本数が掛けられていないことと、表示を限定しても集計が変わらないことは、修正が必要です。どちらもプロンプトに書いていなかった部分なので、使う側で追記して作り直してから数本を登録し、合計と集計が合うかをチェックします。

データをブラウザの中に保存する仕組みなので、別の機器からは開けず、家族とも共有できません。Chromeでサイトデータを削除すると消えます。1人が1台で管理するなら、自作で十分です。家族と共有したい場合やラベルを撮影して登録したい場合は、既製のアプリを使うほうが確実です。

Shelvinは無料プランでセラーに16本まで登録でき、ラベルスキャンは週2回まで使うことができます。専用のQRコードを読み取ってもらうだけでセラーの在庫データを共有する機能もあり、料金プランではチーム共有機能がプレミアムプラン(月500円・税込)の機能として掲載されています。

QRコードで家族とセラーを共有したい人やラベルを撮って登録したい人は、Shelvinを自作アプリと併用することができます。 App Storeでは次のリンクから入手できます。Shelvin(App Store)

セラー登録 16本まで / チーム共有機能 — 前者は料金プランの Free、後者は Premium の掲載項目より
公式情報を見る →
(※:無料ユーザーは週2回まで、プレミアムユーザーは月50回までスキャンできます。) / ワンタップ招待: 専用QRコードを読み取るだけで、セラーの在庫データを簡単に共有。 / タイトル: プレミアムプラン / 期間: 1ヶ月(自動更新) / 価格: 500円(税込) — 掲載文のラベルスキャンと共有の説明、【サブスクリプションについて】の節より
公式情報を見る →

FAQ

プログラミングの知識がなくてもワインの在庫管理アプリは作ることができますか?
作ることができます。sakuttoAIでプロンプトをClaude Sonnet 5に入力すると、1回目の出力で620行のHTMLファイル1枚が出力され、手直しせずにブラウザで登録や集計まで動作しました。データはブラウザのlocalStorageに保存され、MDNの説明ではブラウザのセッションをまたいで残ります。購入価格の合計のようにプロンプトに書いていなかった部分は、追記して作り直します。
自作したアプリのデータはブラウザを閉じると消えますか?
通常のウィンドウで開いていれば閉じても消えません。MDNはlocalStorageのデータには期限がなく、ページを閉じるとクリアされるsessionStorageとは異なるとしています。ただしプライベートブラウジングで開いた場合は、最後のプライベートタブを閉じた時点で消えます。消えて困る記録はアプリの「CSVで書き出し」で控えを残せ、今回書き出したCSVにはExcel向けの印(BOM)も含まれていました。
自作したアプリを家族と共有できますか?
今回のHTMLファイル1枚のアプリでは共有できません。データはアプリを読み込んだブラウザの保存領域に残るため、家族のスマートフォンからは見られません。QRコードで相手を招待できるShelvinのように共有の機能を持つ既製アプリなら、家族と同じ在庫を見られます。
ラベルを撮影して自動で入力する機能も自作できますか?
今回のプロンプトには書いておらず、作ったアプリにもその機能はありません。写真から銘柄を読み取る機能はwinecodeなどの既製アプリにあり、winecodeは生成AIを使って、ラベルが傷んだり見切れたりしていても推測して読み込むとしています。