友人同士のバートとアーニーは、一緒に夢のサイドプロジェクトに取り組んでいます。それは、リモートで働くチームでリアルタイムの会話を可能にするチャットアプリです。フロントエンドのプログラマーであるバートは、バックエンドの専門家である友人に新しい機能を紹介しながら、興奮を抑えきれません。「見て!メッセージを入力すると、サーバーに送信され、ほぼ瞬時に応答がポップアップ表示されます!」バックエンドの第一人者であるアーニーは顔をしかめます。「しかし、2人が同時に会話しようとすると、どうしたら問題を解決できるのでしょうか?アプリは会話を「見る」代わりにページを更新し続けるため、リアルタイムでチャットすることはできません。」
問題は、Bert のアプリが HTTP 上で動作し、クライアントがサーバーからフェッチを要求する必要があることです。Ernie は次のようにまとめています。「友達全員がテキスト メッセージで返信しているのに、突然、新しいメッセージがあるかどうかを確認するために更新するまで返信がまったく表示されなくなるようなものです。更新するまでは最後のメッセージは表示されず、HTTP はこのように動作します。」
ここからが最高の部分です。リアルタイムで動作するフルスタック アプリには、WebSocket があります。この部分は非常に興味深いので、Ernie と Bert と一緒に、WebSocket の機能、実装手順、重要性の理由などについて学びましょう。
WebSocket は、クライアント (Web ブラウザ) とサーバー間の継続的な電話通話に似ています。これは、「メッセージを送信して、返信を待つ」という方法で機能する HTTP プロトコルとは異なります。WebSocket を使用すると、両者が自由に会話できます。WebSocket の機能は次のとおりです。
例: 絶えず更新されるスポーツのスコアボードを想像してください。HTTP では、クライアントは毎秒サーバーに「スコアは変わりましたか?」とスパム送信し続けなければなりません。WebSocket では、サーバーはスコアが変わるとすぐに「ゴール!」とアナウンスします。
バートは腰を下ろして頭を掻きます。「でも、HTTP はほとんどのアプリケーションで問題なく動作します。なぜゴールポストを動かすのですか?」アーニーは静かに笑い、マーカーを取り、ホワイトボードに何かを書き始めます。
「まずは最初から始めましょう」と彼は言います。「たとえば、スポーツのライブスコアなどのリアルタイムの最新情報です。クライアントはサーバーに毎秒『何か知りたいことはありますか?』と尋ねる必要があります。これはポーリングなので、30 秒ごとに『会議中ですか?』と尋ねる必要があります。馬鹿げているとは思いませんか? また、帯域幅も大量に消費します。」
WebSocket はこれを逆転させます:
「HTTP は、ブログや製品ページなどの静的なものを扱うのにかなり適しています。ただし、ライブ インタラクションの場合は、永続的な接続が必要です。WebSocket です。」
アーニーは眼鏡を拭きながら、「それでは、簡単なチャット アプリケーションを作ってみましょう」と声を張り上げます。「バックエンドには Node.js サーバー、フロントエンドには React アプリを用意します。準備はいいですか?」
「Socket.IO の部分で必要になるので、Node.js サーバーを入手してください」と Ernie は急いで次のように入力します。
npm init -y npm install express socket.io
サーバーコード (server.js):
const express = require('express'); const http = require('http'); const { Server } = require('socket.io'); const app = express(); const server = http.createServer(app); const io = new Server(server); // Serve static files (like React's build folder) app.use(express.static('public')); // Handle WebSocket connections io.on('connection', (socket) => { console.log('New user connected:', socket.id); // Listen for Incoming messages from the socket socket.on('sendMessage', (message) => { console.log('Received:', message); // Broadcast the message to everyone io.emit('receiveMessage', message); }); // Handle user disconnects events socket.on('disconnect', () => { console.log('User left:', socket.id); }); }); server.listen(3000, () => { console.log('Visit http://localhost:3000 on a browser, Server started successful on port 3000'); });
「Socket.IO の WebSocket 接続処理のおかげで、クライアントとサーバーは相互に通信できます。クライアントが (sendMessage) 関数を呼び出すと、サーバーは (io.emit) を介して接続している全員にメッセージを送信します。とても簡単ですよね。」
「今度は私の番です。React クライアントに取り掛かりましょう」と Bert は言います。
npx create-react-app chat-client cd chat-client npm install socket.io-client
チャットコンポーネント (Chat.js):
import { useState, useEffect } from 'react'; import io from 'socket.io-client'; // Connect to the server const socket = io('http://localhost:3000'); function Chat() { const [message, setMessage] = useState(''); const [messages, setMessages] = useState([]); useEffect(() => { // Listen for new messages socket.on('receiveMessage', (newMessage) => { setMessages([...messages, newMessage]); }); }, [messages]); const sendMessage = () => { if (message.trim()) { socket.emit('sendMessage', message); setMessage(''); } }; return ( <div style={{ padding: '20px' }}> <div> {messages.map((msg, index) => ( <p key={index}>{msg}</p> ))} </div> <input value={message} onChange={(e) => setMessage(e.target.value)} onKeyPress={(e) => e.key === 'Enter' && sendMessage()} /> <button onClick={sendMessage}>Send</button> </div> ); } export default Chat;
仕組み:
テストする:
これは Bert がアプリケーションに基づいて構築しようとしたものですが、Ernie は「実際のアプリケーションのほとんどは、多少のエラー処理と開発フレームワークを拡張する方法が必要です」と述べています。考えられる問題をいくつか見てみましょう。
React クライアントに再接続戦略を実装します。
socket.on('disconnect', () => { console.log('Disconnected. Trying to reconnect...'); socket.connect(); // Auto-reconnect });
認証された JWT トークンを使用して WebSocket を保護します。
// On login, send a token socket.emit('authenticate', { token: 'USER_JWT' }); // Server verifies token socket.on('authenticate', ({ token }) => { if (isValidToken(token)) { socket.authenticated = true; } else { socket.disconnect(); } });
Redis を使用して、異なるサーバー間でのメッセージングの受け渡しを導入します。
npm install redis
サーバーコード:
const redis = require('redis'); const subscriber = redis.createClient(); subscriber.subscribe('chat-channel'); subscriber.on('message', (channel, message) => { io.emit('receiveMessage', message); });
チャットを超えて: 共有ドキュメントの編集
これは Bert の次のアイデアです。Google Docs に似たエディターです。Ernie は、「Web ソケットを使用して、さまざまな人が行った変更を監視し、それらをマージすることができます」と述べています。
例えば :
ユーザーAは次のように入力します: 「こんにちは」
ユーザーBは「o」を削除し、「, World」を追加します。
サーバーは各キーストロークをリッスンし、それをすべてのクライアントにブロードキャストして、それぞれのクライアントが UPDATED ドキュメントをレンダリングできるようにします。
コードスニペット:
// Client sends keystrokes textarea.addEventListener('input', (e) => { socket.emit('textChange', e.target.value); }); // Server broadcasts changes socket.on('textChange', (text) => { io.emit('updateText', text); });
Ernie は次のように警告しています。「HTTPS/WSS なしで WebSocket を絶対に使用しないでください。」
const io = new Server(server, { cors: { origin: "https://yourdomain.com" }, });
XSS 攻撃につながる可能性のあるデータをサニタイズするために、すべての入力を検証します。
スパム行為を行うユーザーに対してレート制限を実装します。
Q: WebSocket の速度は HTTP よりもはるかに優れていますか?
A: はい。瞬時のアプリケーションの場合、ポーリングを実行する必要がないため、WebSocket が最適です。
Q: REST API を WebSocket と一緒に利用することは可能ですか?
A: はい、もちろんです! ライブ更新には WebSocket を使用し、CRUD 操作を実行するには REST を使用します。
Q: サーバーがクラッシュした場合はどう対処すればよいですか?
A: クライアントは自動的に再接続できます (上記の再接続ロジックを参照)。
Bert のチャット アプリは、リアルタイムの魅力で ping を鳴らすようになり、Ernie は満足そうに微笑んでいます。WebSocket はチャットだけでなく、ライブ ダッシュボード、ゲーム、IoT などの目的にも役立ちます。
あなたの番: さあ、例を試して、破壊して、何か新しいものを作ってみましょう。あなたのリアルタイム アプリが待っています!