6日目のゴールと今日やること
6日目のテーマは
「Datamuse アプリの“中身の設計”を整えて、コードを読める・直せる・拡張できる状態にする」
ことです。
技術キーワードはいつも通りです。
- fetch
- Promise / async-await
- エラーハンドリング
ただし今日は、これらを「書ける」から一歩進めて、
- 処理を役割ごとに分ける(責務分離)
- fetch 周りのコードを“パターン”として固める
- 通信失敗時の分岐を、読みやすい形に整理する
- 小さな機能追加をしながら、設計の大事さを体感する
という「中級者の視点」を意識していきます。
まず“責務”でコードを分けて考える
4つの役割に分解してみる
Datamuse アプリのコードは、ざっくり次の 4 つに分けられます。
- 状態管理(state)
- API 通信(fetch / async/await / エラーハンドリング)
- UI 更新(render 系の関数)
- イベント処理(クリック・入力などのきっかけ)
6日目では、この 4 つを意識してコードを整理していきます。
状態管理を“見える化”する
state を「アプリの現在地」として扱う
今までバラバラに持っていた変数を、
ひとつのオブジェクトにまとめておきます。
const state = {
word: "",
mode: "ml",
isLoading: false,
favorites: [],
history: [],
recent: []
};
JavaScript状態を更新する小さな関数を用意します。
function updateState(updates) {
Object.assign(state, updates);
}
JavaScript例えば、検索開始時はこう書けます。
updateState({
word,
mode,
isLoading: true
});
JavaScriptここでのポイントは、
- 「今アプリがどういう状態か」を state を見るだけで把握できる
- どこからでも同じ方法で状態を更新できる
という“見通しの良さ”です。
API 通信部分を「完全にパターン化」する
Datamuse 用の共通関数を最終形に近づける
6日目では、requestDatamuse を
「どの機能からも安心して呼べる土台」にします。
async function requestDatamuse(params) {
const baseUrl = "https://api.datamuse.com/words";
const url = `${baseUrl}?${params.toString()}`;
let response;
try {
response = await fetch(url);
} catch (networkError) {
throw new Error("ネットワークエラーが発生しました。接続を確認してください。");
}
if (!response.ok) {
throw new Error(`HTTPエラー(${response.status})が発生しました。`);
}
let data;
try {
data = await response.json();
} catch (parseError) {
throw new Error("レスポンスの解析に失敗しました。");
}
if (!Array.isArray(data)) {
throw new Error("予期しないレスポンス形式です。");
}
return data;
}
JavaScriptここで深掘りしたいのは、
- fetch 自体の失敗(ネットワーク)
- HTTP ステータスエラー(4xx / 5xx)
- JSON パースエラー
- データ形式エラー
をすべて「メッセージ付きの Error」として上に投げていることです。
これで、上位の関数は
「何をしようとして失敗したか」だけを足してユーザーに伝えればよくなる
という状態になります。
検索処理を“読みやすい流れ”に整える
URL を組み立てる部分を分離する
検索用のパラメータを作る関数を用意します。
function buildSearchParams(word, mode) {
const params = new URLSearchParams();
if (mode === "ml") {
params.set("ml", word);
} else if (mode === "rel_trg") {
params.set("rel_trg", word);
} else if (mode === "rel_rhy") {
params.set("rel_rhy", word);
} else {
params.set("ml", word);
}
params.set("max", "20");
return params;
}
JavaScript検索のメイン関数を“ストーリー”として読む
async function fetchWordsForMode(word, mode) {
if (state.isLoading) return;
startLoading("単語を取得中です…");
resultDiv.textContent = "";
try {
const params = buildSearchParams(word, mode);
const data = await requestDatamuse(params);
if (data.length === 0) {
const label = getModeShortLabel(mode);
showError(`${label}が見つかりませんでした。`);
resultDiv.textContent = "";
return;
}
const sorted = [...data].sort((a, b) => (b.score || 0) - (a.score || 0));
const label = getModeShortLabel(mode);
showSuccess(`${label}の取得に成功しました。`);
renderWords(sorted, mode);
addHistory(word, mode);
addRecent(word);
} catch (error) {
const label = getModeShortLabel(mode);
showError(`${label}の取得中にエラーが発生しました:${error.message}`);
console.error(error);
} finally {
endLoading();
}
}
JavaScriptこの関数は、上から順に読むとこういう“物語”になっています。
- すでにローディング中なら何もしない
- ローディング状態にして、画面をクリア
- パラメータを作って Datamuse にリクエスト
- 結果が空なら「見つからなかった」と伝える
- 結果をスコア順に並べて表示
- 履歴と最近を更新
- どこかでエラーが起きたら、モード名付きでメッセージ表示
- 最後にローディング状態を解除
ここでの大事なポイントは、
「fetch の細かいエラー処理は requestDatamuse に隠している」
ということです。
ローディング表示とエラー表示を“パターン”として固定する
ステータス表示の関数を使い切る
6日目では、ステータス表示を完全に関数に寄せます。
function setStatus(message, type) {
statusDiv.textContent = message;
statusDiv.className = "status " + type;
}
function showLoading(message) {
setStatus(message || "取得中です…", "loading");
}
function showSuccess(message) {
setStatus(message, "success");
}
function showError(message) {
setStatus(message, "error");
}
JavaScriptCSS で見た目を揃えます。
.status.loading { color: #555; }
.status.success { color: #0a0; }
.status.error { color: #c00; }
ローディング開始・終了を state と連動させる
function startLoading(message) {
updateState({ isLoading: true });
showLoading(message || "取得中です…");
searchButton.disabled = true;
}
function endLoading() {
updateState({ isLoading: false });
searchButton.disabled = false;
}
JavaScriptこれで、
- 「ローディング中かどうか」は state.isLoading
- 見た目は showLoading / showSuccess / showError
- ボタンの無効化も startLoading / endLoading に集約
という“お作法”が固まります。
通信失敗時の分岐を“用途ごと”に整理する
メイン検索とサジェストで重みを変える
メイン検索はユーザーにとって重要なので、
失敗したらしっかりメッセージを出します。
} catch (error) {
const label = getModeShortLabel(mode);
showError(`${label}の取得中にエラーが発生しました:${error.message}`);
}
JavaScript一方、サジェストは「おまけ機能」なので、
失敗しても静かにログだけ残します。
async function fetchSuggestions(prefix) {
if (!prefix) {
suggestDiv.textContent = "";
return;
}
const params = new URLSearchParams();
params.set("sp", prefix + "*");
params.set("max", "5");
try {
const data = await requestDatamuse(params);
if (data.length === 0) {
suggestDiv.textContent = "";
return;
}
renderSuggestions(data);
} catch (error) {
console.error("サジェスト取得中にエラー", error);
suggestDiv.textContent = "";
}
}
JavaScriptここでの深掘りポイントは、
- すべてのエラーを同じように扱わなくていい
- 「ユーザーにとってどれくらい重要な処理か」でメッセージの重さを変える
という“エラーハンドリングの設計”です。
小さな機能追加で「設計の良さ」を体感する
例:モードごとの「デフォルトメッセージ」を足してみる
モードごとに、
「こういうときに使うといいよ」という説明を出したくなったとします。
function getModeDescription(mode) {
if (mode === "ml") return "類義語:意味が近い単語を探します。";
if (mode === "rel_trg") return "連想語:その単語から連想される単語を探します。";
if (mode === "rel_rhy") return "韻:語尾の音が似ている単語を探します。";
return "";
}
JavaScript検索成功時に、
ステータスに一言足すこともできます。
const label = getModeShortLabel(mode);
const desc = getModeDescription(mode);
showSuccess(`${label}の取得に成功しました。 ${desc}`);
JavaScriptここで気づいてほしいのは、
「fetch の型をいじらなくても、機能はどんどん足せる」
ということです。
設計が整理されていると、
“どこに何を足せばいいか”が自然に見えてきます。
6日目のまとめ
6日目でやったことを整理すると、こうなります。
- state にアプリの状態を集約し、updateState で一元管理
- requestDatamuse に fetch・async/await・エラーハンドリングを集約
- fetchWordsForMode を「ストーリーとして読める形」に整理
- ローディング・成功・エラー表示を関数と CSS でパターン化
- メイン検索とサジェストでエラーの“重さ”を変える
- 小さな機能追加(説明文など)を通して、設計の良さを体感
どれも新しい文法ではなく、
「同じ fetch / async/await / エラーハンドリングを、どう整理して使うか」
という話です。
今日いちばん深く理解してほしいこと
6日目の本質は、
「コードは“動けばいい”から、“読めて直せて拡張できる”に育てていくもの」
ということです。
fetch
async/await
try-catch
エラーメッセージ
ローディング状態
state
これらはもう、あなたの中でバラバラではなく、
ひとつの“型”としてまとまり始めています。
7日目は、この Datamuse アプリを
「中級編の完成形」としてまとめていく回にしよう。
そのとき、今日整えた設計が、ものすごく効いてきます。


