Socket.io と React.js を使ったリアルタイムオークションシステムの構築方法 🤯 - DEV コミュニティ

この記事の内容とは?

本物のオークションのように、商品に入札すると、他の入札者からカウンタービッドが入ります。オークションは「速い」決断の入札に基づいており、十分に速く入札しないと他の人が勝ったり、上回ったりする可能性があります。

オンライン入札を使用するためには、同じ原則を守る必要があります。新しい入札が入ったときはすぐに入札者に情報を伝えなければなりません。

オークション

新しい入札についてサーバーからリアルタイムの情報を取得するには、以下の2つの方法があります:

  1. ロングポーリング HTTP リクエストを使用し、新しい入札についての情報を5~10秒ごとにHTTPリクエストする。

  2. オープンソケット(Websockets)を使用し、新しい入札があったときにサーバーから直接情報を取得する。

この記事ではWebsocketsについて、特にNode.jsライブラリのSocket.ioに焦点を当てて話します。

Novu - 最初のオープンソース通知アーキテクチャ

ちょっとした背景情報です。Novuは最初のオープンソース通知インフラです。基本的には、すべての製品の通知を管理するのに役立ちます。In-App(Facebookにあるベルアイコンのようなもの)、メール、SMSなどです。

新しいコントリビューターを探しています

当社のオープンソース通知インフラストラクチャを構築して、コミュニティから認識され、こちらでコミュニティヒーローになるために協力してください:
https://novu.co/contributors

コミュニティヒーローズ

じゃあ一体Socket.ioって何なの?

Socket.io はJavaScriptライブラリで、WebブラウザとNode.jsサーバー間のリアルタイム、双方向通信を可能にします。非常に高性能なライブラリであり、最短時間で大量のデータを処理することができます。

通常、サーバーから情報を取得するためにはHTTPリクエストを送る必要があります。Websocketsを用いると、サーバーから新しい情報があることを知らせてくれるので、リクエストを出す必要がありません。

この記事では、Socket.ioが提供するリアルタイム通信を利用して、ユーザーが商品をオークションに出品し、入札することができる入札システムを構築します。Socket.ioは商品がオークションに出品された際やユーザーが入札を行った後にもユーザーに通知します。

React & Node.js アプリケーションにSocket.ioを追加する方法

このセクションでは、入札システムのプロジェクト環境を設定します。また、ReactおよびNode.jsアプリケーションにSocket.ioを追加して連携させ、Socket.ioを介したリアルタイム通信が開発サーバー間で行えるようにする方法について学びます。

プロジェクトフォルダを作成し、clientとserverという名前の2つのサブフォルダを含むようにします。

mkdir bidding-system
cd bidding-system
mkdir client server

端末を介してclientフォルダに移動し、新しいReact.jsプロジェクトを作成します。

cd client
npx create-react-app ./

Socket.ioクライアントAPIとReact Routerをインストールします。React Routerは、Reactアプリケーション内でページ間をナビゲートするためのJavaScriptライブラリです。

npm install socket.io-client react-router-dom

不要なファイルをReactアプリケーションから削除し、以下のように App.js ファイルを更新して Hello World を表示します。

function App() {
  return (
    <div>
      <p>Hello World!</p>
    </div>
  );
}

次に、serverフォルダに移動して package.json ファイルを作成します。

cd server
npm init -y

Express.js、CORS、Nodemon、およびSocket.io Server APIをインストールします。

Express.js は高速でミニマリストなフレームワークであり、Node.jsでのウェブアプリケーション構築に多くの機能を提供します。CORS は、異なるドメイン間の通信を可能にするNode.jsパッケージです。

Nodemon はファイルの変更を検知して自動的にサーバーを再起動するNode.jsのツールであり、Socket.io はサーバー上でリアルタイム接続を設定するためのものです。

npm install express cors nodemon socket.io 

ウェブサーバーのエントリーポイントであるindex.jsファイルを作成します。

touch index.js

Express.jsを使用してシンプルなNode.jsサーバーを設定します。以下のコードスニペットは、ブラウザで http://localhost:4000/api を訪れたときにJSONオブジェクトを返します。

//index.js
const express = require('express');
const app = express();
const PORT = 4000;

app.get('/api', (req, res) => {
  res.json({
    message: 'Hello world',
  });
});

app.listen(PORT, () => {
  console.log(`Server listening on ${PORT}`);
});

HTTPライブラリとCORSライブラリをインポートし、クライアントとサーバーのドメイン間のデータ転送を可能にします。

const express = require('express');
const app = express();
const PORT = 4000;

// New imports
const http = require('http').Server(app);
const cors = require('cors');

app.use(cors());

app.get('/api', (req, res) => {
  res.json({
    message: 'Hello world',
  });
});

http.listen(PORT, () => {
  console.log(`Server listening on ${PORT}`);
});

次に、プロジェクトにSocket.ioを追加してリアルタイム接続を作成します。app.get() ブロックの前に以下のコードをコピーします。

//New imports
.....
const socketIO = require('socket.io')(http, {
    cors: {
        origin: "http://localhost:3000"
    }
});

//Add this before the app.get() block
socketIO.on('connection', (socket) => {
    console.log(`⚡: ${socket.id} user just connected!`);
    socket.on('disconnect', () => {
      console.log('🔥: A user disconnected');
    });
});

上のコードスニペットから、socket.io("connection") 関数はReactアプリとの接続を確立し、ユーザーがWebページを訪れるたびに各ソケットに一意のIDを作成し、コンソールにIDを記録します。

Webページを更新または閉じると、ソケットがdisconnectイベントを発火させ、ユーザーがソケットから切断されたことを示します。

次に、package.json ファイルのスクリプトリストにstartコマンドを追加してNodemonを設定します。以下のコードスニペットはNodemonを使ってサーバーを起動します。

//In server/package.json

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "nodemon index.js"
  },

以下のコマンドを使用して、Nodemonでサーバーを実行できます。

npm start

クライアントフォルダ内のApp.jsファイルを開いて、Socket.ioサーバーにReactアプリを接続します。

import socketIO from 'socket.io-client';
const socket = socketIO.connect('http://localhost:4000');

function App() {
  return (
    <div>
      <p>Hello World!</p>
    </div>
  );
}

React.jsサーバーを起動します。

npm start

サーバーを実行している端末を確認すると、React.jsクライアントのIDが端末に表示されるはずです。

おめでとうございます 🥂 、ReactアプリをSocket.ioを介してサーバーに成功裏に接続しました。

💡 この記事の残りの部分では、入札システムのフローを作成する方法について説明します。そして、このシリーズの次の記事では、クライアントとサーバー間でメッセージを送受信し、それをJSONファイルに保存する方法を案内します。

💡 JSONファイルはアプリケーションのデータベースとして機能します。この方法はデータを保存する安全な方法ではありませんが、デモ用なので、必要に応じて任意のデータベースを使用してください。

入札システムのワークフロー

各コンポーネントを構築する前に、アプリケーションのワークフローについて案内します。

以下のように動作します:

  • ホームページ:ユーザーはユーザー名のみを提供し、このユーザー名はアプリケーション全体で識別のために保存されます。チュートリアルをシンプルに保つために、どんな認証ライブラリも使いません。
  • 商品ページ:ユーザーはオークションに出品されているすべての商品を閲覧し、各商品をクリックして入札でき、また、オークションのための商品を追加するページへのリダイレクトを行う呼び出しアクションがあります。
  • 商品追加ページ:このページでは、ユーザーがオークションの商品名と価格を追加でき、最近追加された商品を表示するために商品ページにリダイレクトされます。
  • 入札ページ:ユーザーは商品ページから選んだ商品に対して入札できます。このページは選択した商品の名前と価格を含むURLパラメーターを受け入れ、ユーザーが商品の価格を引き上げるためのフォームインプットを表示します。
  • ナビコンポーネント:すべてのページの上部にナビコンポーネントがあり、通知が表示されます。ユーザーが入札を行うか新しい商品を追加すると、ナビコンポーネントは他のすべてのユーザーに通知します。

ナビ

これ以上の迷いなく、すべてのページを含むコンポーネントフォルダを作成し、各ページがHTML要素をレンダーすることを確認します。

cd src
mkdir components
cd components
touch Home.js Products.js AddProduct.js BidProduct.js Nav.js

次に、コンポーネントフォルダ内のすべてのファイルをApp.jsファイルにインポートし、React Router v6を使用して各ページのルートを作成します。

//Pages import
import Home from './components/Home';
import AddProduct from './components/AddProduct';
import BidProduct from './components/BidProduct';
import Products from './components/Products';
import Nav from './components/Nav';
import socketIO from 'socket.io-client';
import { Route, Routes, BrowserRouter as Router } from 'react-router-dom';

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

function App() {
  return (
    <Router>
      <div>
        {/* すべてのページの上部にNavがナビゲーションバーとして存在 */}
        <Nav socket={socket} />
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/products" element={<Products />} />
          <Route
            path="/products/add"
            element={<AddProduct socket={socket} />}
          />
          {/* 動的ルーティングを使用 */}
          <Route
           <br><br>こちらの記事はdev.toの良い記事を日本人向けに翻訳しています。<br>[https://dev.to/novu/how-to-build-a-real-time-auction-system-with-socketio-and-reactjs-3ble](https://dev.to/novu/how-to-build-a-real-time-auction-system-with-socketio-and-reactjs-3ble)