<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Node.js - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ エキスパートの手によるプログラミングチュートリアル記事を幅広く掲載。ウェブ開発、データサイエンス、DevOps、セキュリティ、開発者としてのキャリアなどについて学びましょう。 ]]>
        </description>
        <link>https://www.freecodecamp.org/japanese/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Node.js - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/japanese/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Thu, 21 May 2026 15:48:24 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/japanese/news/tag/node-js/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ Node と NPM を最新バージョンに更新する方法 ]]>
                </title>
                <description>
                    <![CDATA[ Node は、JavaScript コードをブラウザの外、サーバーサイドで実行するためのランタイム環境です。 一方、NPM は JavaScript パッケージ (または Node モジュールとも呼ばれます) を npm レジストリ [https://www.npmjs.com] に公開するためのパッケージマネージャーです。また、アプリケーションにパッケージをインストールするためにも使われます。 Node をインストールするには、Node.js のウェブサイト [https://nodejs.org/en/] にアクセスしてインストーラーをダウンロードする必要があります。ダウンロードしたら、インストーラーを実行し、手順に従い、利用規約に同意してデバイスにインストーラーを入れます。 Node をインストールすると、アプリケーション内のパッケージを管理するために使用できる npm CLI も取得できます。 ただし、Node と NPM はそれぞれ個別に最新バージョンに更新できます。この記事では、その方法を説明します。 Node を更新する方法 1. NPM を使用して Node  ]]>
                </description>
                <link>https://www.freecodecamp.org/japanese/news/how-to-update-node-and-npm-to-the-latest-version/</link>
                <guid isPermaLink="false">664371b1bb553104041dc659</guid>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ npm ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Charlotte Stone ]]>
                </dc:creator>
                <pubDate>Mon, 27 May 2024 14:09:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/japanese/news/content/images/2024/05/pexels-markus-winkler-4052195.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文:</strong> <a href="https://www.freecodecamp.org/news/how-to-update-node-and-npm-to-the-latest-version/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Update Node and NPM to the Latest Version</a>
      </p><p>Node は、JavaScript コードをブラウザの外、サーバーサイドで実行するためのランタイム環境です。</p><p>一方、NPM は JavaScript パッケージ (または Node モジュールとも呼ばれます) を <a href="https://www.npmjs.com">npm レジストリ</a>に公開するためのパッケージマネージャーです。また、アプリケーションにパッケージをインストールするためにも使われます。</p><p>Node をインストールするには、<a href="https://nodejs.org/en/">Node.js のウェブサイト</a>にアクセスしてインストーラーをダウンロードする必要があります。ダウンロードしたら、インストーラーを実行し、手順に従い、利用規約に同意してデバイスにインストーラーを入れます。</p><p>Node をインストールすると、アプリケーション内のパッケージを管理するために使用できる <code>npm</code> CLI も取得できます。</p><p>ただし、Node と NPM はそれぞれ個別に最新バージョンに更新できます。この記事では、その方法を説明します。</p><h2 id="node-">Node を更新する方法</h2><h3 id="1-npm-node-">1. NPM を使用して Node のバージョンを更新する場合</h3><p>NPM を使用して Node を更新するには、デバイス上で Node のバージョンを対話形式で管理するための <a href="https://www.npmjs.com/package/n">n</a> パッケージをインストールします。</p><p>以下がその手順です:</p><h4 id="npm-">NPM のキャッシュをクリアする</h4><p>依存関係をインストールすると、次回のダウンロードの速度を向上させるため、いくつかのモジュールがキャッシュされます。そのため、まず NPM のキャッシュをクリアする必要があります。</p><h4 id="n-">n パッケージをインストールする</h4><pre><code>npm install -g n</code></pre><p>このパッケージをグローバルにインストールする必要があります。これは、ルートで Node のバージョンを管理します。</p><h4 id="-node-">新しいバージョンの Node をインストールする</h4><pre><code>n lts
n latest</code></pre><p>上記の二つのコマンドは、Node の長期サポート版と最新版をインストールします。</p><h4 id="-">以前にインストールされたバージョンを削除する</h4><pre><code>n prune</code></pre><p>このコマンドは、以前にインストールされたバージョンのキャッシュされたバージョンを削除し、最後にインストールされたバージョンのみを保持します。</p><h3 id="2-nvm-node-">2. NVM を使用して Node のバージョンを更新する場合</h3><p>NVM は Node Version Manager の略であり、その名前が示すように、Node のバージョンを管理するのに役立ちます。NVM を使用すると、Node のバージョンを複数インストールし、プロジェクトが使用する Node のバージョンを指定することができます。</p><p>NVM を使用すると、さまざまな Node のバージョンでプロジェクトをテストすることが簡単になります。</p><p>NVM を使用して Node のバージョンを更新するには、まず NVM をインストールする必要があります。</p><p>NVM の<a href="https://github.com/nvm-sh/nvm#installing-and-updating">インストールガイドはこちら</a>です。</p><p>インストール後、次のコマンドでパッケージをインストールできます:</p><pre><code>nvm install [version]</code></pre><p>最新バージョンをインストールするには、次のコマンドを使用します:</p><pre><code>nvm install node</code></pre><p>他のバージョンをアンインストールするには、次のコマンドを使用します:</p><pre><code>nvm uninstall [version]</code></pre><p>多くのバージョンがインストールされている場合、特定の時点で使用するバージョンを指定することもあります。これを行う方法の一つは、次のようにデフォルトのエイリアスを設定することです:</p><pre><code>nvm alias default [version]</code></pre><p>このようにすると、Node の実行は指定されたバージョンで実行されます。</p><h3 id="3-node-">3. 更新された Node のバイナリをダウンロードする場合</h3><p>また、<a href="https://nodejs.org/en/">Node.js</a> のウェブサイトから最新バージョンを入手することもできます。そこでは、デバイスに最適な最新バージョンと長期サポートバージョンを見つけることができます。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/05/image-7.png" class="kg-image" alt="image-7" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2024/05/image-7.png 600w, https://www.freecodecamp.org/japanese/news/content/images/2024/05/image-7.png 1000w" sizes="(min-width: 720px) 720px" width="1000" height="662" loading="lazy"><figcaption>Node.js のダウンロードページ</figcaption></figure><p>最新バージョンをダウンロードすると、NPM の最新バージョンも入手できます。</p><h2 id="npm--1">NPM の更新方法</h2><p>パッケージを更新するために NPM を使用するのと同様に、NPM で NPM 自体を更新することもできます。そのコマンドは以下の通りです:</p><pre><code>npm install -g npm@latest</code></pre><p>このコマンドは、最新バージョンの NPM をグローバルにインストールします。</p><p>Mac では、NPM の前に <code>sudo</code> コマンドを渡す必要があるかもしれません。これは、NPM をデバイスのルートにインストールするために特権が必要なためです。</p><h2 id="--1">結論</h2><p>この記事では、Node と NPM を最新バージョンに更新する方法を見てきました。</p><p>繰り返しますが、Node をインストールすると自動的に NPM もインストールされます。また、ウェブサイトからバイナリをインストールして Node を更新すると、更新された NPM が取得できます。</p><p>また、デバイス全体で Node と NPM を更新する他の方法も学びました。</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ React、Node、Socket.io、HarperDB を使用してリアルタイムのチャットアプリケーションを作成する方法 ]]>
                </title>
                <description>
                    <![CDATA[ この記事では、Socket.io と HarperDB を使用して、チャットルームを備えたフルスタックのリアルタイム・チャットアプリケーションを作成します。 このプロジェクトを通して、フルスタックアプリケーションの作り方や、バックエンドがフロントエンドとリアルタイムで通信できるウェブアプリの作り方を学ぶことができます。 通常、HTTP リクエストを使用する場合、サーバーはリアルタイムでデータをクライアントにプッシュすることはできません。しかし、Socket.io を使用することで、サーバーはサーバー上で発生したいくつかのイベントに関するリアルタイム情報をクライアントにプッシュすることができるようになります。 これから作成するウェブアプリには 2 つのページがあります。 チャットルームに参加するページ。 そしてチャットルームのページ。 このウェブアプリ作成に使用するものは次のとおりです。  * フロントエンド: React [https://reactjs.org/docs/create-a-new-react-app.html]     (インタラクティブなアプリケーショ ]]>
                </description>
                <link>https://www.freecodecamp.org/japanese/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/</link>
                <guid isPermaLink="false">65b3ea0f6e329503f6305828</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Socket.io ]]>
                    </category>
                
                    <category>
                        <![CDATA[ HarperDB ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Saki Basken ]]>
                </dc:creator>
                <pubDate>Wed, 28 Feb 2024 09:31:42 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/japanese/news/content/images/2024/01/pexels-keira-burton-6146929.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文:</strong> <a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Build a Real-time Chat App with React, Node, Socket.io, and HarperDB</a>
      </p><p>この記事では、Socket.io と HarperDB を使用して、チャットルームを備えたフルスタックのリアルタイム・チャットアプリケーションを作成します。</p><p>このプロジェクトを通して、フルスタックアプリケーションの作り方や、バックエンドがフロントエンドとリアルタイムで通信できるウェブアプリの作り方を学ぶことができます。</p><p>通常、HTTP リクエストを使用する場合、サーバーはリアルタイムでデータをクライアントにプッシュすることはできません。しかし、Socket.io を使用することで、サーバーはサーバー上で発生したいくつかのイベントに関するリアルタイム情報をクライアントにプッシュすることができるようになります。</p><p>これから作成するウェブアプリには 2 つのページがあります。</p><p>チャットルームに参加するページ。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/home-page.JPG" class="kg-image" alt="home-page" width="475" height="468" loading="lazy"></figure><p>そしてチャットルームのページ。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/chat-page.JPG" class="kg-image" alt="chat-page" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2024/02/chat-page.JPG 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2024/02/chat-page.JPG 1000w, https://www.freecodecamp.org/japanese/news/content/images/2024/02/chat-page.JPG 1216w" sizes="(min-width: 720px) 720px" width="1216" height="678" loading="lazy"></figure><p>このウェブアプリ作成に使用するものは次のとおりです。</p><ul><li><strong>フロントエンド</strong>: <a href="https://reactjs.org/docs/create-a-new-react-app.html">React</a> (インタラクティブなアプリケーションを構築するためのフロントエンド JavaScript のフレームワークです)</li><li><strong>バックエンド</strong>: <a href="https://nodejs.org/en/">Node</a> と <a href="https://expressjs.com/">Express</a> (Express は、API とバックエンドを簡単に作成できる非常に人気のある NodeJS のフレームワークです)</li><li><strong>データベース</strong>: <a href="https://harperdb.io/">HarperDB</a> (SQL または NoSQL を使用してデータをクエリできるデータ + アプリケーションのプラットフォーム。HarperDB には API も組み込まれているため、大量にバックエンドのコードを書く必要がなくなります)</li><li><strong>リアルタイム通信</strong>: <a href="https://socket.io/docs/v3/">Socket.io</a> (以下を参照してください！)</li></ul><p> <a href="https://github.com/DoableDanny/Realtime-chat-app-with-rooms">ソースコードはここをご覧ください</a>。(星を付けるのをお忘れなく⭐)</p><h2 id="-">目次</h2><ol><li><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#what-is-socket-io">Socket.io</a> とは</li><li><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#project-setup">プロジェクトのセットアップ</a></li><li><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#how-to-build-the-join-a-room-page">「チャットルームに参加する」ページ作成方法</a></li><li><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#how-to-set-up-the-server">サーバーの設定方法</a></li><li><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#how-to-create-our-first-socket-io-event-listener-on-the-server">サーバー上で最初の Socket.io イベントリスナーを作成する方法</a></li><li><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#how-rooms-work-in-socket-io">Socket.io のルームの仕組み</a></li><li><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#how-to-build-the-chat-page">チャットページを作成する方法</a></li><li><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#how-to-create-the-messages-component-b-">メッセージコンポーネント (B) を作成する方法</a></li><li><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#how-to-create-a-schema-and-table-in-harperdb">HarperDB でスキーマとテーブルを作成する方法</a></li><li><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#how-to-create-the-send-message-component-c-">メッセージ送信コンポーネント (C) の作成方法</a></li><li><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#how-to-set-up-harperdb-environment-variables">HarperDB の環境変数を設定する方法</a></li><li><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#how-to-allow-users-to-send-messages-to-each-other-with-socket-io">Socket.io を利用してユーザーが相互にメッセージを送信できるようにする方法</a></li><li><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#how-to-get-messages-from-harperdb">HarperDB からメッセージを取得する方法</a></li><li><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#how-to-display-the-last-100-messages-on-the-client">クライアントで最新メッセージ 100 件を表示する方法</a></li><li><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#how-to-build-the-chat-page">チャット</a><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#how-to-display-the-room-and-users-a-">ルームとユーザー (A) の表示方法</a></li><li><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#how-to-remove-a-user-from-a-socket-io-room">Socket.io のルームからユーザーを削除する方法</a></li><li><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#how-to-add-the-socket-io-disconnect-event-listener">Socket.io 接続を切断するイベントリスナーを追加する方法</a></li></ol><h2 id="socket-io-">Socket.io とは</h2><p>Socket.IO を使用することで、サーバー上でイベントが発生したときに、サーバーはリアルタイムで情報をクライアントにプッシュすることができます。</p><p>たとえば、マルチプレイゲームをしているとします。「友達」があなたに対してものすごいゴールを決めてしまうというイベントが発生するかもしれません。</p><p>Socket.IO を使用することで、失点について (ほぼ) 瞬時に知ることができるのです。</p><p>Socket.IO を使用しない場合では、クライアントはサーバーでイベントが発生したかどうかを確認するために複数のポーリング AJAX 呼び出しを行う必要があります。例えば、クライアントは JavaScript を使用して、サーバー上のイベントを 5 秒ごとに確認することも可能でしょう。</p><p>Socket.IO を使用することで、クライアントはサーバー上でイベントが発生したかどうかを確認するために複数のポーリング AJAX 呼び出しを行う必要がなくなります。代わりに、サーバーは情報を取得したら直ちにそれをクライアントに送信します。はるかに効率的ですね。👌</p><p>そのため、Socket.IO を使用すると、チャットアプリやマルチプレイヤーゲームなどのリアルタイムアプリケーションを簡単に構築することができます。</p><h2 id="--1">プロジェクトのセットアップ</h2><h3 id="1-">1. フォルダーの設定方法</h3><p>お好みのテキストエディター (私の場合は VS Code) で新しいプロジェクトを立ち上げ、ルートフォルダーにクライアント (client) とサーバー (server) という 2 つのフォルダーを作成します。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/folder-structure.jpeg" class="kg-image" alt="folder-structure" width="371" height="106" loading="lazy"></figure><p>フロントエンド React アプリケーションをクライアントフォルダーに、Node/Express バックエンドをサーバーフォルダーに作成していきます。</p><h3 id="2-">2. クライアントの依存関係をインストールする方法</h3><p>プロジェクトのルートディレクトリでターミナルを開きます。(VS Code では、Ctrl+' を押すか、ターミナル -&gt; 新しいターミナルを選択してください。)</p><p>次に、React をクライアントディレクトリにインストールします。</p><pre><code class="language-bash">$ npx create-react-app client</code></pre><p>React がインストールされたら、ディレクトリをクライアントフォルダーに変更し、次の依存関係をインストールします。</p><pre><code class="language-bash">$ cd client
$ npm i react-router-dom socket.io-client</code></pre><p>React-router-dom を使用することで、さまざまな React コンポーネントへのルートを設定でき、それによって色々なページを作成することができます。</p><p>Socket.io-client は、socket.io のクライアント版であり、イベントをサーバーに「送信」できます。サーバーが受信すると、socket.io のサーバー版を使用して、送信者と同じルーム内のユーザーにメッセージを送信したり、ユーザーをソケットルームに参加させたりすることができます。</p><p>後で実際にコードを使って実践してみると、それが何を意味するのか、もっとわかるかと思います。</p><h3 id="3-react-">3. React アプリの起動方法</h3><p>クライアントディレクトリから次のコマンドを実行して、すべてが順調に稼働していることを確認してみましょう。</p><pre><code class="language-bash">$ npm start</code></pre><p>Webpack は React アプリをビルドし、<a href="http://localhost:3000/">http://localhost:3000</a> で起動します。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/react-is-running.jpeg" class="kg-image" alt="react-is-running" width="533" height="400" loading="lazy"></figure><p>次に、ユーザーが送信したメッセージを永続的に保存するために使用する HarperDB データベースを設定しましょう。</p><h3 id="harperdb-">HarperDB のセットアップ方法</h3><p>まず、<a href="https://studio.harperdb.io/">HarperDB でアカウントを作成</a>します。</p><p>次に、新しい HarperDB クラウドインスタンスを作成します。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/harper_instance.jpeg" class="kg-image" alt="harper_instance" width="434" height="346" loading="lazy"></figure><p>クラウドインスタンスを選択すると、設定が楽になるのでおすすめです。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/instance-type.jpeg" class="kg-image" alt="instance-type" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2024/02/instance-type.jpeg 600w, https://www.freecodecamp.org/japanese/news/content/images/2024/02/instance-type.jpeg 819w" sizes="(min-width: 720px) 720px" width="819" height="398" loading="lazy"></figure><p>クラウドプロバイダーを選択します (私は AWS を選択しました)。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/cloud_provider.jpeg" class="kg-image" alt="cloud_provider" width="491" height="424" loading="lazy"></figure><p>クラウドインスタンスに名前を付け、インスタンスの認証情報を作成します。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/instance_credentials.jpeg" class="kg-image" alt="instance_credentials" width="490" height="444" loading="lazy"></figure><p>HarperDB には、有難いことに充分な無料枠があるため、このプロジェクトにはそれを選択します。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/instance_specs.jpeg" class="kg-image" alt="instance_specs" width="486" height="661" loading="lazy"></figure><p>全ての詳細が正しいことを確認してから、インスタンスを作成します。</p><p>インスタンスの立ち上げには数分かかるので、早速最初の React コンポーネントを作成してみましょう！</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/instance_loading.jpeg" class="kg-image" alt="instance_loading" width="324" height="208" loading="lazy"></figure><h2 id="--2">「チャットルームに参加する」ページ作成方法</h2><p>私たちのホームページは最終的には次のようになる予定です。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/home-page-1.JPG" class="kg-image" alt="home-page-1" width="475" height="468" loading="lazy"></figure><p>ユーザーはユーザー名を入力し、ドロップダウンからチャットルームを選択して、Join Room (ルームに参加) をクリックします。すると、ユーザーはチャットルームのページへ移動します。</p><p>ということで、早速このホームページを作ってみましょう。</p><h3 id="1-html-">1. HTML フォームの作成とスタイルの追加方法</h3><p>新しいファイル、src/pages/home/index.js を作成します。</p><p>CSS モジュールを使用してアプリに基本的なスタイルを追加するため、新しいファイル src/pages/home/styles.module.css を作成します。</p><p>ディレクトリの構成は次のようになります。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/pages-folder-structure.jpeg" class="kg-image" alt="pages-folder-structure" width="218" height="142" loading="lazy"></figure><p>次に、基本的な HTML のフォームを作成しましょう。</p><pre><code class="language-jsx">// client/src/pages/home/index.js

import styles from './styles.module.css';

const Home = () =&gt; {
  return (
    &lt;div className={styles.container}&gt;
      &lt;div className={styles.formContainer}&gt;
        &lt;h1&gt;{`&lt;&gt;DevRooms&lt;/&gt;`}&lt;/h1&gt;
        &lt;input className={styles.input} placeholder='Username...' /&gt;

        &lt;select className={styles.input}&gt;
          &lt;option&gt;-- Select Room --&lt;/option&gt;
          &lt;option value='javascript'&gt;JavaScript&lt;/option&gt;
          &lt;option value='node'&gt;Node&lt;/option&gt;
          &lt;option value='express'&gt;Express&lt;/option&gt;
          &lt;option value='react'&gt;React&lt;/option&gt;
        &lt;/select&gt;

        &lt;button className='btn btn-secondary'&gt;Join Room&lt;/button&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
};

export default Home;</code></pre><p>上では、ユーザー名を取得するための単純なテキスト入力と、ユーザーが参加するチャットルームを選択するためのいくつかのデフォルトオプションを含む select ドロップダウンがあります。</p><p>このコンポーネントを App.js にインポートし、react-router-dom パッケージを使用してコンポーネントのルートを設定します。これはホームページとなるため、パスは単に「/」になります。</p><pre><code class="language-jsx">// client/src/App.js

import './App.css';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Home from './pages/home';

function App() {
  return (
    &lt;Router&gt;
      &lt;div className='App'&gt;
        &lt;Routes&gt;
          &lt;Route path='/' element={&lt;Home /&gt;} /&gt;
        &lt;/Routes&gt;
      &lt;/div&gt;
    &lt;/Router&gt;
  );
}

export default App;</code></pre><p>次に、アプリの見栄えを良くするためにいくつか基本のスタイルを追加していきます。</p><pre><code class="language-css">/* client/src/App.css */

html * {
  font-family: Arial;
  box-sizing: border-box;
}
body {
  margin: 0;
  padding: 0;
  overflow: hidden;
  background: rgb(63, 73, 204);
}
::-webkit-scrollbar {
  width: 20px;
}
::-webkit-scrollbar-track {
  background-color: transparent;
}
::-webkit-scrollbar-thumb {
  background-color: #d6dee1;
  border-radius: 20px;
  border: 6px solid transparent;
  background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover {
  background-color: #a8bbbf;
}
.btn {
  padding: 14px 14px;
  border-radius: 6px;
  font-weight: bold;
  font-size: 1.1rem;
  cursor: pointer;
  border: none;
}
.btn-outline {
  color: rgb(153, 217, 234);
  border: 1px solid rgb(153, 217, 234);
  background: rgb(63, 73, 204);
}
.btn-primary {
  background: rgb(153, 217, 234);
  color: rgb(0, 24, 111);
}
.btn-secondary {
  background: rgb(0, 24, 111);
  color: #fff;
}</code></pre><p>ホームページコンポーネントに固有のスタイルも追加していきます。</p><pre><code class="language-css">/* client/src/pages/home/styles.module.css */

.container {
  height: 100vh;
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  background: rgb(63, 73, 204);
}
.formContainer {
  width: 400px;
  margin: 0 auto 0 auto;
  padding: 32px;
  background: lightblue;
  border-radius: 6px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 28px;
}
.input {
  width: 100%;
  padding: 12px;
  border-radius: 6px;
  border: 1px solid rgb(63, 73, 204);
  font-size: 0.9rem;
}
.input option {
  margin-top: 20px;
}
</code></pre><p>また、スタイル属性を追加して、「ルームに参加」ボタンを全幅にしてみましょう。</p><pre><code class="language-jsx">// client/src/pages/home/index.js

&lt;button className='btn btn-secondary' style={{ width: '100%' }}&gt;Join Room&lt;/button&gt;</code></pre><p>なかなか良くなってきましたね。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/home-page-html.jpeg" class="kg-image" alt="home-page-html" width="468" height="433" loading="lazy"></figure><h3 id="2--1">2. ルーム参加フォームに機能を追加する方法</h3><p>基本的なフォームとスタイルができたので、次に機能を追加して行きたいと思います。</p><p>ユーザーが「ルームに参加」ボタンをクリックしたときに起きて欲しい事項を、以下にまとめました。</p><ol><li>ユーザー名と部屋のフィールドが入力されていることを確認する。</li><li>確認できたら、ソケットイベントをサーバーに送信する。</li><li>ユーザーをチャットページ (後で作成予定) にリダイレクトする。</li></ol><p><em><em>username</em></em> と <em><em>room</em></em> の値を保存する state の作成が必要です。また、ソケットインスタンスの作成も必要です。</p><p>これらの state をホームコンポーネント内で直接作成することもできますが、チャットページもユーザー名、ルーム、ソケットにアクセスする必要があります。そこで、state を App.js に引き上げ、そこでこれらの変数をホームページコンポーネントとチャットページコンポーネントの両方に渡します。</p><p>なのでまず、App.js 内で state の作成とソケットの設定をし、これらの変数を props として &lt;Home /&gt; コンポーネントに渡しましょう。&lt;Home /&gt; から state の値を更新できるように、state の set 関数も渡します。</p><pre><code class="language-jsx">// client/src/App.js

import './App.css';
import { useState } from 'react'; // Add this
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import io from 'socket.io-client'; // Add this
import Home from './pages/home';

const socket = io.connect('http://localhost:4000'); // Add this -- our server will run on port 4000, so we connect to it from here

function App() {
  const [username, setUsername] = useState(''); // Add this
  const [room, setRoom] = useState(''); // Add this

  return (
    &lt;Router&gt;
      &lt;div className='App'&gt;
        &lt;Routes&gt;
          &lt;Route
            path='/'
            element={
              &lt;Home
                username={username} // Add this
                setUsername={setUsername} // Add this
                room={room} // Add this
                setRoom={setRoom} // Add this
                socket={socket} // Add this
              /&gt;
            }
          /&gt;
        &lt;/Routes&gt;
      &lt;/div&gt;
    &lt;/Router&gt;
  );
}

export default App;
</code></pre><p>これで、Home コンポーネント内でもこれらの props にアクセスできるようになりました。以下のように、分割代入 (デストラクチャリング) を使って、props にアクセスします。</p><pre><code class="language-jsx">// client/src/pages/home/index.js

import styles from './style.module.css';

const Home = ({ username, setUsername, room, setRoom, socket }) =&gt; {
  return (
    // ...
  );
};

export default Home;</code></pre><p>ユーザーがユーザー名を入力するか、チャットルームを選択したとき、ユーザー名とルームの state を更新する必要があります。</p><pre><code class="language-jsx">// client/src/pages/home/index.js

// ...

const Home = ({ username, setUsername, room, setRoom, socket }) =&gt; {
  return (
    &lt;div className={styles.container}&gt;
      // ...
        &lt;input
          className={styles.input}
          placeholder='Username...'
          onChange={(e) =&gt; setUsername(e.target.value)} // Add this
        /&gt;

        &lt;select
          className={styles.input}
          onChange={(e) =&gt; setRoom(e.target.value)} // Add this
        &gt;
         // ...
        &lt;/select&gt;

        // ...
    &lt;/div&gt;
  );
};

export default Home;
</code></pre><p>ユーザーが入力したデータを取得しているので、ユーザーが「ルームに参加」ボタンをクリックしたときのコールバック関数、<em>joinRoom()</em> を作成できます。</p><pre><code class="language-jsx">// client/src/pages/home/index.js

// ...

const Home = ({ username, setUsername, room, setRoom, socket }) =&gt; {
  
  // Add this
  const joinRoom = () =&gt; {
    if (room !== '' &amp;&amp; username !== '') {
      socket.emit('join_room', { username, room });
    }
  };

  return (
    &lt;div className={styles.container}&gt;
      // ...
      
        &lt;button
          className='btn btn-secondary'
          style={{ width: '100%' }}
          onClick={joinRoom} // Add this
        &gt;
          Join Room
        &lt;/button&gt;
      // ...
    &lt;/div&gt;
  );
};

export default Home;
</code></pre><p>上では、ユーザーがボタンをクリックすると、ユーザーのユーザーネームと選択したルームを含むオブジェクトとともに、join_room というソケットイベントが発行されます。このイベントはその後サーバーに聞き取りされます。</p><p>ホームページコンポーネントを完成するには、ユーザーを /chat ページに誘導するリダイレクトロジックを joinRoom() 関数の最後に追加する必要があります。</p><pre><code class="language-jsx">// client/src/pages/home/index.js

// ...
import { useNavigate } from 'react-router-dom'; // Add this

const Home = ({ username, setUsername, room, setRoom, socket }) =&gt; {
  const navigate = useNavigate(); // Add this

  const joinRoom = () =&gt; {
    if (room !== '' &amp;&amp; username !== '') {
      socket.emit('join_room', { username, room });
    }

    // Redirect to /chat
    navigate('/chat', { replace: true }); // Add this
  };

 // ...
</code></pre><p>テストしてみましょう: ユーザー名を入力してルームを選択し、[ルームに参加] をクリックします。現在は空のページですが、<a href="http://localhost:3000/chat">http://localhost:3000/chat</a> というルートに転送されるはずです。</p><p>フロントエンドのチャットページを作成する前に、まずは少しサーバーの立ち上げをしてみましょう。</p><h2 id="--3">サーバーの設定方法</h2><p>サーバー側は、イベントリスナーを使ってフロントエンドで発生したソケットイベントの通知を待ちます。現在、React に発生しているイベントは join_room のみなので、まずはこのイベントリスナーを追加します。</p><p>ただその前に、サーバーの依存関係をインストールし、サーバーを起動して実行する必要があります。</p><h3 id="1--1"><strong>1. </strong>サーバーの依存関係をインストールする方法</h3><p>新しいターミナルを開き (VS コード: [ターミナル] -&gt; [新しいターミナル])、​​ディレクトリをサーバーフォルダーに変更し、package.json ファイルを初期化し、次の依存関係をインストールします。</p><pre><code class="language-bash">$ cd server
$ npm init -y
$ npm i axios cors express socket.io dotenv</code></pre><ul><li>Axios は、API へのリクエストを簡易化するために一般的に使用されているパッケージです。</li><li>Cors を使用すると、クライアントは他のオリジンにもリクエストを送ることができます。これは socket.io が適切に作動するために不可欠です。CORS についてまだ聞いたことがない方は、ぜひ「<a href="https://medium.com/@electra_chong/what-is-cors-what-is-it-used-for-308cafa4df1a">CORS とは</a>」を参照してみてください。</li><li>Express は、少ないコードでより簡単にバックエンドを作成できる NodeJS のフレームワークです。</li><li>Socket.io は、標準の HTTP リクエストでは実現不可能な、クライアントとサーバーのリアルタイム通信を可能にするライブラリです。</li><li>Dotenv は、秘密キーとパスワードを安全に保管し、必要に応じてコードに読み込むことができるようにするモジュールです。</li></ul><p>また、nodemon を開発依存関係としてインストールすることで、コードを変更するたびにサーバーを再起動する必要がなく、時間と労力を節約することができます。</p><pre><code class="language-bash">$ npm i -D nodemon</code></pre><h3 id="2--2"><strong>2. </strong>サーバーを起動する方法</h3><p>サーバーディレクトリのルートに index.js というフォルダーを作成し、次のコードを追加してサーバーを起動して実行します。</p><pre><code class="language-javascript">// server/index.js

const express = require('express');
const app = express();
const http = require('http');
const cors = require('cors');

app.use(cors()); // Add cors middleware

const server = http.createServer(app);

server.listen(4000, () =&gt; 'Server is running on port 4000');</code></pre><p>サーバー上の package.json ファイルを開き、開発で nodemon を使用できるようにするスクリプトを追加します。</p><pre><code class="language-json">{
  ...
  "scripts": {
    "dev": "nodemon index.js"
  },
  ...
}
</code></pre><p>次に、下記のコマンドでサーバーを起動しましょう。</p><pre><code class="language-bash">$ npm run dev</code></pre><p>GET リクエストハンドラーを追加することで、サーバーが正しく実行されていることをすぐに確認することができます。</p><pre><code class="language-javascript">// server/index.js

const express = require('express');
const app = express();
http = require('http');
const cors = require('cors');

app.use(cors()); // Add cors middleware

const server = http.createServer(app);

// Add this
app.get('/', (req, res) =&gt; {
  res.send('Hello world');
});

server.listen(4000, () =&gt; 'Server is running on port 3000');</code></pre><p><a href="http://localhost:4000/">http://localhost:4000/</a> に移動します。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/localhost4000.jpeg" class="kg-image" alt="localhost4000" width="322" height="186" loading="lazy"></figure><p>サーバーは問題なく稼働しています。今度は、サーバー側の Socket.io の作業を行います。</p><h2 id="-socket-io-">サーバー上で最初の Socket.io イベントリスナーを作成する方法</h2><p>クライアント側から、join_room イベントを送りましたね。これからそのイベント情報をサーバー側で聞き取り、ユーザーをソケットルームに追加していきます。</p><p>しかしまず、クライアントが Socket.io のクライアント経由でサーバーに接続したかを確認する必要があります。</p><pre><code class="language-javascript">// server/index.js

const express = require('express');
const app = express();
http = require('http');
const cors = require('cors');
const { Server } = require('socket.io'); // Add this

app.use(cors()); // Add cors middleware

const server = http.createServer(app); // Add this

// Add this
// Create an io server and allow for CORS from http://localhost:3000 with GET and POST methods
const io = new Server(server, {
  cors: {
    origin: 'http://localhost:3000',
    methods: ['GET', 'POST'],
  },
});

// Add this
// Listen for when the client connects via socket.io-client
io.on('connection', (socket) =&gt; {
  console.log(`User connected ${socket.id}`);

  // We can write our socket event listeners in here...
});

server.listen(4000, () =&gt; 'Server is running on port 3000');</code></pre><p>これで、クライアントがフロントエンドから接続すると、バックエンドが接続イベントをキャプチャし、その特定のクライアントの一意のソケット ID を使用して <code>User connected</code> とログに記録します。</p><p>一度、サーバーがクライアントからの接続イベントをキャプチャしているかどうかをテストしてみましょう。<a href="http://localhost:3000/">http://localhost:3000/</a> にある React アプリに移動し、ページを更新してみてください。</p><p>サーバー端末コンソールに次のログが表示されているはずです。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/user-connected.jpeg" class="kg-image" alt="user-connected" width="442" height="93" loading="lazy"></figure><p>見事、クライアントが socket.io 経由でサーバーに接続しました。これでクライアントとサーバーがリアルタイムで通信できるようになりました。</p><h2 id="socket-io--1">Socket.io のルームの仕組み</h2><p><a href="https://socket.io/docs/v3/rooms/">Socket.io ドキュメント</a>より:</p><blockquote>「ルームとは、ソケットが出入り (<code>join</code> と <code>leave</code>) できる任意のチャネルです。クライアントのサブセットにイベントをブロードキャストするために使用できます。」</blockquote><p>したがって、ユーザーをルームに参加させると、サーバーはそのルーム内のすべてのユーザーにメッセージを送信できるため、ユーザーはリアルタイムで相互にメッセージを送信できるようになるのです。素晴らしい！</p><h3 id="-socket-io--1">ユーザーを Socket.io ルームに参加させる方法</h3><p>ユーザーが Socket.io を介して接続したら、クライアントから通知されたイベントを聞き取るために、サーバー上でソケットイベントリスナーを追加することができます。また、サーバー上でイベント情報を発行し、クライアント上で聞き取ることもできます。</p><p>早速 join_room イベントの発生を待ち、データ (ユーザー名とルーム) をキャプチャして、ユーザーをソケットルームに追加しましょう。</p><pre><code class="language-javascript">// server/index.js

// Listen for when the client connects via socket.io-client
io.on('connection', (socket) =&gt; {
  console.log(`User connected ${socket.id}`);

  // Add this
  // Add a user to a room
  socket.on('join_room', (data) =&gt; {
    const { username, room } = data; // Data sent from client when join_room event emitted
    socket.join(room); // Join the user to a socket room
  });
});</code></pre><h3 id="--4">ルーム内のユーザーにメッセージを送信する方法</h3><p>まずは、参加したばかりのユーザー以外のルーム内すべてのユーザーにメッセージを送信して、新しいユーザーが参加したことを通知しましょう。</p><pre><code class="language-javascript">// server/index.js

const CHAT_BOT = 'ChatBot'; // Add this
// Listen for when the client connects via socket.io-client
io.on('connection', (socket) =&gt; {
  console.log(`User connected ${socket.id}`);

  // Add a user to a room
  socket.on('join_room', (data) =&gt; {
    const { username, room } = data; // Data sent from client when join_room event emitted
    socket.join(room); // Join the user to a socket room

    // Add this
    let __createdtime__ = Date.now(); // Current timestamp
    // Send message to all users currently in the room, apart from the user that just joined
    socket.to(room).emit('receive_message', {
      message: `${username} has joined the chat room`,
      username: CHAT_BOT,
      __createdtime__,
    });
  });
});</code></pre><p>上記では、現在のユーザーが参加したばかりのルーム内にいる全クライアントに、receive_message イベントを発行しています。そしてそのイベントには、メッセージ本文、メッセージを送信したユーザー名、メッセージの送信時刻などのデータが含まれています。</p><p>少し後で React アプリケーションにイベントリスナーを追加して、このイベントをキャプチャし、画面にメッセージを出力します。</p><p>新しく参加したユーザーにも、ウェルカムメッセージを送信しましょう。</p><pre><code class="language-javascript">// server/index.js

io.on('connection', (socket) =&gt; {
  // ...

    // Add this
    // Send welcome msg to user that just joined chat only
    socket.emit('receive_message', {
      message: `Welcome ${username}`,
      username: CHAT_BOT,
      __createdtime__,
    });
  });
});</code></pre><p>ユーザーを Socket.io のルームに追加すると、Socket.io は各ユーザーのソケット ID のみを保存します。しかし今後、ルーム名だけでなく、ルームにいる全員のユーザー名も必要になってきます。そこで、これらのデータをサーバー上の変数に保存しておきましょう。</p><pre><code class="language-javascript">// server/index.js

// ...

const CHAT_BOT = 'ChatBot';
// Add this
let chatRoom = ''; // E.g. javascript, node,...
let allUsers = []; // All users in current chat room

// Listen for when the client connects via socket.io-client
io.on('connection', (socket) =&gt; {
    // ...
    
    // Add this
    // Save the new user to the room
    chatRoom = room;
    allUsers.push({ id: socket.id, username, room });
    chatRoomUsers = allUsers.filter((user) =&gt; user.room === room);
    socket.to(room).emit('chatroom_users', chatRoomUsers);
    socket.emit('chatroom_users', chatRoomUsers);
  });
});</code></pre><p>上記では、chatroom_users イベントを介して全ユーザー (chatRoomUsers) の配列もクライアントに送り返しています。それによって、フロントエンドでルーム内のすべてのユーザー名をリストすることができるようになります。</p><p>サーバーにもっとコードを追加する前に、フロントエンドに戻ってチャットページを作成しましょう。そうすることによって、receive_message イベントをフロントエンドできちんと受信できて​​いるかどうかをテストすることができます。</p><h2 id="--5">チャットページを作成する方法</h2><p>クライアントフォルダーに、2 つの新しいファイルを作成します。</p><ol><li>src/pages/chat/index.js</li><li>src/pages/chat/styles.module.css</li></ol><p>まずはチャットページとコンポーネントで使用するスタイルをいくつか追加しましょう。</p><pre><code class="language-css">/* client/src/pages/chat/styles.module.css */

.chatContainer {
  max-width: 1100px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1fr 4fr;
  gap: 20px;
}

/* Room and users component */
.roomAndUsersColumn {
  border-right: 1px solid #dfdfdf;
}
.roomTitle {
  margin-bottom: 60px;
  text-transform: uppercase;
  font-size: 2rem;
  color: #fff;
}
.usersTitle {
  font-size: 1.2rem;
  color: #fff;
}
.usersList {
  list-style-type: none;
  padding-left: 0;
  margin-bottom: 60px;
  color: rgb(153, 217, 234);
}
.usersList li {
  margin-bottom: 12px;
}

/* Messages */
.messagesColumn {
  height: 85vh;
  overflow: auto;
  padding: 10px 10px 10px 40px;
}
.message {
  background: rgb(0, 24, 111);
  border-radius: 6px;
  margin-bottom: 24px;
  max-width: 600px;
  padding: 12px;
}
.msgMeta {
  color: rgb(153, 217, 234);
  font-size: 0.75rem;
}
.msgText {
  color: #fff;
}

/* Message input and button */
.sendMessageContainer {
  padding: 16px 20px 20px 16px;
}
.messageInput {
  padding: 14px;
  margin-right: 16px;
  width: 60%;
  border-radius: 6px;
  border: 1px solid rgb(153, 217, 234);
  font-size: 0.9rem;
}
</code></pre><p>追加できたら、チャットページが下のようになるかを見てみましょう。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/chat-page.jpeg" class="kg-image" alt="chat-page" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2024/02/chat-page.jpeg 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2024/02/chat-page.jpeg 1000w, https://www.freecodecamp.org/japanese/news/content/images/2024/02/chat-page.jpeg 1216w" sizes="(min-width: 720px) 720px" width="1216" height="678" loading="lazy"></figure><p>このページのすべてのコードとロジックを 1 つのファイルにまとめてしまうと、読みにくく管理が難しくなってしまうため、せっかく便利なフロントエンドフレームワーク (React) を使用しているので、<strong>このページをコンポーネントに分けます</strong>。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/image-248.png" class="kg-image" alt="image-248" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2024/02/image-248.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2024/02/image-248.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/2024/02/image-248.png 1232w" sizes="(min-width: 720px) 720px" width="1232" height="719" loading="lazy"></figure><h3 id="--6">チャットページのコンポーネント:</h3><p><strong><strong>A</strong></strong>: チャットルーム名、そのチャットルーム内にいるユーザーリスト、およびユーザーをチャットルームから削除する「退出」ボタンが含まれる部分。</p><p><strong><strong>B</strong></strong>: 送信されたメッセージ。最初のレンダリング時に、そのチャットルームで送信された最新 100 件のメッセージがデータベースから取得され、ユーザーに表示されます。</p><p><strong><strong>C</strong></strong>: メッセージを入力して送信するための入力フィールドとボタン。</p><p>まずはコンポーネント B を作成して、ユーザーにメッセージを表示できるようにします。</p><h2 id="-b-">メッセージコンポーネント (B) を作成する方法</h2><p>新しいファイル src/pages/chat/messages.js を作成し、次のコードを追加してください。</p><pre><code class="language-jsx">// client/src/pages/chat/messages.js

import styles from './styles.module.css';
import { useState, useEffect } from 'react';

const Messages = ({ socket }) =&gt; {
  const [messagesRecieved, setMessagesReceived] = useState([]);

  // Runs whenever a socket event is recieved from the server
  useEffect(() =&gt; {
    socket.on('receive_message', (data) =&gt; {
      console.log(data);
      setMessagesReceived((state) =&gt; [
        ...state,
        {
          message: data.message,
          username: data.username,
          __createdtime__: data.__createdtime__,
        },
      ]);
    });

	// Remove event listener on component unmount
    return () =&gt; socket.off('receive_message');
  }, [socket]);

  // dd/mm/yyyy, hh:mm:ss
  function formatDateFromTimestamp(timestamp) {
    const date = new Date(timestamp);
    return date.toLocaleString();
  }

  return (
    &lt;div className={styles.messagesColumn}&gt;
      {messagesRecieved.map((msg, i) =&gt; (
        &lt;div className={styles.message} key={i}&gt;
          &lt;div style={{ display: 'flex', justifyContent: 'space-between' }}&gt;
            &lt;span className={styles.msgMeta}&gt;{msg.username}&lt;/span&gt;
            &lt;span className={styles.msgMeta}&gt;
              {formatDateFromTimestamp(msg.__createdtime__)}
            &lt;/span&gt;
          &lt;/div&gt;
          &lt;p className={styles.msgText}&gt;{msg.message}&lt;/p&gt;
          &lt;br /&gt;
        &lt;/div&gt;
      ))}
    &lt;/div&gt;
  );
};

export default Messages;</code></pre><p>上記には、ソケットイベントが受信されるたびに実行される <em>useEffect</em> フックがあります。次に、receive_message というイベントリスナーに渡されたメッセージデータを取得します。そこから、<em>messagesReceived</em> の state を設定します。これは、メッセージ、送信者のユーザー名、メッセージが送信された日付を含むメッセージオブジェクトの配列です。</p><p>今作ったこのメッセージコンポーネントをチャットページにインポートし、App.js でチャットページのルートを作成しましょう。</p><pre><code class="language-jsx">// client/src/pages/chat/index.js

import styles from './styles.module.css';
import MessagesReceived from './messages';

const Chat = ({ socket }) =&gt; {
  return (
    &lt;div className={styles.chatContainer}&gt;
      &lt;div&gt;
        &lt;MessagesReceived socket={socket} /&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
};

export default Chat;
</code></pre><pre><code class="language-jsx">// client/src/App.js

import './App.css';
import { useState } from 'react';
import Home from './pages/home';
import Chat from './pages/chat';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import io from 'socket.io-client';

const socket = io.connect('http://localhost:4000');

function App() {
  const [username, setUsername] = useState('');
  const [room, setRoom] = useState('');

  return (
    &lt;Router&gt;
      &lt;div className='App'&gt;
        &lt;Routes&gt;
          &lt;Route
            path='/'
            element={
              &lt;Home
                username={username}
                setUsername={setUsername}
                room={room}
                setRoom={setRoom}
                socket={socket}
              /&gt;
            }
          /&gt;
          {/* Add this */}
          &lt;Route
            path='/chat'
            element={&lt;Chat username={username} room={room} socket={socket} /&gt;}
          /&gt;
        &lt;/Routes&gt;
      &lt;/div&gt;
    &lt;/Router&gt;
  );
}

export default App;
</code></pre><p>一度テストしてみましょう。ホームページにアクセスしてルームに参加してみてください。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/joining-a-room.jpeg" class="kg-image" alt="joining-a-room" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2024/02/joining-a-room.jpeg 600w, https://www.freecodecamp.org/japanese/news/content/images/2024/02/joining-a-room.jpeg 661w" width="661" height="538" loading="lazy"></figure><p>チャットページに移動し、ChatBot からウェルカムメッセージを受け取ります。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/welcome-message.jpeg" class="kg-image" alt="welcome-message" width="571" height="357" loading="lazy"></figure><p>ユーザーは受信したメッセージを確認できるようになりました。上出来です！</p><p>次は、メッセージを永続的に保存できるようにデータベースを設定します。</p><h2 id="harperdb--1">HarperDB でスキーマとテーブルを作成する方法</h2><p>HarperDB のダッシュボードに戻り、「browse (参照)」をクリックします。次に、realtime_chat_app という新しいスキーマを作成します。スキーマというのは、単に複数のテーブルをまとめたもののことです。</p><p>そのスキーマ内に、ハッシュ属性が id の messages というテーブルを作成します。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/image-258.png" class="kg-image" alt="image-258" width="591" height="430" loading="lazy"></figure><p>これで、メッセージを保存する場所ができたので、SendMessage コンポーネントを作成しましょう。</p><h2 id="-c-">メッセージ送信コンポーネント (C) の作成方法</h2><p>ファイル src/pages/chat/send-message.js を作成し、次のコードを追加してください。</p><pre><code class="language-jsx">// client/src/pages/chat/send-message.js

import styles from './styles.module.css';
import React, { useState } from 'react';

const SendMessage = ({ socket, username, room }) =&gt; {
  const [message, setMessage] = useState('');

  const sendMessage = () =&gt; {
    if (message !== '') {
      const __createdtime__ = Date.now();
      // Send message to server. We can't specify who we send the message to from the frontend. We can only send to server. Server can then send message to rest of users in room
      socket.emit('send_message', { username, room, message, __createdtime__ });
      setMessage('');
    }
  };

  return (
    &lt;div className={styles.sendMessageContainer}&gt;
      &lt;input
        className={styles.messageInput}
        placeholder='Message...'
        onChange={(e) =&gt; setMessage(e.target.value)}
        value={message}
      /&gt;
      &lt;button className='btn btn-primary' onClick={sendMessage}&gt;
        Send Message
      &lt;/button&gt;
    &lt;/div&gt;
  );
};

export default SendMessage;</code></pre><p>上では、ユーザーが「メッセージの送信」というボタンをクリックすると、send_message のソケットイベントがメッセージオブジェクトとともにサーバーに送信されます。後ほどこのイベントをサーバー上で処理します。</p><p>SendMessage をチャットページにインポートします。</p><pre><code class="language-js">// src/pages/chat/index.js

import styles from './styles.module.css';
import MessagesReceived from './messages';
import SendMessage from './send-message';

const Chat = ({ username, room, socket }) =&gt; {
  return (
    &lt;div className={styles.chatContainer}&gt;
      &lt;div&gt;
        &lt;MessagesReceived socket={socket} /&gt;
        &lt;SendMessage socket={socket} username={username} room={room} /&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
};

export default Chat;</code></pre><p>チャットページは次のようになります。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/image-259.png" class="kg-image" alt="image-259" width="521" height="980" loading="lazy"></figure><p>次に、データベースとのやり取りを開始できるように、HarperDB の環境変数を設定する必要があります。</p><h2 id="harperdb--2">HarperDB の環境変数を設定する方法</h2><p>HarperDB にメッセージを保存できるようにするには、HarperDB インスタンスの URL と API パスワードが必要です。</p><p>HarperDB ダッシュボードでインスタンスをクリックし、「config (設定)」に移動します。インスタンス URL とインスタンス API 認証ヘッダー (データベースへのあらゆるリクエストを許可する「super_user」パスワード) が表示されます。絶対に誰とも共有しないでください！</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/image-263.png" class="kg-image" alt="image-263" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2024/02/image-263.png 600w, https://www.freecodecamp.org/japanese/news/content/images/2024/02/image-263.png 943w" sizes="(min-width: 720px) 720px" width="943" height="410" loading="lazy"></figure><p>これらの変数を .env ファイルに保存します。<strong>警告: .env ファイルは絶対に GitHub にプッシュしないでください</strong>。このファイルは一般に公開すべきものではありません。これらの変数はバックグラウンドでサーバー経由で読み込みされます。</p><p>次のファイルを作成し、あなたの HarperDB の URL とパスワードを追加します。</p><pre><code class="language-bash">// server/.env

HARPERDB_URL="&lt;your url goes here&gt;"
HARPERDB_PW="Basic &lt;your password here&gt;"</code></pre><p>また、.env が node_modules フォルダーとともに GitHub にプッシュされるのを防ぐための .gitignore ファイルも作成します。</p><pre><code class="language-bash">// server/.gitignore

.env
node_modules</code></pre><p>注意: Git や GitHub をうまく使いこなすことは全ての開発者にとって必須です。もし Git の流儀がまだ曖昧な方は、私の <a href="https://www.doabledanny.com/git-workflows">Git のワークフローに関する記事</a>を見てみてください。</p><p>または、同じ Git コマンドをいつも検索している方や、コマンドを検索、修正、コピー/ ペーストする簡単な方法をお探しの方はぜひ、私が作った人気 <a href="https://doabledanny.gumroad.com/l/git-commands-cheat-sheet-pdf">Git コマンドチートシートの PDF 版</a>と <a href="https://doabledanny.gumroad.com/l/git-cheat-sheet-poster">Git チートシートポスター版</a>をチェックしてみてください。</p><p>最後に、次のコードをメインサーバーファイルの先頭に追加して、環境変数をサーバーに読み込みましょう。</p><pre><code class="language-js">// server/index.js

require('dotenv').config();
console.log(process.env.HARPERDB_URL); // remove this after you've confirmed it working
const express = require('express');
// ...</code></pre><h2 id="socket-io--2">Socket.io を利用してユーザーが相互にメッセージを送信できるようにする方法</h2><p>サーバー上で send_message のイベントを聞き取り、ルーム内のすべてのユーザーにメッセージを送信します。</p><pre><code class="language-js">// server/index.js

const express = require('express');
// ...
const harperSaveMessage = require('./services/harper-save-message'); // Add this

// ...

// Listen for when the client connects via socket.io-client
io.on('connection', (socket) =&gt; {
    
  // ...

  // Add this
  socket.on('send_message', (data) =&gt; {
    const { message, username, room, __createdtime__ } = data;
    io.in(room).emit('receive_message', data); // Send to all users in room, including sender
    harperSaveMessage(message, username, room, __createdtime__) // Save message in db
      .then((response) =&gt; console.log(response))
      .catch((err) =&gt; console.log(err));
  });
});

server.listen(4000, () =&gt; 'Server is running on port 3000');
</code></pre><p>次に、harperSaveMessage という関数を作成する必要があります。server/services/harper-save-message.js に新しいファイルを作成し、以下のコードを追加します。</p><pre><code class="language-js">// server/services/harper-save-message.js

var axios = require('axios');

function harperSaveMessage(message, username, room) {
  const dbUrl = process.env.HARPERDB_URL;
  const dbPw = process.env.HARPERDB_PW;
  if (!dbUrl || !dbPw) return null;

  var data = JSON.stringify({
    operation: 'insert',
    schema: 'realtime_chat_app',
    table: 'messages',
    records: [
      {
        message,
        username,
        room,
      },
    ],
  });

  var config = {
    method: 'post',
    url: dbUrl,
    headers: {
      'Content-Type': 'application/json',
      Authorization: dbPw,
    },
    data: data,
  };

  return new Promise((resolve, reject) =&gt; {
    axios(config)
      .then(function (response) {
        resolve(JSON.stringify(response.data));
      })
      .catch(function (error) {
        reject(error);
      });
  });
}

module.exports = harperSaveMessage;
</code></pre><p>上記では、データの保存には少し時間がかかる場合があるため、データが正常に保存された場合は解決され、失敗した場合は拒否される Promise を返しています。</p><p>上記のコードをどこで入手したか気になりましたか？HarperDB のスタジオダッシュボードには、便利な「<a href="https://studio.harperdb.io/resources/examples/QuickStart%20Examples/Create%20dev%20Schema">コードサンプル</a>」というセクションが用意されており、それを使うことによって作業がはるかに簡単になります。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/image-265.png" class="kg-image" alt="image-265" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2024/02/image-265.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2024/02/image-265.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/2024/02/image-265.png 1113w" sizes="(min-width: 720px) 720px" width="1113" height="713" loading="lazy"></figure><p>テストの時間です！ユーザーとしてルームに参加し、メッセージを送信します。次に、HarperDB に移動し、「browse (参照)」をクリックして、「メッセージ」テーブルをクリックします。データベースにメッセージが表示されているはずです。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/image-264.png" class="kg-image" alt="image-264" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2024/02/image-264.png 600w, https://www.freecodecamp.org/japanese/news/content/images/2024/02/image-264.png 937w" sizes="(min-width: 720px) 720px" width="937" height="383" loading="lazy"></figure><p>素晴らしい😎これができたら次は、ユーザーがチャットルームに参加したときに、ルーム内で送信された最新 100 件のメッセージが読み込まれるようになれば最高だと思いませんか？</p><h2 id="harperdb--3">HarperDB からメッセージを取得する方法</h2><p>サーバー上で、特定のチャットルームで送信された最新 100 件のメッセージを取得する関数を作成しましょう (HarperDB では SQL クエリも使用できるんですよ👌):</p><pre><code class="language-js">// server/services/harper-get-messages.js

let axios = require('axios');

function harperGetMessages(room) {
  const dbUrl = process.env.HARPERDB_URL;
  const dbPw = process.env.HARPERDB_PW;
  if (!dbUrl || !dbPw) return null;

  let data = JSON.stringify({
    operation: 'sql',
    sql: `SELECT * FROM realtime_chat_app.messages WHERE room = '${room}' LIMIT 100`,
  });

  let config = {
    method: 'post',
    url: dbUrl,
    headers: {
      'Content-Type': 'application/json',
      Authorization: dbPw,
    },
    data: data,
  };

  return new Promise((resolve, reject) =&gt; {
    axios(config)
      .then(function (response) {
        resolve(JSON.stringify(response.data));
      })
      .catch(function (error) {
        reject(error);
      });
  });
}

module.exports = harperGetMessages;</code></pre><p>ユーザーがチャットルームに参加するたびにこの関数を呼び出します。</p><pre><code class="language-js">// server/index.js

// ...
const harperSaveMessage = require('./services/harper-save-message');
const harperGetMessages = require('./services/harper-get-messages'); // Add this

// ...

// Listen for when the client connects via socket.io-client
io.on('connection', (socket) =&gt; {
  console.log(`User connected ${socket.id}`);

  // Add a user to a room
  socket.on('join_room', (data) =&gt; {
      
    // ...

    // Add this
    // Get last 100 messages sent in the chat room
    harperGetMessages(room)
      .then((last100Messages) =&gt; {
        // console.log('latest messages', last100Messages);
        socket.emit('last_100_messages', last100Messages);
      })
      .catch((err) =&gt; console.log(err));
  });

 // ...</code></pre><p>上記では、メッセージが正常にフェッチされると、last_100_messages という Socket.io イベントが発行されます。そうすると、フロントエンドでこのイベントを聞き取ります。</p><h2 id="-100-">クライアントで最新メッセージ 100 件を表示する方法</h2><p>以下では、<em>last_100_messages</em> イベントのための、Socket.io イベントリスナーを含む useEffect フックを追加します。そこから、メッセージは日付順に並べ替えられ、最新のメッセージが一番下に表示され、<em>messagesReceived</em> の state が更新されます。</p><p><em>messagesReceived</em> が更新されると、useEffect が実行され、<em>messageColumn</em> div が最新のメッセージまでスクロールされます。これにより、アプリのユーザーエクスペリエンスが向上します👍。</p><pre><code class="language-js">// client/src/pages/chat/messages.js

import styles from './styles.module.css';
import { useState, useEffect, useRef } from 'react';

const Messages = ({ socket }) =&gt; {
  const [messagesRecieved, setMessagesReceived] = useState([]);

  const messagesColumnRef = useRef(null); // Add this

  // Runs whenever a socket event is recieved from the server
  useEffect(() =&gt; {
    socket.on('receive_message', (data) =&gt; {
      console.log(data);
      setMessagesReceived((state) =&gt; [
        ...state,
        {
          message: data.message,
          username: data.username,
          __createdtime__: data.__createdtime__,
        },
      ]);
    });

    // Remove event listener on component unmount
    return () =&gt; socket.off('receive_message');
  }, [socket]);

  // Add this
  useEffect(() =&gt; {
    // Last 100 messages sent in the chat room (fetched from the db in backend)
    socket.on('last_100_messages', (last100Messages) =&gt; {
      console.log('Last 100 messages:', JSON.parse(last100Messages));
      last100Messages = JSON.parse(last100Messages);
      // Sort these messages by __createdtime__
      last100Messages = sortMessagesByDate(last100Messages);
      setMessagesReceived((state) =&gt; [...last100Messages, ...state]);
    });

    return () =&gt; socket.off('last_100_messages');
  }, [socket]);

  // Add this
  // Scroll to the most recent message
  useEffect(() =&gt; {
    messagesColumnRef.current.scrollTop =
      messagesColumnRef.current.scrollHeight;
  }, [messagesRecieved]);

  // Add this
  function sortMessagesByDate(messages) {
    return messages.sort(
      (a, b) =&gt; parseInt(a.__createdtime__) - parseInt(b.__createdtime__)
    );
  }

  // dd/mm/yyyy, hh:mm:ss
  function formatDateFromTimestamp(timestamp) {
    const date = new Date(timestamp);
    return date.toLocaleString();
  }

  return (
    // Add ref to this div
    &lt;div className={styles.messagesColumn} ref={messagesColumnRef}&gt;
      {messagesRecieved.map((msg, i) =&gt; (
        &lt;div className={styles.message} key={i}&gt;
          &lt;div style={{ display: 'flex', justifyContent: 'space-between' }}&gt;
            &lt;span className={styles.msgMeta}&gt;{msg.username}&lt;/span&gt;
            &lt;span className={styles.msgMeta}&gt;
              {formatDateFromTimestamp(msg.__createdtime__)}
            &lt;/span&gt;
          &lt;/div&gt;
          &lt;p className={styles.msgText}&gt;{msg.message}&lt;/p&gt;
          &lt;br /&gt;
        &lt;/div&gt;
      ))}
    &lt;/div&gt;
  );
};

export default Messages;
</code></pre><h2 id="-a-">チャットルームとユーザー (A) の表示方法</h2><p>コンポーネント B と C の作成が完了したので、最後に A を作成しましょう。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/image-248-1.png" class="kg-image" alt="image-248-1" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2024/02/image-248-1.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2024/02/image-248-1.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/2024/02/image-248-1.png 1232w" sizes="(min-width: 720px) 720px" width="1232" height="719" loading="lazy"></figure><p>サーバー上では、誰かユーザーがチャットルームに参加すると、ルーム内全ユーザーをそのルーム内の全クライアントに送る<em> chatroom_users</em> イベントが発行されます。<em>RoomAndUsers</em> というコンポーネントでそのイベントの聞き取りをしてみましょう。</p><p>下のコードには、「Leave (退室)」ボタンもあり、これを押すとサーバーに <em>leave_room</em> イベントが発行されます。その後、そのユーザーをホームページへリダイレクトします。</p><pre><code class="language-js">// client/src/pages/chat/room-and-users.js

import styles from './styles.module.css';
import { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';

const RoomAndUsers = ({ socket, username, room }) =&gt; {
  const [roomUsers, setRoomUsers] = useState([]);

  const navigate = useNavigate();

  useEffect(() =&gt; {
    socket.on('chatroom_users', (data) =&gt; {
      console.log(data);
      setRoomUsers(data);
    });

    return () =&gt; socket.off('chatroom_users');
  }, [socket]);

  const leaveRoom = () =&gt; {
    const __createdtime__ = Date.now();
    socket.emit('leave_room', { username, room, __createdtime__ });
    // Redirect to home page
    navigate('/', { replace: true });
  };

  return (
    &lt;div className={styles.roomAndUsersColumn}&gt;
      &lt;h2 className={styles.roomTitle}&gt;{room}&lt;/h2&gt;

      &lt;div&gt;
        {roomUsers.length &gt; 0 &amp;&amp; &lt;h5 className={styles.usersTitle}&gt;Users:&lt;/h5&gt;}
        &lt;ul className={styles.usersList}&gt;
          {roomUsers.map((user) =&gt; (
            &lt;li
              style={{
                fontWeight: `${user.username === username ? 'bold' : 'normal'}`,
              }}
              key={user.id}
            &gt;
              {user.username}
            &lt;/li&gt;
          ))}
        &lt;/ul&gt;
      &lt;/div&gt;

      &lt;button className='btn btn-outline' onClick={leaveRoom}&gt;
        Leave
      &lt;/button&gt;
    &lt;/div&gt;
  );
};

export default RoomAndUsers;</code></pre><p>このコンポーネントをチャットページにインポートしましょう。</p><pre><code class="language-js">// client/src/pages/chat/index.js

import styles from './styles.module.css';
import RoomAndUsersColumn from './room-and-users'; // Add this
import SendMessage from './send-message';
import MessagesReceived from './messages';

const Chat = ({ username, room, socket }) =&gt; {
  return (
    &lt;div className={styles.chatContainer}&gt;
      {/* Add this */}
      &lt;RoomAndUsersColumn socket={socket} username={username} room={room} /&gt;

      &lt;div&gt;
        &lt;MessagesReceived socket={socket} /&gt;
        &lt;SendMessage socket={socket} username={username} room={room} /&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
};

export default Chat;
</code></pre><h2 id="socket-io--3">Socket.io のルームからユーザーを削除する方法</h2><p>Socket.io には、Socket.io ルームからユーザーを削除するために使用できる <em>leave()</em> メソッドが用意されています。また、サーバーメモリー上の配列でユーザー情報を管理しているため、この配列からもユーザーを削除します。</p><pre><code class="language-js">// server/index.js

const leaveRoom = require('./utils/leave-room'); // Add this

// ...

// Listen for when the client connects via socket.io-client
io.on('connection', (socket) =&gt; {
    
  // ...

  // Add this
  socket.on('leave_room', (data) =&gt; {
    const { username, room } = data;
    socket.leave(room);
    const __createdtime__ = Date.now();
    // Remove user from memory
    allUsers = leaveRoom(socket.id, allUsers);
    socket.to(room).emit('chatroom_users', allUsers);
    socket.to(room).emit('receive_message', {
      username: CHAT_BOT,
      message: `${username} has left the chat`,
      __createdtime__,
    });
    console.log(`${username} has left the chat`);
  });
});

server.listen(4000, () =&gt; 'Server is running on port 3000');
</code></pre><p>次に、<em>leaveRoom()</em> という関数を作成する必要があります。</p><pre><code class="language-js">// server/utils/leave-room.js

function leaveRoom(userID, chatRoomUsers) {
  return chatRoomUsers.filter((user) =&gt; user.id != userID);
}

module.exports = leaveRoom;
</code></pre><p>なぜこの短い関数を別の utils フォルダーに置いておく必要があるのでしょうか。それは、後で再び使用することになるので、同じことを繰り返さなくてもいいようにです。(コードを <a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself">DRY</a> に保つことができます。)</p><p>さあ、テストしてみましょう。2 つのブラウザーを並べて開き、両方のブラウザーでチャットに参加してみてください。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/image-266.png" class="kg-image" alt="image-266" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2024/02/image-266.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2024/02/image-266.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/2024/02/image-266.png 1441w" sizes="(min-width: 720px) 720px" width="1441" height="954" loading="lazy"></figure><p>次に、ブラウザー 2 の Leave (退室) ボタンをクリックします。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/image-267.png" class="kg-image" alt="image-267" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2024/02/image-267.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2024/02/image-267.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/2024/02/image-267.png 1341w" sizes="(min-width: 720px) 720px" width="1341" height="952" loading="lazy"></figure><p>ユーザーはチャットから削除され、他のユーザーに、退席したことを通知するメッセージが送信されます。ナイス！</p><h2 id="socket-io--4">Socket.io 接続を切断するイベントリスナーを追加する方法</h2><p>インターネットの接続が切断された場合など、ユーザーが何らかの理由でサーバーから切断された場合はどうなるのでしょうか？Socket.io にはそのような時のために、ビルトインの切断イベントリスナーがあります。これをサーバーに追加して、接続の切断時にユーザーをメモリから削除します。</p><pre><code class="language-js">// server/index.js

// ...

// Listen for when the client connects via socket.io-client
io.on('connection', (socket) =&gt; {
    
  // ...
    
  // Add this
  socket.on('disconnect', () =&gt; {
    console.log('User disconnected from the chat');
    const user = allUsers.find((user) =&gt; user.id == socket.id);
    if (user?.username) {
      allUsers = leaveRoom(socket.id, allUsers);
      socket.to(chatRoom).emit('chatroom_users', allUsers);
      socket.to(chatRoom).emit('receive_message', {
        message: `${user.username} has disconnected from the chat.`,
      });
    }
  });
});

server.listen(4000, () =&gt; 'Server is running on port 3000');
</code></pre><p>これで、React フロントエンド、Node/Express バックエンド、HarperDB データベースを備えたフルスタックのリアルタイムチャットアプリケーションが構築されました。お見事！</p><p>次回は、ユーザーが HarperDB 内で独自の API エンドポイントを定義できるようにする HarperDB の<a href="https://harperdb.io/docs/custom-functions/">カスタム関数</a>をご紹介する予定です。こうすることで、アプリケーション全体を 1 か所でまとめて構築できます。HarperDB でスタックを縮小 (シンプル化) する例は、<a href="https://harperdb.io/blog/mean-stack-alternative/">この記事で</a>ご覧ください。</p><h2 id="--7"><strong>挑戦状💪</strong></h2><p>チャットページでページを更新すると、ユーザーのユーザーネームとルームが失われてしまいます。ユーザーがページを更新してもこの情報が失われないようにする方法を探し出してみてください。ヒント: <a href="https://www.w3schools.com/html/html5_webstorage.asp">ローカルストレージ</a>が役立つかもしれません！</p><h2 id="--8"><strong><strong><strong>最後まで読んでいただきありがとうございます！</strong></strong></strong></h2><p>この記事が少しでもお役に立てたのであれば、ぜひ以下もお願いします。</p><ul><li><a href="https://www.youtube.com/channel/UC0URylW_U4i26wN231yRqvA">私の YouTube チャンネルに登録</a>。React/NextJS/Node/Express に関する詳細なチュートリアルとプロジェクトビデオを投稿していく予定です。</li><li><a href="https://twitter.com/doabledanny">Twitter でフォロー</a>。私のフリーランス活動、趣味のプロジェクトや、最近学んでいることについてツイートしています。</li><li><a href="https://doabledanny.gumroad.com/">私の Gumroad ストアをチェックする</a>。そこでは、便利で人気な早見表やポスターを作成していて、執筆現在までに 8000 ダウンロードを達成しています。</li><li><a href="https://www.doabledanny.com/blog/">私の Web 開発ブログをチェックする。</a></li></ul> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ npm uninstall - パッケージを削除する方法 ]]>
                </title>
                <description>
                    <![CDATA[ Node のパッケージマネージャー (NPM) には、パッケージを操作するために、さまざまなコマンドが用意されています。 npm ライブラリからパッケージをインストールするのと同じく、アンインストールすることもできます。 npm uninstall コマンドは、この目的のために用意されているコマンドで、パッケージのアンインストールができます。 ただし、通常のパッケージ (dependencies に指定されたパッケージ) をアンインストールする方法と、グローバルにインストールされたパッケージや、開発環境のみで使うパッケージをアンインストール方法には違いがあります。 この記事では、通常のパッケージ、グローバルパッケージ、開発用のパッケージのアンインストール方法について説明していきます。 パッケージを npm uninstall で削除する方法 パッケージの配置されているディレクトリ中で npm uninstall パッケージ名 という構文を使うと、コマンドでパッケージを削除することができます。 この記事では NodeJS のフレームワークである Express をデモ用に取り ]]>
                </description>
                <link>https://www.freecodecamp.org/japanese/news/npm-uninstall-how-to-remove-a-package/</link>
                <guid isPermaLink="false">63708a93196be305f95c542e</guid>
                
                    <category>
                        <![CDATA[ npm ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Akiko Takano ]]>
                </dc:creator>
                <pubDate>Fri, 25 Nov 2022 23:30:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/japanese/news/content/images/2022/11/shut-down-g5ad24366d_1280.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文:</strong> <a href="https://www.freecodecamp.org/news/npm-uninstall-how-to-remove-a-package/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">npm Uninstall – How to Remove a Package</a>
      </p><p>Node のパッケージマネージャー (NPM) には、パッケージを操作するために、さまざまなコマンドが用意されています。</p><p>npm ライブラリからパッケージをインストールするのと同じく、アンインストールすることもできます。</p><p><code>npm uninstall</code> コマンドは、この目的のために用意されているコマンドで、パッケージのアンインストールができます。</p><p>ただし、通常のパッケージ (dependencies に指定されたパッケージ) をアンインストールする方法と、グローバルにインストールされたパッケージや、開発環境のみで使うパッケージをアンインストール方法には違いがあります。</p><p>この記事では、通常のパッケージ、グローバルパッケージ、開発用のパッケージのアンインストール方法について説明していきます。</p><h2 id="-npm-uninstall-">パッケージを npm uninstall で削除する方法</h2><p>パッケージの配置されているディレクトリ中で <code>npm uninstall パッケージ名</code> という構文を使うと、コマンドでパッケージを削除することができます。</p><p>この記事では NodeJS のフレームワークである Express をデモ用に取り上げ、パッケージをアンインストールする方法をご紹介します。</p><p>以下のスクリーンショットから、Express が依存関係 (dependencies) として package.json 中に指定されていることがわかりますね。</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/11/ss-1.png" class="kg-image" alt="ss-1" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/11/ss-1.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2022/11/ss-1.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/size/w1600/2022/11/ss-1.png 1600w, https://www.freecodecamp.org/japanese/news/content/images/2022/11/ss-1.png 1920w" sizes="(min-width: 1200px) 1200px" width="1920" height="1080" loading="lazy"></figure><p>ですが、<code>npm uninstall express</code> を実行した後は、依存関係の中には Express は見当たりません。</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/11/ss-2.png" class="kg-image" alt="ss-2" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/11/ss-2.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2022/11/ss-2.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/size/w1600/2022/11/ss-2.png 1600w, https://www.freecodecamp.org/japanese/news/content/images/2022/11/ss-2.png 1920w" sizes="(min-width: 1200px) 1200px" width="1920" height="1080" loading="lazy"></figure><p>Express がもはや存在しないことがわかりますね。依存関係がなくなったので、package.json 中の "dependencies" というキーもなくなります。</p><h2 id="-npm-uninstall--1">開発用のパッケージを npm uninstall で削除する方法</h2><p>開発環境でのみ使われるパッケージは、dev dependency として指定されたものが該当します。</p><p>dev dependency のパッケージを削除するには、<code>-D</code> もしくは <code>--save-dev</code> フラグを添えて、パッケージの名前を指定して npm uninstall を実行します。</p><p>基本的な構文は &nbsp;<code>npm uninstall -D package-name</code> または <code>npm uninstall --save-dev package-name</code> となります。</p><p>依存関係が配置されているディレクトリ (フォルダ) で、このコマンドを実行してください。</p><p>この記事ではデモ用に Nodemon を使って dev dependency のパッケージを削除する例をご紹介します。</p><p>Nodemon を使って NodeJS アプリケーションを起動すると、開発中のファイルやディレクトリ内の変更を検知して、自動的にアプリケーションを再読み込みしてくれます。</p><p>以下のスクリーンショットでは、Nodemon が dev dependency の一覧に含まれているのがわかりますね。</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/11/ss-3.png" class="kg-image" alt="ss-3" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/11/ss-3.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2022/11/ss-3.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/size/w1600/2022/11/ss-3.png 1600w, https://www.freecodecamp.org/japanese/news/content/images/2022/11/ss-3.png 1920w" sizes="(min-width: 1200px) 1200px" width="1920" height="1080" loading="lazy"></figure><p>Nodemon を削除するためには、<code>npm uninstall –D nodemon</code> を実行します。</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/11/ss-4.png" class="kg-image" alt="ss-4" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/11/ss-4.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2022/11/ss-4.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/size/w1600/2022/11/ss-4.png 1600w, https://www.freecodecamp.org/japanese/news/content/images/2022/11/ss-4.png 1920w" sizes="(min-width: 1200px) 1200px" width="1920" height="1080" loading="lazy"></figure><p>すると &nbsp;<code>package.json</code> から Nodemon がなくなったことがわかりますね。</p><h2 id="-npm-uninstall--2">グローバルパッケージを npm uninstall で削除する方法</h2><p>グローバルパッケージは皆さんのコンピューター上にグローバルにインストールされているパッケージです。そのため、プロジェクトに毎回インストールする必要がありません。</p><p>グローバルパッケージを削除するためには <code>-g</code> フラグを添えて、パッケージ名を指定して npm uninstall を実行します。</p><p>基本的な構文は、<code>npm uninstall -g package-name</code> となります。</p><p>ここでは、グローバルパッケージを削除する例として、オリジン間リソース共有を指定するために用いる CORS (Cross-origin Resource Sharing) パッケージを取り上げます。</p><p>CORS はブラウザの同一オリジンポリシー / Same Origin Policy (SOP) の動作をブロックしてくれます。この結果、ブラウザから他のサイトへのリクエストを送信することができます。</p><p>以下のスクリーンショットから、package.json 中には CORS パッケージが存在していないことがわかりますね。</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/11/ss-5.png" class="kg-image" alt="ss-5" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/11/ss-5.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2022/11/ss-5.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/size/w1600/2022/11/ss-5.png 1600w, https://www.freecodecamp.org/japanese/news/content/images/2022/11/ss-5.png 1920w" sizes="(min-width: 1200px) 1200px" width="1920" height="1080" loading="lazy"></figure><p>CORS はプロジェクト直下ではなく、このコンピューターにグローバルにインストールされています。</p><p>パッケージをグローバルにインストールしたり、グローバルにインストールされたパッケージを一覧表示したい場合は、<code>npm list -g</code> &nbsp;を実行します。</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/11/ss-6.png" class="kg-image" alt="ss-6" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/11/ss-6.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2022/11/ss-6.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/size/w1600/2022/11/ss-6.png 1600w, https://www.freecodecamp.org/japanese/news/content/images/2022/11/ss-6.png 1920w" sizes="(min-width: 1200px) 1200px" width="1920" height="1080" loading="lazy"></figure><p>今度は、CORS がグローバルのパッケージ中に含まれることがわかりますね。</p><p>グローバルにインストールされた CORS パッケージをアンインストールするには、<code>npm uninstall -g cors</code> を実行します。</p><p>アンインストール用のコマンドの実行後、<code>npm list –g</code> を実行しても CORS が含まれていないことがわかります。</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/11/ss-7.png" class="kg-image" alt="ss-7" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/11/ss-7.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2022/11/ss-7.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/size/w1600/2022/11/ss-7.png 1600w, https://www.freecodecamp.org/japanese/news/content/images/2022/11/ss-7.png 1920w" sizes="(min-width: 1200px) 1200px" width="1920" height="1080" loading="lazy"></figure><h2 id="-">まとめ</h2><p>この記事では、いろいろな種類の NPM パッケージをアンインストールする方法について学びました。これで不要なパッケージを削除できるようになり、コードをより管理しやすくなりました。</p><p>読んでくださってありがとうございます。</p><p>この記事がお役に立つようであれば、皆さんからシェアしていただけたら幸いです。</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Ubuntu に Node.js をインストールし、npm を最新バージョンに更新する方法 ]]>
                </title>
                <description>
                    <![CDATA[ ※この記事は 2020 年 6 月時点の内容になっています。 apt パッケージマネージャを使って最新版の Node.js をインストールしようとすると、v10.19.0 になってしまいます。これは Ubuntu App Store にある最新版ですが、Node.js の最新版ではありません。 これは、あるソフトウェアの新バージョンがリリースされた場合、Ubuntu チームがテストして、公式の Ubuntu App Store でリリースするまで数ヶ月かかることがあるためです。その結果、どんなソフトウェアでも最新版を入手するためには、開発者が公開しているプライベートパッケージを使用しなければならない場合があります。 このチュートリアルでは、Node.js の v12.18.1 (LTS - Long Term Support: 長期サポート) または v14.4  のいずれかを取得します。最新版を取得するには、NodeSource または nvm (Node Version Manager) のいずれかを使用することができます。両方の使い方を紹介します。 ここでのコマンドはすべ ]]>
                </description>
                <link>https://www.freecodecamp.org/japanese/news/how-to-install-node-js-on-ubuntu-and-update-npm-to-the-latest-version/</link>
                <guid isPermaLink="false">621f83217e520d051a27fb12</guid>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Ubuntu ]]>
                    </category>
                
                    <category>
                        <![CDATA[ npm ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Mell ]]>
                </dc:creator>
                <pubDate>Sun, 20 Mar 2022 23:30:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/japanese/news/content/images/2022/03/node-npm-ubuntu-cover.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文:</strong> <a href="https://www.freecodecamp.org/news/how-to-install-node-js-on-ubuntu-and-update-npm-to-the-latest-version/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Install Node.js on Ubuntu and Update npm to the Latest Version</a>
      </p><p>※この記事は 2020 年 6 月時点の内容になっています。</p><p>apt パッケージマネージャを使って最新版の Node.js をインストールしようとすると、<strong>v10.19.0</strong> になってしまいます。これは Ubuntu App Store にある最新版ですが、Node.js の最新版ではありません。</p><p>これは、あるソフトウェアの新バージョンがリリースされた場合、Ubuntu チームがテストして、公式の Ubuntu App Store でリリースするまで数ヶ月かかることがあるためです。その結果、どんなソフトウェアでも最新版を入手するためには、開発者が公開しているプライベートパッケージを使用しなければならない場合があります。</p><p>このチュートリアルでは、Node.js の <strong>v12.18.1</strong> (LTS - Long Term Support: 長期サポート) または<strong> v14.4</strong> のいずれかを取得します。最新版を取得するには、<strong>NodeSource</strong> または <strong>nvm</strong> (Node Version Manager) のいずれかを使用することができます。両方の使い方を紹介します。</p><p>ここでのコマンドはすべて Ubuntu の CLI (ターミナル) を使用して実行されます。</p><h2 id="nvm-">NVM の使用 - 私が推奨する方法</h2><p>nvm を使用すると、プロジェクトごとに異なるバージョンの Node.js を使うことができるので気に入っています。</p><p>時には、異なるバージョンの Node.js を使っている人とプロジェクトで共同作業をしていて、プロジェクトで使われている Node.js のバージョンに切り替える必要があるかもしれません。そのような際、nvm は最適なツールです。</p><h2 id="nvm--1">NVM のインストール</h2><p><code>curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash</code></p><p>nvm がインストールされているか確認するには、<code>nvm --version</code> と入力します。 <code>0.35.3</code> のようなバージョンの番号が返ってくれば、nvm は正常にインストールされています。</p><p><strong>変更内容を反映するために、ターミナルを再起動してください。</strong></p><h2 id="nodejs-">NodeJSのインストール</h2><p>次に、バージョン 14.4 の Node.js をインストールしましょう。</p><p><code>nvm install 14.4.0</code> と実行するだけです。</p><p>同様のコマンドを使用し、任意のバージョンの Node.js をインストールすることもできます。(例: <code>nvm install 12.18.1</code>)</p><p>このコマンドは <strong>Node.js</strong> と最新の <strong>npm </strong>バージョン (<code>v6.14.5</code>) を自動的にインストールします。</p><p>Node.js のバージョンを切り替える必要がある場合は、<code>nvm use &lt;バージョン番号&gt;</code> &nbsp;を実行するだけです (例: <code>nvm use v12.18.1</code>)。</p><p>nvm でインストールした異なるバージョンの Node.js の一覧を表示するには、 <code>nvm ls</code> を実行します。</p><h2 id="nodesource-">NodeSourceのインストール</h2><p>以下のコマンドを実行して、Ubuntu に Node.js をインストールすることを伝えます。パッケージを NodeSource から取得します。</p><p><code>curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash -</code></p><p><strong>注意: </strong>v14.4.0 は Node.js の最新バージョンですが、現在 LTS は提供されていません。最新の LTS 版の Node.js をインストールするには、上記のコマンドの <code>14</code> を <code>12</code> に変更してください。</p><p>root ユーザーのパスワードを入力するよう促される場合があります。その場合、入力して Enter を押下してください。</p><h2 id="nodejs--1">NodeJSのインストール</h2><p>NodeSource の設定が終わったら、次は v14.4 の Node.js をインストールします。</p><p><code>sudo apt-get install -y nodejs</code> を実行してください。</p><p>インストールが完了したら、最新バージョンの Node.js がインストールされているか確認します。</p><p>ターミナルに <code>nodejs -v</code> と入力すると、<code>v14.4.0</code> が返ってくるはずです。</p><p>この時点で、npm が自動的にインストールされているはずです。npm のバージョンを確認するには <code>npm version</code> を実行します。npm の最新バージョン 6.14.5 を含むオブジェクトの <code>{ npm: '6.14.5' }</code> が表示されない場合は、以下のコマンドを実行して、手動で npm を更新することができます。</p><p><code>npm install -g npm@latest</code></p><p>npm がインストールされていないためにアップデートできないという問題が発生した場合は、<code>sudo apt-get install -y npm</code> で先に npm をインストールした後、上記のコマンドを実行してアップデートすることが可能です。</p><p>また、ある種の npm パッケージを実行するためには、以下のコマンドも実行する必要があります。</p><p><code>sudo apt install build-essential</code></p><p>以上です。</p><p>これであなたの Ubuntu 環境に最新バージョンの Node.js と NPM がインストールされた状態になりました。</p><p>素晴らしい作品を作ってください 😊</p> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
