# 銀行アプリを作る 第3部: データの取得と活用方法 『スタートレック』のエンタープライズ号のコンピュータを思い出してください。ピカード艦長が船の状況を尋ねると、インターフェース全体がシャットダウンして再構築されることなく、情報が瞬時に表示されます。このシームレスな情報の流れこそが、動的なデータ取得を通じてここで構築しようとしているものです。 現在のあなたの銀行アプリは、印刷された新聞のようなものです。情報はありますが静的です。これをNASAのミッションコントロールのように、データが絶えず流れ、リアルタイムで更新され、ユーザーの作業を中断させないものに変えていきます。 サーバーと非同期で通信し、異なるタイミングで到着するデータを処理し、生の情報をユーザーにとって意味のあるものに変換する方法を学びます。これがデモと本番用ソフトウェアの違いです。 ## ⚡ 次の5分間でできること **忙しい開発者向けのクイックスタート** ```mermaid flowchart LR A[⚡ 5 minutes] --> B[Set up API server] B --> C[Test fetch with curl] C --> D[Create login function] D --> E[See data in action] ``` - **1~2分目**: APIサーバーを起動 (`cd api && npm start`) して接続をテスト - **3分目**: fetchを使って基本的な`getAccount()`関数を作成 - **4分目**: ログインフォームを`action="javascript:login()"`で接続 - **5分目**: ログインをテストし、コンソールにアカウントデータが表示されるのを確認 **クイックテストコマンド**: ```bash # Verify API is running curl http://localhost:5000/api # Test account data fetch curl http://localhost:5000/api/accounts/test ``` **なぜ重要なのか**: 5分で、現代のウェブアプリケーションを支える非同期データ取得の魔法を体験できます。これがアプリを応答性が高く生き生きとしたものにする基盤です。 ## 🗺️ データ駆動型ウェブアプリケーション学習の旅 ```mermaid journey title From Static Pages to Dynamic Applications section Understanding the Evolution Traditional page reloads: 3: You Discover AJAX/SPA benefits: 5: You Master Fetch API patterns: 7: You section Building Authentication Create login functions: 4: You Handle async operations: 6: You Manage user sessions: 8: You section Dynamic UI Updates Learn DOM manipulation: 5: You Build transaction displays: 7: You Create responsive dashboards: 9: You section Professional Patterns Template-based rendering: 6: You Error handling strategies: 7: You Performance optimization: 8: You ``` **学習の目的地**: このレッスンの終わりまでに、現代のウェブアプリケーションがデータを動的に取得、処理、表示する方法を理解し、プロフェッショナルなアプリケーションに期待されるシームレスなユーザー体験を作り出す方法を学びます。 ## 講義前のクイズ [講義前のクイズ](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サーバーがアクティブで応答していることを確認 - アプリがサーバーに到達できることを検証(ミッション前の無線接続確認のようなものです) ## 🧠 データ管理エコシステムの概要 ```mermaid mindmap root((Data Management)) Authentication Flow Login Process Form Validation Credential Verification Session Management User State Global Account Object Navigation Guards Error Handling API Communication Fetch Patterns GET Requests POST Requests Error Responses Data Formats JSON Processing URL Encoding Response Parsing Dynamic UI Updates DOM Manipulation Safe Text Updates Element Creation Template Cloning User Experience Real-time Updates Error Messages Loading States Security Considerations XSS Prevention textContent Usage Input Sanitization Safe HTML Creation CORS Handling Cross-Origin Requests Header Configuration Development Setup ``` **コア原則**: 現代のウェブアプリケーションはデータオーケストレーションシステムです。ユーザーインターフェース、サーバー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) ```  **このアプローチがぎこちなく感じられた理由:** - クリックするたびにページ全体を再構築する必要があった - ユーザーは思考の途中で中断され、煩わしいページの点滅に悩まされた - インターネット接続がヘッダーやフッターを何度もダウンロードするために過剰に働いた - アプリはソフトウェアを使うというより、ファイルキャビネットをクリックしているように感じられた ### 現代のシングルページアプリケーション (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) ```  **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()`**を適用してURL内の特殊文字を安全に処理 - **レスポンスをJSON形式に変換**してデータ操作を簡単に - **エラーを優雅に処理**してクラッシュする代わりにエラーオブジェクトを返す > ⚠️ **セキュリティ注意点**: `encodeURIComponent()`関数はURL内の特殊文字を処理します。海軍通信で使用されるエンコードシステムのように、"#"や"&"などの文字が誤解されることなく、メッセージが正確に届くことを保証します。 > **重要性:** - 特殊文字がURLを壊すのを防ぐ - URL操作攻撃から保護 - サーバーが意図したデータを受け取ることを保証 - 安全なコーディングプラクティスに従う #### HTTP GETリクエストの理解 驚くかもしれませんが、`fetch`を追加オプションなしで使用すると、自動的に[`GET`](https://developer.mozilla.org/docs/Web/HTTP/Methods/GET)リクエストを作成します。これは私たちがしていることに最適です - サーバーに「このユーザーのアカウントデータを見せてもらえますか?」と尋ねることです。 GETリクエストは、図書館で本を借りるように既存のデータを要求するものです。POSTリクエスト(登録時に使用したもの)は、新しい本をコレクションに追加するようなものです。 | GETリクエスト | POSTリクエスト | |-------------|-------------| | **目的** | 既存データを取得 | 新しいデータをサーバーに送信 | | **パラメータ** | URLパス/クエリ文字列内 | リクエストボディ内 | | **キャッシュ** | ブラウザによるキャッシュ可能 | 通常キャッシュされない | | **セキュリティ** | URL/ログに表示される | リクエストボディ内で非表示 | ```mermaid sequenceDiagram participant B as Browser participant S as Server Note over B,S: GET Request (Data Retrieval) B->>S: GET /api/accounts/test S-->>B: 200 OK + Account Data Note over B,S: POST Request (Data Submission) B->>S: POST /api/accounts + New Account Data S-->>B: 201 Created + Confirmation Note over B,S: Error Handling B->>S: GET /api/accounts/nonexistent S-->>B: 404 Not Found + Error Message ``` #### ステップ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: 登録関数を強化する 一貫性のために、`register`関数を更新してアカウントデータを保存し、ダッシュボードに移動するようにします: ```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サーバーとウェブアプリがどのように通信しているの?」と思うかもしれません。良い質問です!これはすべてのウェブ開発者が最終的に直面する問題に触れています。 > 🔒 **クロスオリジンセキュリティ**: ブラウザは「同一オリジンポリシー」を強制して、異なるドメイン間の無許可の通信を防ぎます。ペンタゴンのチェックポイントシステムのように、通信が許可 DOM操作は、静的なウェブページをユーザーの操作やサーバーの応答に基づいてコンテンツを更新する動的なアプリケーションに変える技術です。 ### 適切なツールを選ぶ JavaScriptでHTMLを更新する際には、いくつかの選択肢があります。これらは工具箱の中の異なるツールのようなもので、それぞれ特定の作業に最適です。 | メソッド | 得意なこと | 使用するタイミング | 安全性 | |--------|---------------------|----------------|--------------| | `textContent` | ユーザーデータを安全に表示 | テキストを表示する際はいつでも | ✅ 非常に安全 | | `createElement()` + `append()` | 複雑なレイアウトの構築 | 新しいセクションやリストを作成する際 | ✅ 安全性抜群 | | `innerHTML` | HTMLコンテンツの設定 | ⚠️ できるだけ避けるべき | ❌ 危険性あり | #### 安全にテキストを表示する方法: textContent [`textContent`](https://developer.mozilla.org/docs/Web/API/Node/textContent)プロパティは、ユーザーデータを表示する際の頼れる味方です。ウェブページの「用心棒」のようなもので、有害なものは通しません。 ```javascript // The safe, reliable way to update text const balanceElement = document.getElementById('balance'); balanceElement.textContent = account.balance; ``` **textContentの利点:** - すべてをプレーンテキストとして扱う(スクリプトの実行を防ぐ) - 既存のコンテンツを自動的にクリア - シンプルなテキスト更新に効率的 - 悪意のあるコンテンツに対するセキュリティを提供 #### 動的なHTML要素の作成 より複雑なコンテンツには、[`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のリスク:** - ユーザーデータ内の`