# 銀行アプリを作成するパート3:データの取得と利用方法 『スタートレック』のエンタープライズのコンピュータを思い浮かべてください。ピカード艦長が船の状況を尋ねると、インターフェース全体がシャットダウンして再構築されることなく、情報が瞬時に表示されます。このシームレスな情報の流れこそが、動的なデータ取得を通じてここで構築しようとしているものです。 現在、あなたの銀行アプリは印刷された新聞のようなものです。情報はありますが静的です。これをNASAのミッションコントロールのように、データが継続的に流れ、リアルタイムで更新され、ユーザーの作業を中断しないものに変えていきます。 サーバーと非同期で通信し、異なるタイミングで到着するデータを処理し、生の情報をユーザーにとって意味のあるものに変換する方法を学びます。これがデモと本番用ソフトウェアの違いです。 ## 講義前のクイズ [講義前のクイズ](https://ff-quizzes.netlify.app/web/quiz/45) ### 前提条件 データ取得に進む前に、以下のコンポーネントを準備してください: - **前回のレッスン**:[ログインと登録フォーム](../2-forms/README.md)を完了してください - これを基盤として構築します - **ローカルサーバー**:[Node.js](https://nodejs.org)をインストールし、[サーバーAPIを実行](../api/README.md)してアカウントデータを提供します - **API接続**:以下のコマンドでサーバー接続をテストします: ```bash curl http://localhost:5000/api # Expected response: "Bank API v1.0.0" ``` この簡単なテストで、すべてのコンポーネントが正しく通信していることを確認します: - Node.jsがシステム上で正しく動作していることを検証 - APIサーバーがアクティブで応答していることを確認 - アプリがサーバーに到達できることを検証(ミッション前の無線接続確認のようなものです) --- ## 現代のウェブアプリにおけるデータ取得の理解 ウェブアプリケーションがデータを処理する方法は、過去20年間で劇的に進化しました。この進化を理解することで、AJAXやFetch APIのような現代的な技術がなぜ強力で、ウェブ開発者にとって必須のツールとなったのかを理解する助けになります。 従来のウェブサイトがどのように動作していたかと、今日構築する動的で応答性の高いアプリケーションを比較してみましょう。 ### 従来のマルチページアプリケーション(MPA) ウェブの初期では、クリックするたびに古いテレビのチャンネルを変えるようなものでした。画面が真っ暗になり、新しいコンテンツにゆっくりと切り替わる。このような状況が初期のウェブアプリケーションの現実であり、すべての操作がページ全体をゼロから再構築することを意味していました。 ```mermaid sequenceDiagram participant User participant Browser participant Server User->>Browser: Clicks link or submits form Browser->>Server: Requests new HTML page Note over Browser: Page goes blank Server->>Browser: Returns complete HTML page Browser->>User: Displays new page (flash/reload) ``` ![マルチページアプリケーションの更新ワークフロー](../../../../translated_images/mpa.7f7375a1a2d4aa779d3f928a2aaaf9ad76bcdeb05cfce2dc27ab126024050f51.ja.png) **このアプローチがぎこちなく感じられる理由:** - クリックするたびにページ全体をゼロから再構築する必要がある - ユーザーが考え中にページの点滅で中断される - インターネット接続がヘッダーやフッターを何度もダウンロードするために過剰に働く - アプリがソフトウェアというよりもファイリングキャビネットをクリックしているように感じられる ### 現代のシングルページアプリケーション(SPA) AJAX(非同期JavaScriptとXML)はこのパラダイムを完全に変えました。国際宇宙ステーションのモジュール設計のように、宇宙飛行士が全体を再構築することなく個々のコンポーネントを交換できるように、AJAXはウェブページの特定の部分を再読み込みせずに更新することを可能にします。名前にXMLが含まれているにもかかわらず、今日では主にJSONを使用しますが、基本的な原則は変わりません:変更が必要な部分だけを更新する。 ```mermaid sequenceDiagram participant User participant Browser participant JavaScript participant Server User->>Browser: Interacts with page Browser->>JavaScript: Triggers event handler JavaScript->>Server: Fetches only needed data Server->>JavaScript: Returns JSON data JavaScript->>Browser: Updates specific page elements Browser->>User: Shows updated content (no reload) ``` ![シングルページアプリケーションの更新ワークフロー](../../../../translated_images/spa.268ec73b41f992c2a21ef9294235c6ae597b3c37e2c03f0494c2d8857325cc57.ja.png) **SPAがより快適に感じられる理由:** - 実際に変更された部分だけが更新される(賢いですよね?) - 突然の中断がなくなる - ユーザーは流れに乗ったまま - ワイヤを通じて移動するデータが少なくなるため、読み込みが速くなる - すべてがスナッピーで応答性が高く、スマホのアプリのように感じられる ### 現代のFetch APIへの進化 現代のブラウザは[`Fetch` API](https://developer.mozilla.org/docs/Web/API/Fetch_API)を提供しており、古い[`XMLHttpRequest`](https://developer.mozilla.org/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest)を置き換えます。電報を操作するのとメールを使う違いのように、Fetch APIはクリーンな非同期コードのためにプロミスを使用し、JSONを自然に処理します。 | 機能 | XMLHttpRequest | Fetch API | |------|---------------|-----------| | **構文** | 複雑なコールバックベース | クリーンなプロミスベース | | **JSON処理** | 手動での解析が必要 | 組み込みの`.json()`メソッド | | **エラー処理** | 限られたエラー情報 | 包括的なエラー詳細 | | **現代的なサポート** | レガシー互換性 | ES6+プロミスとasync/await | > 💡 **ブラウザ互換性**:朗報です - Fetch APIはすべての現代的なブラウザで動作します!特定のバージョンについて興味がある場合は、[caniuse.com](https://caniuse.com/fetch)で完全な互換性情報を確認できます。 > **要点:** - Chrome、Firefox、Safari、Edgeで素晴らしく動作(つまり、ユーザーがいる場所でほぼすべて) - Internet Explorerだけは追加の対応が必要(正直なところ、IEはそろそろ手放す時です) - 後で使用するエレガントなasync/awaitパターンに完璧に対応 ### ユーザーログインとデータ取得の実装 次に、銀行アプリを静的な表示から機能的なアプリケーションに変えるログインシステムを実装します。安全な軍事施設で使用される認証プロトコルのように、ユーザーの資格情報を確認し、その特定のデータへのアクセスを提供します。 基本的な認証から始め、データ取得機能を追加して段階的に構築していきます。 #### ステップ1:ログイン機能の基盤を作成 `app.js`ファイルを開き、新しい`login`関数を追加します。この関数はユーザー認証プロセスを処理します: ```javascript async function login() { const loginForm = document.getElementById('loginForm'); const user = loginForm.user.value; } ``` **これを分解して説明します:** - `async`キーワードはJavaScriptに「この関数は待機が必要かもしれない」と伝えます - ページからフォームを取得(特別なことはせず、IDで見つけるだけ) - 次に、ユーザーが入力したユーザー名を取得 - 便利なトリック:フォーム入力の`name`属性を使ってアクセス可能 - 追加のgetElementById呼び出しは不要! > 💡 **フォームアクセスパターン**:すべてのフォームコントロールは、HTMLで`name`属性を使用して設定された名前としてフォーム要素のプロパティとしてアクセスできます。これにより、フォームデータを取得するためのクリーンで読みやすい方法が提供されます。 #### ステップ2:アカウントデータ取得関数を作成 次に、サーバーからアカウントデータを取得する専用関数を作成します。これは登録関数と同じパターンに従いますが、データ取得に焦点を当てています: ```javascript async function getAccount(user) { try { const response = await fetch('//localhost:5000/api/accounts/' + encodeURIComponent(user)); return await response.json(); } catch (error) { return { error: error.message || 'Unknown error' }; } } ``` **このコードが達成すること:** - **モダンな**`fetch` APIを使用して非同期でデータをリクエスト - **GETリクエストURLを**ユーザー名パラメータで構築 - **特殊文字を安全に処理するために**`encodeURIComponent()`を適用 - **レスポンスを**JSON形式に変換して簡単にデータ操作 - **エラーを**クラッシュする代わりにエラーオブジェクトを返すことで優雅に処理 > ⚠️ **セキュリティ注意**:`encodeURIComponent()`関数はURL内の特殊文字を処理します。海軍通信で使用されるエンコードシステムのように、"#"や"&"などの文字が誤解されることなく、意図した通りにメッセージが届くことを保証します。 > **これが重要な理由:** - 特殊文字がURLを壊すのを防ぐ - URL操作攻撃から保護 - サーバーが意図したデータを受け取ることを保証 - 安全なコーディングプラクティスに従う #### HTTP GETリクエストの理解 驚くかもしれませんが、`fetch`を追加オプションなしで使用すると、自動的に[`GET`](https://developer.mozilla.org/docs/Web/HTTP/Methods/GET)リクエストを作成します。これは、サーバーに「このユーザーのアカウントデータを見せてもらえますか?」と尋ねるのに最適です。 GETリクエストは、図書館で本を借りるように既存のものを要求することに似ています。POSTリクエスト(登録時に使用したもの)は、新しい本をコレクションに追加するようなものです。 | GETリクエスト | POSTリクエスト | |--------------|---------------| | **目的** | 既存データを取得 | 新しいデータをサーバーに送信 | | **パラメータ** | URLパス/クエリ文字列内 | リクエストボディ内 | | **キャッシュ** | ブラウザによるキャッシュ可能 | 通常キャッシュされない | | **セキュリティ** | URL/ログに表示される | リクエストボディ内で非表示 | #### ステップ3:すべてをまとめる さて、満足感のある部分です。アカウント取得関数をログインプロセスに接続しましょう。これがすべてがうまく機能する瞬間です: ```javascript async function login() { const loginForm = document.getElementById('loginForm'); const user = loginForm.user.value; const data = await getAccount(user); if (data.error) { return console.log('loginError', data.error); } account = data; navigate('/dashboard'); } ``` この関数は明確な手順に従います: - フォーム入力からユーザー名を抽出 - サーバーからユーザーのアカウントデータをリクエスト - プロセス中に発生するエラーを処理 - アカウントデータを保存し、成功時にダッシュボードに移動 > 🎯 **Async/Awaitパターン**:`getAccount`は非同期関数であるため、`await`キーワードを使用してサーバーが応答するまで実行を一時停止します。これにより、未定義のデータでコードが続行するのを防ぎます。 #### ステップ4:データの保存場所を作成 アカウント情報が読み込まれたら、それを保持する場所がアプリに必要です。これはアプリの短期記憶のようなもので、現在のユーザーのデータを手元に置いておく場所です。`app.js`ファイルの冒頭にこの行を追加してください: ```javascript // This holds the current user's account data let account = null; ``` **これが必要な理由:** - アプリ内のどこからでもアカウントデータにアクセス可能 - `null`で始めることで「まだ誰もログインしていない」ことを示す - 誰かがログインまたは登録に成功したときに更新される - 単一の真実の情報源として機能 - 誰がログインしているか混乱しない #### ステップ5:フォームを接続する 新しいログイン機能をHTMLフォームに接続しましょう。フォームタグを次のように更新します: ```html
``` **この小さな変更がすること:** - フォームがデフォルトの「ページ全体を再読み込みする」動作を停止 - カスタムJavaScript関数を呼び出す - すべてをスムーズでシングルページアプリのように保つ - ユーザーが「ログイン」を押したときに何が起こるかを完全に制御 #### ステップ6:登録機能を強化する 一貫性のために、登録機能を更新してアカウントデータを保存し、ダッシュボードに移動するようにします: ```javascript // Add these lines at the end of your register function account = result; navigate('/dashboard'); ``` **この強化が提供するもの:** - **シームレスな**登録からダッシュボードへの移行 - **一貫した**ログインと登録フロー間のユーザー体験 - **即時の**登録成功後のアカウントデータへのアクセス #### 実装のテスト ```mermaid flowchart TD A[User enters credentials] --> B[Login function called] B --> C[Fetch account data from server] C --> D{Data received successfully?} D -->|Yes| E[Store account data globally] D -->|No| F[Display error message] E --> G[Navigate to dashboard] F --> H[User stays on login page] ``` **試してみる時間です:** 1. 新しいアカウントを作成してすべてが機能していることを確認 2. 同じ資格情報でログインしてみる 3. 何か問題がある場合はブラウザのコンソール(F12)を確認 4. 成功したログイン後にダッシュボードに到達することを確認 何かがうまくいかない場合でも、慌てないでください!ほとんどの問題はタイプミスやAPIサーバーの起動忘れなどの簡単な修正です。 #### クロスオリジンの仕組みについての簡単な説明 「異なるポートで動作しているAPIサーバーとウェブアプリがどのように通信しているの?」と思うかもしれません。良い質問です!これはすべてのウェブ開発者が最終的に直面する問題です。 > 🔒 **クロスオリジンセキュリティ**:ブラウザは「同一オリジンポリシー」を強制して、異なるドメイン間の不正な通信を防ぎます。ペンタゴンのチェックポイントシステムのように、データ転送を許可する前に通信が認可されていることを確認します。 > **私たちのセットアップでは:** - ウェブアプリは`localhost:3000`(開発サーバー)で動作 - APIサーバーは`localhost:5000`(バックエンドサーバー)で動作 - APIサーバーは[CORSヘッダー](https://developer.mozilla.org/docs/Web/HTTP/CORS)を含み、ウェブアプリからの通信を明示的に許可 この構成は、フロントエンドとバックエンドアプリケーションが通常別々のサーバーで動作する現実の開発を反映しています。 > 📚 **さらに学ぶ**:APIとデータ取得について詳しく学ぶには、この包括的な[Microsoft Learnモジュール](https://docs.microsoft.com/learn/modules/use-apis-discover-museum-art/?WT.mc_id=academic-77807-sagibbon)をご覧ください。 ## HTMLでデータを生き生きと表示する 次に、取得したデータをDOM操作を通じてユーザーに表示します。暗室で写真を現像するプロセスのように、見えないデータをユーザーが見て操作できるものにレンダリングします。 DOM操作は、静的なウェブページをユーザーの操作やサーバーの応答に基づいてコンテンツを更新する動的なアプリケーションに変える技術です。 ### 適切なツールを選ぶ JavaScriptでHTMLを更新する際には、いくつかの選択肢があります。これらはツールボックスの中の異なるツールのようなもので、それぞれ特定の仕事に最適です: | メソッド | 得意なこと | 使用するタイミング | 安全性 | |---------|-----------|------------------|-------| | `textContent` | ユーザーデータを安全に表示 | テキストを表示 より複雑な内容については、[`document.createElement()`](https://developer.mozilla.org/docs/Web/API/Document/createElement) と [`append()`](https://developer.mozilla.org/docs/Web/API/ParentNode/append) メソッドを組み合わせて使用します: ```javascript // Safe way to create new elements const transactionItem = document.createElement('div'); transactionItem.className = 'transaction-item'; transactionItem.textContent = `${transaction.date}: ${transaction.description}`; container.append(transactionItem); ``` **このアプローチの理解:** - **新しい** DOM 要素をプログラムで作成 - **要素の属性や内容を完全に**コントロール - **複雑でネストされた**要素構造を作成可能 - **セキュリティを維持**しながら構造と内容を分離 > ⚠️ **セキュリティに関する考慮事項**: [`innerHTML`](https://developer.mozilla.org/docs/Web/API/Element/innerHTML) は多くのチュートリアルで紹介されていますが、埋め込まれたスクリプトを実行する可能性があります。CERNのセキュリティプロトコルが不正なコードの実行を防ぐように、`textContent` や `createElement` を使用することでより安全な代替手段を提供します。 > **innerHTMLのリスク:** - ユーザーデータ内の `