JavaScript | ゼロからはじめるプログラミング、30日で基礎を学ぶJavaScript:JavaScriptを使えるレベルにする - Day13:例外処理

JavaScript JavaScript
スポンサーリンク

Day13 後半のゴール

後半では、try / catch を
「書き方を知っている」レベルから
「どこで、何のために使うかを設計できる」レベルに引き上げます。

特にここを深掘りします。

Day13 後半で押さえたいポイント

エラーが起きたときに「どう振る舞うか」をパターンとして持つ

ユーザー向けのメッセージと、開発者向けのログを分けて考える

try の“範囲”を意識して、必要なところだけ囲う


パターン1:安全なデフォルト値で処理を続ける

外部データが壊れていてもアプリを止めない

例えば、ローカルストレージから設定を読み込むケースを考えます。

const raw = localStorage.getItem("settings");

let settings;

try {
  settings = JSON.parse(raw);
} catch (error) {
  settings = { theme: "light", language: "ja" };
}

console.log(settings);
JavaScript

ここでやっていることはこうです。

JSON.parse が成功したら → 保存されていた設定を使う
失敗したら → 安全なデフォルト設定を使う

「エラーが起きたら終わり」ではなく、
「エラーが起きたら、こういうふるまいに切り替える」という設計になっています。

深掘り:デフォルト値は“安全側”に倒す

セキュリティ的には、
デフォルト値は「安全側」に倒すのが鉄則です。

例えば、

ログイン状態ではなく「未ログイン」にする
管理者権限ではなく「権限なし」にする
危険な操作は「無効」にしておく

など、「間違っても危険な状態にならない」方向に倒します。

try / catch の中でデフォルト値を決めるときも、
「この値で本当に安全か?」を一度立ち止まって考えるクセをつけると、
コードの質が一気に上がります。


パターン2:ユーザーには優しく、ログには厳しく

ユーザーに生のエラーメッセージを見せない

例えば、JSON のパースに失敗したとき。

const text = "これはJSONじゃない";

try {
  const data = JSON.parse(text);
  console.log("成功:", data);
} catch (error) {
  alert("データの読み込みに失敗しました。時間をおいて再度お試しください。");
  console.error("JSON parse error:", error);
}
JavaScript

ここでは、

ユーザーには → わかりやすく、余計な情報を出さないメッセージ
開発者には → console.error で詳細なエラー情報

というふうに、役割を分けています。

深掘り:なぜ生のエラーを見せてはいけないのか

生のエラーメッセージには、
内部構造・ファイルパス・ライブラリ名など、
攻撃者にヒントを与えうる情報が含まれることがあります。

ユーザーには「状況と次の行動だけ」を伝える
内部ログには「原因調査に必要な情報」を残す

この分離は、セキュリティとユーザー体験の両方にとって重要です。


パターン3:try の“範囲”を小さくする

何でもかんでも囲うと「どこで失敗したか」が見えなくなる

悪い例から見てみます。

try {
  const text = localStorage.getItem("settings");
  const data = JSON.parse(text);
  initUI(data);
  startApp();
} catch (error) {
  console.error("なんかエラー", error);
}
JavaScript

これだと、

localStorage.getItem が失敗したのか
JSON.parse が失敗したのか
initUI の中でエラーなのか

が一発では分かりません。

処理ごとに try / catch を分ける

少し丁寧に書くとこうなります。

let data = null;

try {
  const text = localStorage.getItem("settings");
  data = JSON.parse(text);
} catch (error) {
  console.error("設定の読み込みに失敗:", error);
  data = { theme: "light", language: "ja" };
}

try {
  initUI(data);
} catch (error) {
  console.error("UI 初期化に失敗:", error);
}

startApp();
JavaScript

どこで失敗したかがログから分かるし、
「設定の読み込みに失敗したときはデフォルトを使う」
「UI 初期化に失敗したときはログだけ残す」
といったふるまいを個別に設計できます。

深掘り:try は「境界」に置く

良い置き場所の目安は、

外部との境界(ネットワーク・ストレージ・ユーザー入力)
大きな処理の境界(初期化・画面切り替え・保存処理)

です。

「このブロックが丸ごと失敗したら、どう振る舞うべきか?」
という単位で try を置くと、設計がきれいになります。


パターン4:自分でエラーを投げて、catch で扱う(軽く触れる)

想定外の状態を「例外」として扱う

例えば、年齢がマイナスのユーザーはおかしい、というルールがあるとします。

function validateAge(age) {
  if (age < 0) {
    throw new Error("年齢が不正です");
  }
  return age;
}
JavaScript

この関数を使う側で try / catch します。

try {
  const age = validateAge(-5);
  console.log("年齢:", age);
} catch (error) {
  console.error("バリデーションエラー:", error.message);
}
JavaScript

ここでは、

「マイナスの年齢」は「例外的な状態」
→ throw で例外として投げる
→ 呼び出し側が catch で受け止める

という流れになっています。

今は「自分で throw したエラーも catch できるんだな」くらいでOKです。
大事なのは、「おかしな状態を見つけたら、その場で黙って進めない」という姿勢です。


セキュリティの視点から見る例外処理の“設計”

「失敗したらどうするか」を先に決める

try / catch を書くときに、
一番やってはいけないのは「とりあえず囲っておく」ことです。

囲う前に、必ず自分に問いかけてほしいのはこれです。

この処理が失敗したら、どうするのが一番安全か?
ユーザーには何を伝えるべきか?
ログには何を残すべきか?
処理を続けていいのか、それとも止めるべきか?

この問いに答えた結果が、
catch の中身になります。

例:ログイン処理の失敗をどう扱うか

try {
  const result = await login(email, password);
  showWelcome(result.user);
} catch (error) {
  console.error("ログイン失敗:", error);
  showError("メールアドレスまたはパスワードが正しくありません。");
}
JavaScript

ここでは、

ログインに失敗したら → 絶対に「ログイン済み」として扱わない
内部ログにはエラー詳細を残す
ユーザーには「認証失敗」とだけ伝える

という設計になっています。

「失敗したときに絶対にやってはいけないこと」を
先に決めておくのも、セキュリティ設計の一部です。


Day13 後半のサンプルコード

try / catch の実務寄りミニデモ

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Day13 例外処理 後半</title>
  </head>
  <body>
    <h1>Day13: 例外処理(後半)</h1>

    <script>
      function loadSettings() {
        const raw = localStorage.getItem("settings");
        if (!raw) {
          return { theme: "light", language: "ja" };
        }

        try {
          return JSON.parse(raw);
        } catch (error) {
          console.error("設定のパースに失敗:", error);
          return { theme: "light", language: "ja" };
        }
      }

      function init() {
        const settings = loadSettings();
        console.log("設定:", settings);

        try {
          console.log("アプリを開始します");
        } catch (error) {
          console.error("初期化中にエラー:", error);
          alert("アプリの起動に失敗しました。時間をおいて再度お試しください。");
        }
      }

      init();
    </script>
  </body>
</html>

設定の読み込みは「失敗してもデフォルトで続行」、
初期化の致命的なエラーは「ユーザーに知らせる」という、
強弱をつけた例外処理になっています。


Day13 後半のまとめ

try / catch は「エラーを隠す道具」ではなく、
「エラーが起きたときのふるまいを設計する道具」です。

どこを囲うか
失敗したらどうするか
誰に何を見せるか

ここまで意識できるようになると、
あなたのコードは一気に「壊れにくく、安全で、信頼できる」ものになっていきます。

タイトルとURLをコピーしました