5日目のゴールと今日やること
5日目のテーマは
「ExchangeRate.host 通貨変換アプリを“中級者の設計”に進化させる」
ことです。
ここまでであなたは、
- 金額変換
- 入力チェック
- ローディング表示
- エラーハンドリング
- 逆変換ボタン
- プリセット通貨ペア
- お気に入り保存(state + localStorage)
といった、通貨変換アプリの基礎と応用をしっかり作ってきました。
5日目では、これらをさらに一段レベルアップさせて、
- fetch を共通化して「どこでも同じ型で使える」ようにする
- Promise / async‑await を使って「複数通貨の一括変換」を実装する
- エラーハンドリングを「原因別」に整理する
- ローディング表示を「複数リクエスト」に対応させる
という、まさに中級者らしい設計に踏み込みます。
fetch を「共通関数」にまとめるという発想
なぜ共通化が必要なのか
通貨変換アプリでは、いろいろな場面で fetch を使います。
- 通常の変換
- お気に入りからの再変換
- 複数通貨の一括変換
- プリセットからの変換
これらすべてで fetch を直書きすると、
- エラーハンドリングがバラバラ
- 修正したいときに全部直す必要がある
- コードが読みづらくなる
という問題が出てきます。
そこで、
「JSON を返す API を叩くための共通関数」 を作ります。
共通の requestJson 関数を作る
まずは素直な形で書く
async function requestJson(url) {
try {
const response = await fetch(url);
if (!response.ok) {
if (response.status >= 500) {
throw new Error("サーバー側でエラーが発生しています。時間をおいて再試行してください。");
}
throw new Error(`HTTPエラー(${response.status})`);
}
const data = await response.json();
return data;
} catch (error) {
throw new Error(error.message);
}
}
JavaScript深掘りポイント
この関数は、
- fetch の失敗(ネットワークエラー)
- HTTP エラー(404, 500 など)
- JSON パースエラー
をすべて Error として上に投げる 役割を持っています。
これにより、
上位の関数は「成功したか・失敗したか」だけを扱えばよくなります。
ExchangeRate.host 専用の変換関数を作る
API の仕様チェックをここに集約する
async function convertViaAPI(from, to, amount) {
const url =
`https://api.exchangerate.host/convert` +
`?from=${encodeURIComponent(from)}` +
`&to=${encodeURIComponent(to)}` +
`&amount=${encodeURIComponent(amount)}`;
const data = await requestJson(url);
if (!data || data.success === false) {
throw new Error("レートの取得に失敗しました。");
}
if (typeof data.result !== "number") {
throw new Error("予期しない形式のデータが返されました。");
}
return {
converted: data.result,
rate: data.info?.rate
};
}
JavaScript深掘りポイント
convertViaAPI は
「ExchangeRate.host の仕様を知っている唯一の関数」
です。
UI 側は API の細かい仕様を知らなくてよくなり、
コードが読みやすくなります。
UI 側の convertCurrency を“ストーリーとして読める”形にする
5日目版 convertCurrency(完成形)
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;
setLoading(true, `${amount} ${from} → ${to} に変換中です…`);
resultDiv.textContent = "";
try {
const { converted, rate } = await convertViaAPI(from, to, amount);
updateState({ lastResult: { from, to, amount, converted, rate } });
statusDiv.textContent = "通貨変換に成功しました。";
renderConversion(from, to, amount, converted, rate);
} catch (error) {
statusDiv.textContent = `変換中にエラーが発生しました:${error.message}`;
console.error(error);
} finally {
setLoading(false);
}
}
JavaScript深掘りポイント
この関数は、
「変換の流れをそのまま読める」
という設計になっています。
- 入力チェック
- ローディング開始
- convertViaAPI で変換
- 成功時の表示
- 失敗時のメッセージ
- ローディング終了
fetch の細かい処理はconvertViaAPI と requestJson に隠れているため、
とても読みやすくなっています。
Promise.all を使って「複数通貨の一括変換」を実装する
複数通貨変換のニーズ
ユーザーはよくこう思います。
「1000 JPY を USD と EUR と GBP で同時に見たい」
これを実現するのが Promise.all です。
複数通貨の変換関数
async function convertToMultiple(from, amount, targets) {
const promises = targets.map((to) =>
convertViaAPI(from, to, amount)
);
const results = await Promise.all(promises);
return results.map((res, index) => ({
from,
to: targets[index],
amount,
converted: res.converted,
rate: res.rate
}));
}
JavaScript深掘りポイント
Promise.all のメリットは、
- 並列で API を叩ける
- 全部終わるまで待てる
- 結果をまとめて扱える
という点です。
複数通貨変換の UI を作る
例:JPY → [USD, EUR, GBP]
async function convertToPopularCurrencies() {
const rawAmount = amountInput.value.trim();
const from = fromSelect.value;
const parsed = parseAmount(rawAmount);
if (!parsed.ok) {
statusDiv.textContent = parsed.message;
return;
}
const amount = parsed.value;
const targets = ["USD", "EUR", "GBP"];
setLoading(true, `${amount} ${from} を複数通貨に変換中です…`);
resultDiv.textContent = "";
try {
const results = await convertToMultiple(from, amount, targets);
updateState({ lastResult: results });
renderMultiple(results);
statusDiv.textContent = "複数通貨への変換に成功しました。";
} catch (error) {
statusDiv.textContent = `変換中にエラーが発生しました:${error.message}`;
console.error(error);
} finally {
setLoading(false);
}
}
JavaScript複数通貨の結果を表示する
function renderMultiple(results) {
let html = `<h3>複数通貨への変換結果</h3>`;
results.forEach((item) => {
html += `
<p>${item.amount} ${item.from} = ${item.converted} ${item.to}
(1 ${item.from} = ${item.rate} ${item.to})</p>
`;
});
resultDiv.innerHTML = html;
}
JavaScript深掘りポイント
複数通貨の結果を一覧で見られると、
アプリの価値が一気に上がります。
ローディング表示を「複数リクエスト」に対応させる
state と連動させる
function setLoading(isLoading, message) {
updateState({ isLoading });
if (isLoading) {
statusDiv.textContent = message || "処理中です…";
}
convertButton.disabled = isLoading;
swapButton.disabled = isLoading;
const presetButtons = document.querySelectorAll(".preset");
presetButtons.forEach((btn) => (btn.disabled = isLoading));
const multiButton = document.getElementById("multiButton");
if (multiButton) multiButton.disabled = isLoading;
}
JavaScript深掘りポイント
複数通貨変換でも、
単一通貨変換でも、
ローディング表示が一貫して動く ようになります。
5日目のまとめ
今日あなたがやったことを整理すると、こうなります。
- fetch を requestJson に共通化
- ExchangeRate.host 専用の convertViaAPI を作成
- UI 側の convertCurrency を「読みやすい流れ」に整理
- Promise.all で複数通貨の一括変換を実装
- 複数通貨変換の UI を追加
- ローディング表示を state と連動
- エラーハンドリングを原因別に整理
どれも新しい文法ではなく、
「fetch / async‑await / エラーハンドリングを“設計として扱う”」
という話です。
今日いちばん深く理解してほしいこと
5日目の本質は、
「API 通信は、型を作ればどんどん応用できる」
ということです。
単一通貨変換
お気に入り
複数通貨変換
localStorage 保存
これらは全部、
同じ fetch / async-await / try-catch の型の上に乗っています。
6日目では、この通貨変換アプリを
さらに整理し、拡張しやすい構造に仕上げる 回に入ります。
あなたのアプリが「本当に使えるツール」になる瞬間が近づいています。

