0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

個人開発のWebアプリで「投資助言業」に抵触しないUI文言設計をした記録

0
Posted at

この記事では、個人開発の家計・ライフプランWebアプリで「投資助言業(金商法)」に抵触しないようにUI文言・実装パターンを設計した記録を共有します。

前提:私は弁護士ではありません

最初に明確にしておきます。私は弁護士ではなく、この記事は法律的助言ではありません。実装上の参考パターンを共有するもので、最終的な法的判断は必ず弁護士・専門家にご相談ください。

その上で、個人開発で家計・投資・ライフプラン系のWebアプリを作るとき、多くの開発者が踏みやすい落とし穴があります。それを実装側でどう回避するかをまとめます。

なぜUI文言が問題になるのか

金融商品取引法では、**「投資助言業」**として登録していない事業者が、特定の金融商品やサービスを推奨する行為は規制対象です。

個人開発のWebアプリでよくある「やらかしパターン」:

  • 「あなたへのおすすめは○○証券」
  • 「このプランで決めましょう」
  • 「○○ファンドを買えば資産が増えます」
  • 「節約型がベストです」

**「データを見せる」「これが正解です」**の境界を踏み越えると、規制対象になり得ます。

NG / OK 言い換えパターン

実際にみらいコンパスというライフプランシミュレーターを作る中で整理した言い換え一覧です。

眉ラベル(ヒーローカードのトップ)

❌ NG ✅ OK
良いニュース シミュレーション結果
あなたへのおすすめ 試算結果
ベストプラン 入力値変更時の試算例
注意:このままだと危険 試算上の検討点

本文

❌ NG ✅ OK
「〜すべきです」 「〜という選択肢があります」
「このプランで決めましょう」 「このプランをシミュレーションする」
「○○がおすすめ」 「○○という選択肢」
「素晴らしい状態です」 「まだ余地のある試算結果です」

CTAボタン

❌ NG ✅ OK
「○○証券で口座を開く」 「NISA制度について詳しく見る」
「このファンドを購入する」 「商品の特徴を確認する」
「○○を始めよう」 「制度の詳細を見る」

ポイントは 「主語が特定企業・特定商品」 だとアウトということです。

実装パターン1:状態判定 → 中立メッセージ生成

メインプランのシミュレーション結果を「事実」として 4 状態に分類するだけ。

function renderInsightHero() {
  const mainSim = calcRetirementSim();
  const sufficiency = mainSim.assetsAtRetire / mainSim.requiredAssets;
  const depleted = mainSim.postData.find(d => d.depleted);

  let state, eyebrow, title, body;

  if (depleted) {
    state = 'warning';
    eyebrow = '⚠️ 試算上の検討点';
    title = `現在の入力では ${depleted.age}歳で資産がゼロになる試算です`;
    body = '想定余命より早く資産が枯渇する試算結果です。以下は入力値を一部変更した場合の試算例です。';
  } else if (sufficiency >= 1.5) {
    state = 'success';
    eyebrow = '🎉 シミュレーション結果';
    title = `${mainSim.lifeExpectancy}歳まで持続する試算(必要資産比 ${Math.round(sufficiency*100)}%)`;
    body = '想定余命までの生活費を確保した上で、まだ余地のある試算結果です。';
  } else {
    // ...
  }

  // 「良い悪い」を直接書かず、「結果」として淡々と提示する
  return renderCard({ state, eyebrow, title, body });
}

ポイント

  • 眉ラベルは「シミュレーション結果」で統一
  • 本文は「○○する試算です」と事実形式
  • 「良い」「悪い」「危険」など主観評価を避ける

実装パターン2:「What-if 試算」の数値提示

「○○すれば改善できる」を選択肢の試算結果として並列表示。

// 入力値を一部変更してシミュレーションを連続実行
function generateImprovementOptions() {
  const options = [];

  // 月の生活費を1〜8万円減らした場合
  for (const reduction of [1, 2, 3, 5, 8]) {
    const expenseMod = -reduction / baseExpense;
    const sim = calcRetirementSimWithOpts({ expenseMod });
    const dep = sim.find(d => d.depleted);
    if (!dep) {
      // 持続したケースを「試算結果」として記録
      options.push({
        emoji: '🍙',
        label: `月の生活費を${reduction}万円減らす`,
        detail: `月${baseExpense}万→${baseExpense - reduction}万に圧縮すると、${lifeExpectancy}歳まで持続見込み`,
      });
      break; // 最少コストで達成できる選択肢を採用
    }
  }

  // リタイアを延長した場合
  for (const extend of [1, 2, 3, 5]) {
    // targetAge を一時書換でシミュレーション
    const orig = state.retirement.targetAge;
    state.retirement.targetAge = parseInt(orig) + extend;
    const sim = calcRetirementSimWithOpts({});
    state.retirement.targetAge = orig; // 必ず復元
    if (!sim.find(d => d.depleted)) {
      options.push({
        emoji: '',
        label: `リタイアを${origAge + extend}歳に延長`,
        detail: `あと${extend}年働くと、${lifeExpectancy}歳まで持続見込み`,
      });
      break;
    }
  }

  return options;
}

UIでは「あなたはこうすべき」ではなく、**「以下のような試算結果があります」**と並べて提示します。

<div class="hero-card">
  <h3>試算上の検討点</h3>
  <p>以下は入力値を一部変更した場合の試算例です。</p>

  <div class="option">🍙 月の生活費を3万円減らす → 90歳まで持続</div>
  <div class="option">⏰ リタイアを65歳→67歳に延長 → 88歳まで持続</div>

  <button>テーマ別シミュレーションを見る</button>
</div>

実装パターン3:免責文を必ず入れる

各ヒーローカード・各シミュレーション結果の下部に 明示的な免責文 を入れます。

<div style="font-size:10px;color:#888;margin-top:10px;border-top:1px solid #eee;padding-top:8px">
  ※ 上記は入力値に基づくシミュレーション結果であり、特定の投資商品・
  金融サービスの選択や採否を推奨するものではありません。具体的な投資
  判断・運用方針については、ファイナンシャルプランナー等の専門家にご
  相談ください。
</div>

これは「データを見せること」と「推奨」の間に明確な線を引く役目を果たします。

実装パターン4:保存済みプラン同士の比較

複数のプランを保存した時、差分の事実だけ並べる

function renderSavedScenariosInsight(planA, planB) {
  const diffs = [];

  // 各KPIの差分を計算(ただし「どっちが良いか」は言わない)
  if (planA.targetAge !== planB.targetAge) {
    diffs.push({
      label: 'リタイア年齢',
      a: `${planA.targetAge}歳`,
      b: `${planB.targetAge}歳`,
      delta: `${planB.targetAge - planA.targetAge > 0 ? '+' : ''}${planB.targetAge - planA.targetAge}年`,
    });
  }
  // ...

  return `
    <table>
      <tr><th>指標</th><th>${planA.name}</th><th>${planB.name}</th><th>差分(B-A)</th></tr>
      ${diffs.map(d => `<tr><td>${d.label}</td><td>${d.a}</td><td>${d.b}</td><td>${d.delta}</td></tr>`).join('')}
    </table>
    <p style="font-size:10px">
      ※ 上記は2つの入力値セットを並べたシミュレーション結果の差分を示すものです。
      プランの優劣・採否を推奨するものではありません。
    </p>
  `;
}

「プランA は プランB より◯歳長く持続」のような事実は OK。
「だからプランB を選ぶべき」のような推奨は NG。

チェックリスト:あなたの実装は大丈夫?

リリース前にこのチェックリストを通すと、踏み外しを防げます。

  • 眉ラベルが「シミュレーション結果」「試算結果」で統一されているか
  • 本文に「〜すべき」「〜がおすすめ」が出ていないか
  • CTAボタンが「特定企業×行動喚起」になっていないか(「○○証券で口座開設」NG)
  • 各カード下部に免責文が入っているか
  • 「良い」「悪い」「危険」などの主観評価が削除されているか
  • 比較表で「どちらが優れている」と書いていないか(差分の数字だけ)
  • 自動生成のヒントで「採用すべき」と促していないか

みらいコンパスでの試算は中立な情報提供

みらいコンパス は完全ローカル(データは端末から外に出ない)で動く家計・ライフプランシミュレーターです。

入力した数値に基づいて将来の資産推移をグラフで表示しますが、上記の文言設計を全機能に通底させています。気づきヒーロー、What-if 試算、保存済みプラン比較、すべて「事実の提示」に徹しています。

技術スタックは Vanilla JS + Chart.js + localStorage + Service Worker のシンプル構成です。

まとめ

  • 金融系の個人開発Webアプリでは、「データ提示」と「推奨」の境界に注意
  • 中立な眉ラベル(「シミュレーション結果」)と免責文を全箇所に
  • 「○○すべき」「○○がおすすめ」を避け、「○○という選択肢」「試算結果」で統一
  • 比較画面では数字の事実差分だけを並べ、優劣判断は書かない
  • 最終的な法的判断は必ず弁護士・専門家に相談

個人開発で家計・投資系のアプリを作る方の参考になれば幸いです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?