JavaScript | 2週間で身につく、アプリを作りながら学ぶJavaScriptの基本 - 12日目

JavaScript JavaScript
スポンサーリンク

12日目のゴールとテーマ

12日目のテーマは「外部データを“取ってきて使う”体験をする」です。
ここまでで、ブラウザの中だけで完結するアプリ(名簿・タスク)を作ってきました。
今日は一歩外に出て、

外部サービスからデータを取ってくる
JSON というデータ形式を読む
非同期処理(すぐには終わらない処理)と少し仲良くなる

これを、小さな「ランダム名言アプリ」を作りながら体験していきます。


今日作る「ランダム名言アプリ」のイメージ

どんな動きをするアプリかを先に言葉で決める

まずは仕様を言葉で決めます。

画面に「名言を取得する」ボタンがある。
ボタンを押すと、外部のAPI(名言サービス)からデータを取ってくる。
取得した名言と作者を画面に表示する。
通信中は「読み込み中…」と表示しておく。
エラーが起きたら「取得に失敗しました」と表示する。

ここで大事なのは、「外部からデータを取ってくる」という一点です。
名言じゃなくても、天気でもニュースでもいいのですが、
今日はシンプルに「テキスト+作者」の形で進めます。


API と JSON をざっくりイメージで理解する

「データの自動販売機」と「データの箱」

難しい言葉を一気に覚える必要はありません。
イメージだけ持っておきましょう。

API というのは、「データの自動販売機」のようなものです。
決められたURLに「ください」とアクセスすると、
決められた形でデータを返してくれます。

その「決められた形」が JSON です。
JSON は、JavaScript のオブジェクトや配列にとてもよく似た「データの箱」の書き方です。

例えば、こんな感じです。

{
  "text": "人生とは自転車のようなものだ。倒れないようにするには走り続けなければならない。",
  "author": "アルベルト・アインシュタイン"
}

これは、「text というキーに名言の本文」「author というキーに作者名」が入っている箱です。
JavaScript では、これをオブジェクトとして扱えます。


HTML の土台を用意する

ボタンと表示エリアだけのシンプルな画面

index.html を次のように用意します。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>12日目:ランダム名言アプリ</title>
</head>
<body>
  <h1>ランダム名言アプリ</h1>

  <div>
    <button id="fetch-quote-button">名言を取得する</button>
  </div>

  <h2>結果</h2>
  <div id="status-area">
    ボタンを押して名言を取得してください。
  </div>
  <div id="quote-area"></div>
  <div id="author-area"></div>

  <script src="main.js"></script>
</body>
</html>
HTML

ここで押さえておきたいのは、三つの表示エリアです。

status-area
今の状態(待機中・読み込み中・成功・失敗)を表示する場所。

quote-area
名言の本文を表示する場所。

author-area
作者名を表示する場所。

JavaScript からこれらをつかんで、状態に応じて中身を書き換えていきます。


fetch という「取りに行く関数」を知る

「URL にアクセスして、結果を Promise で返す」

ブラウザには、fetch という関数が用意されています。
これは、「指定したURLにアクセスして、結果を返してくれる関数」です。

ざっくり書くと、こういう形です。

fetch("https://example.com/api/quote")
  .then(function (response) {
    // ここでレスポンスを処理する
  })
  .catch(function (error) {
    // ここでエラーを処理する
  });
JavaScript

ここで初めて出てくるのが thencatch です。
これは「非同期処理」のための書き方です。

非同期というのは、「すぐには終わらない処理」のことです。
ネットワーク通信は時間がかかるので、
「終わったらこの関数を呼んでね」という形で書きます。

fetch(...) は、「すぐに結果そのもの」ではなく、「そのうち結果が入る約束(Promise)」を返します。
then は「結果が返ってきたときに呼ばれる関数」、
catch は「エラーが起きたときに呼ばれる関数」です。

ここは一気に完璧に理解しなくて大丈夫です。
「ネットワークは時間がかかるから、終わったら then の中が動く」とだけ押さえておきましょう。


main.js の準備と要素の取得

まずは DOM をつかんで、ボタンにイベントを付ける

main.js を作り、次のように書きます。

let fetchQuoteButtonElement = document.getElementById("fetch-quote-button");
let statusAreaElement = document.getElementById("status-area");
let quoteAreaElement = document.getElementById("quote-area");
let authorAreaElement = document.getElementById("author-area");
JavaScript

次に、「名言を取得する」ボタンにイベントを付けます。

fetchQuoteButtonElement.addEventListener("click", function () {
  fetchRandomQuote();
});
JavaScript

ここで fetchRandomQuote という関数は、これから作る「名言を取りに行く処理」の本体です。
イベントリスナーは、「ボタンが押されたら、その関数を呼ぶ」だけにしておきます。


名言を取得する関数の骨組みを作る

状態表示をしながら、fetch を呼ぶ

fetchRandomQuote 関数を次のように書きます。

function fetchRandomQuote() {
  statusAreaElement.textContent = "読み込み中…";
  quoteAreaElement.textContent = "";
  authorAreaElement.textContent = "";

  let url = "https://example.com/api/random-quote";

  fetch(url)
    .then(function (response) {
      if (!response.ok) {
        throw new Error("HTTP エラー: " + response.status);
      }

      return response.json();
    })
    .then(function (data) {
      showQuote(data);
    })
    .catch(function (error) {
      console.error("名言の取得に失敗しました:", error);
      statusAreaElement.textContent = "取得に失敗しました。時間をおいて再度お試しください。";
    });
}
JavaScript

ここでの重要ポイントを丁寧に見ていきます。

最初に、status-area に「読み込み中…」と表示しています。
同時に、quote-area と author-area を空にしています。
これで、「今は通信中なんだな」とユーザーに伝わります。

url には、本来は実在するAPIのURLを入れます(ここでは例として書いています)。

fetch(url) で通信を開始し、
最初の thenresponse(レスポンス)を受け取ります。

response.ok は、「HTTPステータスが 200番台かどうか」を表します。
もし ok でなければ、throw new Error(...) でエラーを投げて、
後ろの catch に処理を飛ばします。

response.json() は、「レスポンスの中身を JSON として読み込んで、JavaScript のオブジェクトに変換する」処理です。
これも非同期なので、return response.json(); と書いて、
次の then に「変換後の data」を渡しています。

二つ目の then では、data を受け取って showQuote(data) を呼んでいます。
ここで「画面に表示する処理」を分離しているのがポイントです。

catch では、通信エラーや HTTP エラーが起きたときに呼ばれます。
console.error で詳細を出しつつ、画面には「取得に失敗しました」と表示しています。


取得したデータを画面に表示する

JSON の中身をオブジェクトとして扱う

次に、showQuote 関数を書きます。

function showQuote(data) {
  console.log("取得したデータ:", data);

  let text = data.text;
  let author = data.author;

  if (!text) {
    statusAreaElement.textContent = "名言の本文が取得できませんでした。";
    quoteAreaElement.textContent = "";
    authorAreaElement.textContent = "";
    return;
  }

  statusAreaElement.textContent = "取得に成功しました。";

  quoteAreaElement.textContent = "「" + text + "」";

  if (author) {
    authorAreaElement.textContent = "― " + author;
  } else {
    authorAreaElement.textContent = "― 作者不明";
  }
}
JavaScript

ここでの重要ポイントを深掘りします。

data は、JSON をパースした結果のオブジェクトです。
例えば、{ text: "...", author: "..." } のような形を想定しています。

let text = data.text; で本文を取り出し、
let author = data.author; で作者名を取り出しています。

もし text が空(null や undefined)なら、
「名言の本文が取得できませんでした」と表示して return しています。
これは、「API 側のデータがおかしい」場合に備えた防御です。

正常な場合は、status-area に「取得に成功しました。」と表示し、
quote-area に「「本文」」という形で表示しています。

作者名がある場合は「― 作者名」、
ない場合は「― 作者不明」としています。
ここでも、「null や undefined の可能性を考える」クセを意識しています。


非同期処理の流れを言葉で追ってみる

「ボタンを押してから、画面が更新されるまで」

ここまでの流れを、コードではなく“物語”として追ってみます。

ユーザーが「名言を取得する」ボタンを押す。
クリックイベントが発火し、fetchRandomQuote() が呼ばれる。
status-area に「読み込み中…」と表示される。
fetch が URL にアクセスしに行く(ここで少し時間がかかる)。
レスポンスが返ってきたら、最初の then が呼ばれる。
HTTP ステータスをチェックし、問題なければ response.json() を呼ぶ。
JSON のパースが終わったら、二つ目の then が呼ばれ、showQuote(data) が実行される。
showQuote が、quote-area と author-area にテキストを表示する。
同時に、status-area に「取得に成功しました。」と表示される。
もし途中でエラーが起きたら、catch が呼ばれ、「取得に失敗しました。」と表示される。

この流れを一度自分の言葉で説明できると、
「非同期処理って、ただ“時間差で then が呼ばれるだけなんだな”」という感覚がつかめてきます。


async / await という書き方も少しだけ覗いてみる

「then をつなげる」代わりに「一見ふつうの順番で書ける」

今は then を使った書き方をしましたが、
同じことを async / await という構文で書くこともできます。

完全に理解するのはもう少し先でいいですが、
「こういう書き方もあるんだな」とだけ見ておきましょう。

async function fetchRandomQuote() {
  statusAreaElement.textContent = "読み込み中…";
  quoteAreaElement.textContent = "";
  authorAreaElement.textContent = "";

  let url = "https://example.com/api/random-quote";

  try {
    let response = await fetch(url);

    if (!response.ok) {
      throw new Error("HTTP エラー: " + response.status);
    }

    let data = await response.json();

    showQuote(data);
  } catch (error) {
    console.error("名言の取得に失敗しました:", error);
    statusAreaElement.textContent = "取得に失敗しました。時間をおいて再度お試しください。";
  }
}
JavaScript

ポイントだけ言うと、

関数の前に async を付けると、その中で await が使える
await fetch(url) は、「fetch が終わるまで待ってから、結果を response に入れる」
try { ... } catch (error) { ... } で、エラー処理をまとめて書ける

という感じです。

見た目としては、「上から順番に処理が進んでいる」ように見えるので、
then より読みやすいと感じる人も多いです。


12日目で一番大事な感覚

「ブラウザの外の世界とつながった」

今日あなたが体験したのは、かなり大きな一歩です。

今までは、「自分で用意した配列や localStorage の中身」だけを相手にしていました。
今日は、「外部サービスからデータをもらって、それを画面に反映する」という流れを通しました。

これは、現実のWebアプリのほとんどがやっていることです。

天気アプリなら、天気APIからデータを取ってくる。
ニュースアプリなら、ニュースAPIから記事一覧を取ってくる。
チャットアプリなら、サーバーからメッセージを取ってくる。

その入口に、もう立っています。


12日目のまとめ

今日のキーポイントを短く整理すると、こうなります。

API は「決められたURLにアクセスすると、決められた形でデータを返してくれる仕組み」。
JSON は「JavaScript のオブジェクトに似た“データの箱”の書き方」。
fetch は「URL にアクセスして、結果を Promise(そのうち結果が入る約束)として返す関数」。
then / catch で、「成功したとき」「失敗したとき」の処理を分けて書ける。
非同期処理は、「時間がかかるから、終わったらこの関数を呼んでね」という世界。

もし余裕があれば、
今日の名言アプリを少しアレンジしてみてください。

例えば、

ボタンを押した回数を数えて、「何回目の名言か」を表示する
前回の名言を localStorage に保存しておき、「前回の名言」も表示する
「日本語だけ」「英語だけ」など、フィルタ用のボタンを増やす(API側が対応していれば)

など、小さな工夫でいいです。

「外からデータをもらって、自分のアプリの中で料理する」
この感覚が育ってくると、作れるものの世界が一気に広がります。

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