- 2日目のゴールと今日やること
- 1日目のアプリをざっくり振り返る
- 金額入力を追加する
- 金額の入力チェックをちゃんとやる
- ExchangeRate.host の amount パラメータを使うかどうか
- ローディング表示を少し丁寧にする
- 通貨変換関数の全体像(2日目版)
- 重要ポイント1:入力チェックを「通貨」と「金額」で分ける
- 重要ポイント2:URL を“安全に”組み立てる
- 重要ポイント3:HTTP エラーを「サーバー側」と「その他」で分ける
- 重要ポイント4:API レベルの失敗とデータ形式のチェック
- 重要ポイント5:catch で「通信失敗」を一括で扱う
- 通貨変換結果を表示する
- ボタンにイベントをつなぐ
- 2日目のまとめ
- 今日いちばん深く理解してほしいこと
2日目のゴールと今日やること
2日目のテーマは
「1日目の“レートを見るだけアプリ”を、“ちゃんと使える通貨変換ツール”に育てる」 ことです。
やることはこうです。
- 金額を入力して「いくらになるか」まで計算する
- 入力チェックをちゃんとやる(未入力・マイナス・文字など)
- ローディング表示を少し丁寧にする
- エラーメッセージを「原因別」に分ける
技術の柱は昨日と同じです。
- fetch
- Promise / async‑await
- エラーハンドリング(try / catch)
今日は「新しい文法」よりも、
昨日の型を“現実のアプリ”に寄せていく作業 です。
1日目のアプリをざっくり振り返る
昨日の流れを言葉で確認する
1日目のアプリは、こんな流れでした。
- from 通貨(例:JPY)を選ぶ
- to 通貨(例:USD)を選ぶ
- 「レート取得」ボタンを押す
- fetch で ExchangeRate.host の
/convertにアクセス - JSON をパース
- success や result をチェック
- レートを表示
- 通信失敗時にメッセージを出す
つまり、「1 JPY = 0.00… USD」を見るところまではできている状態です。
2日目では、ここに 「金額 × レート」 を足していきます。
金額入力を追加する
HTML のイメージ
頭の中で、こんな UI をイメージしてください。
<input id="amountInput" type="number" placeholder="金額を入力(例: 1000)" />
<select id="fromSelect">
<option value="JPY">JPY</option>
<option value="USD">USD</option>
<option value="EUR">EUR</option>
</select>
<select id="toSelect">
<option value="USD">USD</option>
<option value="JPY">JPY</option>
<option value="EUR">EUR</option>
</select>
<button id="convertButton">通貨変換</button>
<div id="status"></div>
<div id="result"></div>
JavaScript 側では、こう取ります。
const amountInput = document.getElementById("amountInput");
const fromSelect = document.getElementById("fromSelect");
const toSelect = document.getElementById("toSelect");
const convertButton = document.getElementById("convertButton");
const statusDiv = document.getElementById("status");
const resultDiv = document.getElementById("result");
JavaScript金額の入力チェックをちゃんとやる
「空じゃないか」だけでは足りない
金額入力で起こりがちなパターンはこうです。
- 空欄
- 0
- マイナス
- 文字列(ブラウザによっては入り得る)
- めちゃくちゃ大きい数
これを一箇所でチェックする関数を作ります。
function parseAmount(raw) {
if (!raw) {
return { ok: false, message: "金額を入力してください。" };
}
const amount = Number(raw);
if (!Number.isFinite(amount)) {
return { ok: false, message: "金額は数値で入力してください。" };
}
if (amount <= 0) {
return { ok: false, message: "金額は 1 以上を入力してください。" };
}
if (amount > 1_000_000_000) {
return { ok: false, message: "金額が大きすぎます。もう少し小さい値を入力してください。" };
}
return { ok: true, value: amount };
}
JavaScript深掘りポイント
ここでやっているのは、
- 「入力されているか」
- 「数値として解釈できるか」
- 「0 より大きいか」
- 「現実的な範囲か」
を分けてチェックしていることです。
エラーメッセージも「何がダメなのか」がわかるようにしてあります。
ExchangeRate.host の amount パラメータを使うかどうか
2つのやり方がある
通貨変換には、実は 2 パターンあります。
- 「1 単位のレート」を取って、自分で掛け算する
- API に amount を渡して「いくらになるか」まで計算してもらう
ExchangeRate.host の /convert は、amount を渡せます。
https://api.exchangerate.host/convert?from=JPY&to=USD&amount=1000
返ってくる JSON の result が、
「1000 JPY が何 USD か」になります。
今日は 2番の「API に計算してもらう」 パターンでいきます。
その方がコードがシンプルで、API の使い方もよくわかります。
ローディング表示を少し丁寧にする
1日目のパターンを思い出す
昨日はこんな感じでした。
function startLoading(message) {
statusDiv.textContent = message || "取得中です…";
convertButton.disabled = true;
}
function endLoading() {
convertButton.disabled = false;
}
JavaScript2日目でもこの形を使いますが、
メッセージを「何をしているか」に合わせて変えます。
通貨変換関数の全体像(2日目版)
ここが今日のメインです。
金額入力・fetch・async/await・エラーハンドリング・ローディング
が全部入った関数を書きます。
async function convertCurrency() {
const rawAmount = amountInput.value.trim();
const from = fromSelect.value;
const to = toSelect.value;
if (from === to) {
statusDiv.textContent = "異なる通貨を選択してください。";
resultDiv.textContent = "";
return;
}
const parsed = parseAmount(rawAmount);
if (!parsed.ok) {
statusDiv.textContent = parsed.message;
resultDiv.textContent = "";
return;
}
const amount = parsed.value;
startLoading(`${amount} ${from} → ${to} に変換中です…`);
resultDiv.textContent = "";
try {
const url =
`https://api.exchangerate.host/convert` +
`?from=${encodeURIComponent(from)}` +
`&to=${encodeURIComponent(to)}` +
`&amount=${encodeURIComponent(amount)}`;
const response = await fetch(url);
if (!response.ok) {
if (response.status >= 500) {
statusDiv.textContent = "サーバー側でエラーが発生しています。時間をおいて再試行してください。";
} else {
statusDiv.textContent = `サーバーエラーが発生しました。(${response.status})`;
}
return;
}
const data = await response.json();
if (!data || data.success === false) {
statusDiv.textContent = "レートの取得に失敗しました。";
console.error("API error response:", data);
return;
}
if (typeof data.result !== "number") {
statusDiv.textContent = "予期しない形式のデータが返されました。";
console.error("Unexpected data:", data);
return;
}
statusDiv.textContent = "通貨変換に成功しました。";
renderConversion(from, to, amount, data.result, data.info?.rate);
} catch (error) {
statusDiv.textContent = "通信に失敗しました。ネットワークを確認してください。";
console.error(error);
} finally {
endLoading();
}
}
JavaScriptここを、重要ポイントごとに分解していきます。
重要ポイント1:入力チェックを「通貨」と「金額」で分ける
if (from === to) {
statusDiv.textContent = "異なる通貨を選択してください。";
resultDiv.textContent = "";
return;
}
const parsed = parseAmount(rawAmount);
if (!parsed.ok) {
statusDiv.textContent = parsed.message;
resultDiv.textContent = "";
return;
}
JavaScriptここで、
- 通貨の組み合わせが妥当か
- 金額が妥当か
を別々にチェックしています。
これにより、
「どこが悪いのか」がユーザーに伝わりやすくなります。
重要ポイント2:URL を“安全に”組み立てる
const url =
`https://api.exchangerate.host/convert` +
`?from=${encodeURIComponent(from)}` +
`&to=${encodeURIComponent(to)}` +
`&amount=${encodeURIComponent(amount)}`;
JavaScriptencodeURIComponent を使っているのは、
通貨コードや金額に予期せぬ文字が入っても
URL として壊れないようにするためです。
今回は通貨コードが英大文字、金額が数字なので、
実際には問題になりにくいですが、
「パラメータは encode する」 という癖をつけておくと、
他の API を扱うときに役立ちます。
重要ポイント3:HTTP エラーを「サーバー側」と「その他」で分ける
if (!response.ok) {
if (response.status >= 500) {
statusDiv.textContent = "サーバー側でエラーが発生しています。時間をおいて再試行してください。";
} else {
statusDiv.textContent = `サーバーエラーが発生しました。(${response.status})`;
}
return;
}
JavaScriptここでやっているのは、
- 500 番台 → サーバー側の問題
- それ以外 → 一般的なサーバーエラー
という分け方です。
ユーザーにとっては、
「自分の入力が悪いのか」
「サーバーが悪いのか」
がわかるだけで、ストレスがかなり減ります。
重要ポイント4:API レベルの失敗とデータ形式のチェック
if (!data || data.success === false) {
statusDiv.textContent = "レートの取得に失敗しました。";
console.error("API error response:", data);
return;
}
if (typeof data.result !== "number") {
statusDiv.textContent = "予期しない形式のデータが返されました。";
console.error("Unexpected data:", data);
return;
}
JavaScriptここで、
- HTTP 的には成功でも、API 的には失敗 → 「レートの取得に失敗しました。」
- result が数値でない → 「予期しない形式のデータ」
と分けています。
「通信は成功したけど、データがおかしい」
というケースをちゃんと扱っているのがポイントです。
重要ポイント5:catch で「通信失敗」を一括で扱う
} catch (error) {
statusDiv.textContent = "通信に失敗しました。ネットワークを確認してください。";
console.error(error);
}
JavaScriptここは 1日目と同じですが、
改めてとても重要です。
- Wi‑Fi が切れている
- DNS が引けない
- ブラウザがオフライン
など、HTTP ステータス以前の問題は、
ここで一括して「通信失敗」として扱います。
通貨変換結果を表示する
レートと変換結果を両方見せる
function renderConversion(from, to, amount, converted, rate) {
const rateText =
typeof rate === "number"
? `1 ${from} = ${rate} ${to}`
: "";
const html = `
<h3>通貨変換結果</h3>
<p>${amount} ${from} = ${converted} ${to}</p>
<p>${rateText}</p>
`;
resultDiv.innerHTML = html;
}
JavaScriptここで、
- 「1000 JPY = 6.8 USD」
- 「1 JPY = 0.0068 USD」
のように、
変換結果とレートの両方 を表示しています。
ユーザーにとっては、
「このレートで計算されたんだな」が見えると安心感が増します。
ボタンにイベントをつなぐ
convertButton.addEventListener("click", convertCurrency);
JavaScriptこれで、
- 金額を入力
- 通貨を選ぶ
- ボタンを押す
- ローディング開始
- API から取得
- 成功 or 失敗で分岐
- ローディング終了
という一連の流れが完成します。
2日目のまとめ
今日あなたがやったことを、言葉で整理してみます。
- 金額入力欄を追加した
- parseAmount で「空・数値・0 以下・大きすぎる」をチェックした
- 通貨の組み合わせ(from と to が同じ)を最初に弾いた
- ExchangeRate.host の
/convertに amount を渡して「変換後の金額」まで取得した - URL を encodeURIComponent で安全に組み立てた
- HTTP エラーを「サーバー側(500 番台)」と「その他」で分けてメッセージを変えた
- API レベルの success と result の型をチェックした
- ローディング表示を「何をしているか」に合わせて出した
- 通信失敗時に「ネットワークを確認してください」と伝えた
- 変換結果とレートの両方を表示する renderConversion を作った
どれも新しい文法ではなく、
「昨日の型を、現実の通貨変換アプリに寄せていった」 内容です。
今日いちばん深く理解してほしいこと
2日目の本質は、
「fetch / async‑await / エラーハンドリングは、“入力チェック”と組み合わさったときにアプリになる」
ということです。
通貨の組み合わせ
金額の妥当性
サーバーの状態
API の成功・失敗
データ形式の妥当性
ネットワークの状態
これらをひとつずつ分けて考え、
それぞれに合ったメッセージを出してあげる。
それができているあなたは、
もう「API を叩いてみる人」ではなく、
「API を使ったツールを設計する人」 の側に立っています。
3日目では、この通貨変換アプリに
- 「逆方向に変換」
- 「よく使う通貨ペアのプリセット」
- 「入力の自動補完」
などを足して、
さらに“使っていて気持ちいいツール”に育てていきます。

