はじめに
私は歯科医院で勤務しながら、エンジニア転職を目指して学習中です。
歯科医院向けに、お薬手帳などの写真から薬の情報を整理して印刷できる Web アプリを作りました。
この記事では、コードの細かい解説よりも、「なぜ作ったか」「どう設計したか」「どこで苦労したか」を中心に書きます。
※診療判断の代わりにはなりません。AI の結果は必ず人が確認する前提です。画像は解析のため Google Gemini API に送信されます。
※※デモ版のパスワードはGitHubのREADMEに載せておりますので、宜しければそちらからご覧いただけたらと思います。
どんな課題があったか / なぜ作ろうと思ったか
歯科の定期検診では、来院のたびに患者さんのお薬を確認しています。
いまの現場では、紙のお薬手帳をコピーして、必要なときに薬を調べる、というやり方が中心でした。ただ、次のような負担がありました。
- 高齢の患者さんが多く、飲んでいる薬の種類も、変更も多い。ご本人も把握しきれていないことが多い
- 最近はマイナンバーで薬情報を見られる場面もあるが、コピーできず、その日限りで残せない
- 3ヶ月ごとの検診で毎回確認するので、手間が大きい
- 検診時間を短くしたい(たとえば 60 分を 45 分にしたい)計画のなかで、お薬確認がネックの一つになっている
「調べる・覚える・残す」がバラバラで、毎回ゼロから近い確認になっている、という感覚がありました。
そこで、形式の違うお薬情報(紙、スマホ画面、薬袋など)を写真から読み取って、同じフォーマットの表に直し、前回と比べて、何の薬か・歯科で気をつける点まで整理して、印刷できるようにしたいと考えました。
もう一つ大切にしたのが、IT に苦手意識の強い職場でも使えることです。難しい操作を増やさず、カメラで撮るだけで進められる手軽さを優先しました。
何ができるアプリか(ざっくり)
- 写真から薬品名を抜き出して表にする(A4 印刷)
- 前回と今回を比べて、増えた薬・消えた薬・続いている薬を整理する
- 印刷した紙に QR を付けて、次回は QR から前回情報を読める(サーバーに保存しない)
- 院内向けの歯科注意リストで、特記がある薬だけ注意を出す
- 共有パスワードでログイン保護(一度入ればしばらく再入力不要)
患者番号は印刷に載せるためだけに使い、サーバーや DB には患者情報を残しません。
技術選定の理由
最初に決めていたのは、「学習のために技術を選ぶ」のではなく、いちばん早く院内で使える形にすることでした。
自分は Java(Spring)なども勉強中ですが、今回はそこを優先しませんでした。
使ったものはだいたい次です。
- Next.js(画面と API)
- TypeScript / Tailwind CSS
- Google Gemini(写真から薬名を読む)
- Vercel(公開)
- DB なし
なぜ Next.js と Vercel か
画面と API を一体にできる枠組みは、他にもあります(Laravel や Spring など)。
それでも Next.js にしたのは、次をまとめて早く届けたかったからです。
- タブレット向けの大きなボタン、撮影、印刷の UI
- 画像を受けて Gemini に渡す API(キーをサーバー側に置く)
- すぐ公開して現場で試せること
「UI が Next じゃないと作れない」というより、UI・API・公開を一つの流れで速く回せるのが決め手でした。
なぜ DB なしにしたか
患者情報をサーバーに溜めたくない、という方針が先にありました。
扱うのは確認用の患者番号くらいにして、写真や薬データは保存しません。残るのは印刷した紙(と、紙に載せた QR)だけ、という形です。
こうする事で、漏えいのリスクを小さくしやすく、サーバー管理も軽くなります。
なぜ Gemini か
紙、スマホ画面、薬袋など、見た目がバラバラな写真から薬名を読む必要がありました。
そこで Gemini を使い、コストと速度のバランスが取りやすいモデルを選びました。
歯科で特に注意したい薬は、院内リスト側でも出し分けるようにしています。
なぜ「撮る」入力にしたか
職場には IT が苦手な人もいます。
手入力や複雑な画面を増やすより、「撮る → 確認 → 印刷」で終わらせた方が現場に合うと思いました。
(PC でデモするときは、カメラの代わりに画像ファイル選択でも使えるようにしています)
設計で意識したこと
技術選定と重なりますが、設計の意図を言葉にすると次です。
-
残さない
患者情報をアプリ側に溜めない。必要なのはその場の確認と、紙として残せることです。 -
操作を増やさない
現場は忙しく、新しいツールに慣れる余裕も少ないので、ボタンは大きく流れは短くしました。 -
AI は補助まで
画像読み取りは便利でも、抜けや誤りはある前提で、最終確認は人がする前提でスタッフにも共有しました。 -
公開しても院外から自由に触られないようにする
デモや院内利用のために URL は公開しうるので、共有パスワードで入れるようにしました。
一度ログインすればしばらくそのまま使えるようにして、毎回の入力で現場が止まらないようにしています。 -
前回情報もサーバーに頼らない
比較のために DB を持つと、また「保存」の話になります。
なので印刷紙に QR を付けて、次回はその QR を読む、という形にしました。
画像を Gemini に送ること自体は避けられません。
なので運用では、氏名などが写らないように撮る/隠す、という現場ルールもセットで考えています。技術だけで完璧に守る、というより、残さない設計 + 撮り方 +(必要なら)有料 API の条件確認、という組み合わせです。
苦労した点と、取った解決策
いちばん悩んだのは、速さと正確さの両立です。
速いほうがいいが、雑にすると漏れる
写真が多いほど、送信と解析に時間がかかります。
なので最初は、画像を小さく圧縮したり、軽いモデル(gemini-3.1-flash-lite)を使ったりして、待ち時間を短くする方向に振りました。
ところが、圧縮を強くしすぎると字が潰れて、薬が抜けることがありました。
特に、読みにくい写真を 1枚だけ 前回比較に使うと、漏れが出やすい、という感覚があり、
AIモデルをもっと重いものに変える事も検討しましたが、速さやコストのバランスを崩したくなかったので、まずは別のやり方を試しました。
解決: モデルを上げるより、1枚のときだけ画質を上げる
方針はシンプルです。
- 複数枚のときは、これまでどおり軽めの圧縮で速さを優先する
- 1枚のときだけ、解像度を上げて送る
モデルを 3.5 系などに上げるのではなく、入力の質で精度を補う判断です。
現場でも、読みにくいときは別角度で撮影し、もう1枚足す、という運用が効きます。
その他の工夫(待ち時間まわり)
正確さ以外にも、「遅いと感じる」問題はありました。
- 比較は、前回と今回を同時に投げると不安定な場面があったので、前回 → 今回の順に読むようにした
- 待ち中は「準備 → 送信 → 解析」と表示を分けて、止まっているように見えにくくした
- 画像送信を JSON(base64)から multipart に変えて、送りやすくした
- Vercel の実行場所を東京寄りにして、日本からの遅さを減らした
また、multipart に変えた直後、比較だけ薬が抜けることがありました。
原因は、複数枚の受け取り方が環境によってうまくいかないケースがあったことで、
「何枚送ったか」と「何枚受け取ったか」を照合する形に直して解決しました。
速さのための変更が、別の不具合を生む、というのは今回よくあったパターンです。
ログイン実装でつまずいたこと
院外から自由に触られないよう、共有パスワードを付けました。
ここで、ログイン後にエラーになる問題が出ました。
原因は、Next.js の Middleware(Edge)では Node の crypto が使えなかったことです。
Web Crypto に切り替えて直しました。
「動くはずのコードが、動く場所によって使えない」という、地味ですが印象に残るつまずきでした。
これから
現場の「毎回コピーして調べる」を、撮って表にして印刷する、までに寄せられたのは大きかったです。
一方で、AI の読み取りは万全ではないので、人の確認と撮り方のルールがまだ本体だとも思っています。
これからやりたいこと・考えていること:
- 歯科で注意したい薬リストの拡充
- スタッフ向けの短い使い方ガイド
- (将来)電子版お薬手帳の QR(JAHIS)対応など
学習のためのアプリではなく、現場で使うためのアプリとして作ったので、技術選定も「正しいか」より「すぐ試せるか」で選びました。
同じように、現場の小さな不便からプロダクトを考えたい人の参考になれば嬉しいです。
