はじめに
飲み会の幹事をやると必ずぶつかる問題がある。
▎ 「均等に割ると新人に重い」「上司が定額で出してくれる」「子供はタダにしたい」「幹事は少なくていいよ」
こういった"ちょっとイレギュラーな割り勘"を、その場でサクッと計算できるツールがほしくて https://warikan.keydrop.net を作った。
この記事では、割り勘の計算パターンを整理した思考プロセスと、静的サイトとして実装した技術的な判断を書く。
割り勘の5パターン
まず「世の中の割り勘シーン」を洗い出すところから始めた。
- 均等割り(基本)
1人あたり = 端数処理(合計 ÷ 人数)
差額 = 回収額 - 合計
一番シンプル。ただし端数処理の選択肢が意外と重要で、「切り上げ/切り捨て/四捨五入」×「100円単位/10円単位/1円単位」の組み合わせを用意した。
現実では「10円単位で切り上げ、端数は幹事が預かる」が多数派なので、デフォルトは 切り上げ・10円単位。
- 傾斜割り勘(グループ別倍率)
役職・年齢で支払い比率を変えるパターン。
基準単価 = 合計 ÷ Σ(グループ人数 × 倍率)
グループXの1人あたり = 端数処理(基準単価 × 倍率X)
ポイントは「基準単価」の導出。単純に均等割りした額に倍率をかけると合計が合わなくなるため、加重平均の逆算で基準単価を求める。
倍率の相場は「役職者1.3〜1.5倍、若手0.7〜0.8倍、新入社員0.5倍」あたりが現実的。
- 無料枠(N人タダ)
子供や特別ゲストを無料にするパターン。
支払い人数 = 総人数 - 無料人数
1人あたり = 端数処理(合計 ÷ 支払い人数)
シンプルだが、UI上で「総人数」と「無料人数」を別々に管理することが重要。「8人で来て子供2人タダ」を直感的に入力できるようにした。
- 幹事設定(無料 or 追加負担)
幹事の扱いには2パターンある。
- 幹事は無料(予約・集金の労働に対する報酬)
- 幹事が多めに払う(余裕がある場合の気遣い)
// 幹事無料の場合
残り合計 = 合計金額 - 0
他メンバー1人あたり = 端数処理(残り合計 ÷ (人数 - 1))
// 幹事が追加負担する場合
残り合計 = 合計金額 - 幹事追加額
他メンバー1人あたり = 端数処理(残り合計 ÷ (人数 - 1))
「幹事が多く払うのがマナー?」という疑問を持つ人もいるが、決まりはない。予約・集金・連絡の手間を考えると、むしろ少なめが自然という場面も多い。
- 固定額メンバー(上司N人が定額)
「上司3人が各8,000円出す、残りを若手で割る」という現実によくあるパターン。
固定額合計 = 固定人数 × 1人あたり固定額
残り合計 = 合計金額 - 固定額合計
残り人数 = 総人数 - 固定人数
残りメンバー1人あたり = 端数処理(残り合計 ÷ 残り人数)
バリデーションとして「固定額合計 ≥ 合計金額」はエラーにする必要がある。上司が全額以上払う場合は計算が破綻する。
実装の技術的判断
フレームワークを使わなかった理由
React/Vueを使う選択肢もあったが、割り勘計算という「状態が少なく、画面遷移がない」用途にはオーバーキル。
- HTML + CSS + Vanilla JS のみ
- ビルドステップなし
- 依存パッケージなし
ホスティングはS3 + CloudFrontの静的配信で、デプロイは aws s3 sync + CloudFrontキャッシュ無効化のシェルスクリプト1本。
JSのモジュール設計(Revealing Module Pattern)
// calc.js — 計算ロジックのみ。DOMに一切触れない
const Calc = (() => {
function equal(total, people, rounding, unit) { ... }
function weighted(total, groups, rounding, unit) { ... }
// ...
return { equal, weighted, withFree, withOrganizer, withFixed };
})();
calc.js(計算)→ ui.js(DOM操作)→ app.js(イベント配線)の3層に分離。計算ロジックはDOMを知らないため、テストしやすく、ロジックの変更がUIに波及しない。
端数処理の設計
端数処理は計算エンジンの中核。applyRounding(value, method, unit) として独立させた。
function applyRounding(value, method, unit) {
switch (method) {
case "ceil": return Math.ceil(value / unit) * unit;
case "floor": return Math.floor(value / unit) * unit;
case "round": return Math.round(value / unit) * unit;
}
}
value / unit で単位に変換してから処理し、* unit で戻す。これで「100円単位で切り上げ」が Math.ceil(value / 100) * 100 と自然に書ける。
差額(端数の行き先)の可視化
端数処理後の「回収額 - 合計」を必ず計算して表示する。
例:30,000円を7人で10円切り上げ → 1人4,290円 × 7人 = 30,030円 → +30円が余る
この差額を表示することで「誰かが30円少なく払える」「幹事が30円受け取る」という調整ができる。現実の割り勘で一番モヤモヤする部分を明示した。
SEO対策
AdSenseで収益化を目指すため、SEOも意識した。
- JSON-LD構造化データ:WebApplication + FAQPage の2種類を設置。FAQリッチリザルトを狙う
- FAQ内容:「割り勘 端数」「割り勘 傾斜 相場」などの検索クエリに自然に答える内容を用意
- パフォーマンス:静的HTMLのため初回表示が速く、Core Web Vitalsに有利
まとめ
「割り勘を計算する」という単純に見えるタスクでも、現実のシーンを丁寧に分解すると5パターンが出てきた。
- 均等割り(基本)
- 傾斜割り(加重平均の逆算)
- 無料枠(除外計算)
- 幹事設定(固定差引)
- 固定額メンバー(差引+均等)
計算ロジックをDOMから完全に分離したことで、各パターンが独立してテストできる構造になっている。