Vercelが使えなくてAWS Amplifyに乗り換えた話 〜損益カレンダー開発記(2)〜

前回、Claude Codeと一緒に「損益カレンダー」というWebアプリを作った話を書きました。楽天証券の取引履歴CSVを読み込んで、日別の実現損益をカレンダー表示するところまでできて、実データと1円単位まで突き合わせて計算ロジックのバグも直せた、というところまでが前回のあらすじです。

今回はその続きです。「PCでもスマホでも見たい」という一言から、データベースとログイン機能を足すことになり、その過程でまた新しい発見がいくつかありました。

「PCでもスマホでも見たい」で構成が変わった

前回作ったバージョンは、ブラウザにCSVを読み込ませてその場で計算するだけの、いわば使い捨ての仕組みでした。ページを閉じたらデータは消えるし、当然PCとスマホで別々に読み込まないと見られません。

「PCでもスマホでも見たい」と伝えたところ、Claude Codeから「それだとブラウザのlocalStorageでは端末をまたげないので、サーバー側にデータを保存する構成が必要です」という話になり、そこから、

  • データの保存先どうするか
  • 自分以外がアクセスできないようにログインは必要か

を聞かれました。保存先はSupabase(無料で使えるPostgreSQL + 認証機能のサービス)、ログインは家族も将来使うかもしれないのでシンプルなメール+パスワード認証を入れることにしました。

同じCSVを読み込み直したら、数字が合わなくなった

データを貯めていく形にすると、当然「前に読み込んだCSVと、今日ダウンロードしたCSVで期間が重なっている」という状況が出てきます。例えば1〜2月分を読み込んだあとに2〜3月分を読み込んだら、2月分は重複してほしくありません。

最初のアプローチは、「取引の内容(日付・銘柄・数量・単価など)が完全に一致する行は重複とみなして無視する」というものでした。理屈の上ではこれで良さそうだったのですが、実際に試してみると合計金額がズレる、という不具合が出ました。

原因を調べてもらうと、楽天証券のCSVは、同じ期間を再エクスポートすると行の並び順や中身が変わることがあるということが分かりました。決済がまだ確定していない取引は最初「-」と表示されていますが、後日エクスポートし直すと金額が埋まっている、といった具合です。これだと「内容が完全に一致するか」で重複判定するロジックは機能しません。

最終的には、もっと割り切った方式に変更しました。新しいCSVをアップロードしたら、そのCSVがカバーする日付範囲の既存データを一旦削除してから入れ直す、というシンプルな方式です。行単位で「同じかどうか」を悩む必要がなくなり、常に「最後にアップロードした内容が正」というルールになったので、挙動も説明しやすくなりました。

Vercelでハマった話

アプリができたので、次はいよいよ公開です。Next.jsを使っているので、素直に考えるとホスティング先はVercelです。アカウントを作って、GitHubのリポジトリを連携しようとしたところ、

Deploying from a private GitHub organization requires a Vercel Pro plan.

というメッセージで止まりました。このリポジトリは会社(fmlabo-inc)のGitHub Organization配下に置いていたのですが、Vercelの無料プラン(Hobby)は、GitHub Organization配下のリポジトリのデプロイに対応していないという制限があるようです。個人アカウント配下のリポジトリなら無料で使えるのに、Organization配下だとProプラン(月額課金)が必須になる、という仕様でした。これは知りませんでした。

選択肢としては、

  • リポジトリを個人アカウントに移す
  • Vercel Proの無料トライアルを使う(14日後に自動課金)
  • Vercel以外のホスティングを使う

あたりが考えられたのですが、たまたまAWSのアカウントを持っていたので、思い切ってAWS Amplify Hostingを使うことにしました。GitHub連携でNext.jsアプリをほぼ自動デプロイしてくれるサービスで、Organizationのリポジトリでも特に制限なく使えました。

今回使っているNext.jsはかなり新しいバージョン(16系)だったので、Amplify側のビルド環境が対応しているか少し不安だったのですが、特に何も手を加えずにビルドが通り、無事に公開できました。

公開後に見つかった、もう一つのバグ

公開してしばらく実際に使っていたところ、ある信用取引の銘柄で損益が0円と表示され、「建玉の取得価格が確定できない決済があり、その分の損益は概算です」という警告が出ました。

原因を調べると、これは前回見つけたのと似た種類の問題でした。決済がまだ確定していない(受渡金額が「-」の)取引を計算するとき、「同じ日に建てた建玉」しか探していなかったため、数日前に建てた建玉を当日中に決済したケースでは一致する建玉が見つからず、丸ごと「原価不明」扱いになって0円になっていました。

建玉を探す範囲を「同じ日」から「その銘柄の未決済の建玉すべて(古い順に消化するFIFO)」に広げて修正し、実データとの突き合わせでも引き続き1円単位で一致することを確認しました。

「他の証券会社にも対応できる?」という雑談から

開発を進めている中で、「この先、松井証券やSBI証券にも対応してほしいと言われたら対応できるか」という話になりました。今すぐ必要なわけではなかったのですが、せっかくなので設計だけ先に考えることにしました。

結論としては、

  • データは証券会社ごとにテーブルを分けるのではなく、1つのテーブルに「どの証券会社か」を持たせる
  • CSVの日付範囲を削除・置き換えする処理も、証券会社ごとに独立させる
  • アップロードボタンも「楽天証券の取引履歴CSVを読み込む」のように証券会社名を明示しておく

という方針にして、証券会社の列だけ先に追加しておきました。実際にSBI証券などのCSVフォーマットに対応する作業はまだ先ですが、その時に土台からやり直さなくて済むようにはなったはずです。

今回もやっぱり、本当の勝負は「見えないところ」だった

前回は「実データと突き合わせて計算が合っているか確認する」ところに一番時間がかかりましたが、今回は、

  • CSVという不完全なデータソースの癖(再エクスポートで中身が変わる)にどう向き合うか
  • ホスティングサービスの制限(Organizationリポジトリ問題)にどう対応するか
  • 将来の拡張(他社対応)にどこまで先回りして設計しておくか

といった、コードの正しさとは少し違う種類の判断が中心でした。こういうところは、Claude Codeが選択肢とトレードオフを整理してくれるのは助かりつつも、最終的に「どうするか」を決めるのは自分の役割だなと感じます。

ひとまず、PCでもスマホでも同じ損益カレンダーが見られる状態にはなりました。次は複数証券会社対応か、グラフ機能あたりを進めてみようと思います。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です