⚛️

Socket.IO の紹介と導入【Next.js】

に公開

はじめに

先日、Next.js の勉強会で、Socket.IO の実装について取り上げました 🫐

勉強会で作成したアプリのデモ

スクリーンショット

リアルタイムチャット、ライブ通知、作業やミーティングツールなど、
リアルタイム通信は、モダンな Web アプリケーションにおいて重要な要素です。

今回は、Socket.IO について調査したので、基礎的な内容をまとめました!

時間の節約になれば、嬉しいです 🙌

Socket.IO とは?

https://socket.io/

Socket.IO は、リアルタイムで双方向通信を簡単に実装できるライブラリです

チャットアプリや、ライブダッシュボードのような、
サーバーとクライアント間でリアルタイムにデータをやり取りする機能が、構築できます!

Socket.IO は WebSocket プロトコルを基盤としていますが、
ブラウザや環境によって最適な通信方式を自動的に選択してくれるのが、特徴の一つです。

そもそも、リアルタイム通信の WebSocket とは?

https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API

WebSocket とは、サーバーとクライアント間で双方向のリアルタイム通信を可能にするプロトコルです。

従来の HTTP 通信とは異なり、:

  • HTTP:1 つのリクエストに対して 1 つのレスポンス(単方向)
  • WebSocket:一度接続を確立すれば、双方向で自由にデータを送受信可能

WebSocket は、リアルタイム性が求められるアプリケーションに最適で、
チャットアプリやオンラインゲーム、ライブ配信などで広く使用されています。

しかし、
WebSocket はブラウザやネットワーク環境によって対応状況が異なるため、
純粋な WebSocket を使った開発は、複雑になることがあります!

Socket.IO の使い方

Socket.IO は、サーバーサイドとクライアントサイドの両方で、
イベントベースの通信を、簡単に実装できます。

基本的な概念として、emit(送信)と on(受信)があります:

emit と on の基本

  • emit: メッセージやデータを送信する
  • on: 特定のイベントを受信して処理する
// メッセージを送信
socket.emit("message", "Hello World!");

// メッセージを受信
socket.on("message", (data) => {
  console.log("受信:", data);
});

この簡単な API で、基本的なリアルタイム通信が実現できます 👍

Next.js への導入手順

https://socket.io/how-to/use-with-nextjs

さて、基本的な使い方を理解したところで、
Next.js への導入を確認してみましょう!

下記は、簡略化したサンプルコードです:

1. ライブラリのインストール

npm install socket.io socket.io-client

2. サーバーサイドの設定

Next.js では、カスタムサーバーを作成する必要があります。
プロジェクトルートに server.js を作成:

// server.js
import { createServer } from "node:http";
import next from "next";
import { Server } from "socket.io";

const dev = process.env.NODE_ENV !== "production";
const hostname = "localhost";
const port = 3000;

const app = next({ dev, hostname, port });
const handler = app.getRequestHandler();

app.prepare().then(() => {
  const httpServer = createServer(handler);
  const io = new Server(httpServer);

  io.on("connection", (socket) => {
    console.log("ユーザーが接続しました");

    // メッセージを受信
    socket.on("message", (msg) => {
      console.log("受信したメッセージ:", msg);
      // 全クライアントにブロードキャスト
      io.emit("message", msg);
    });

    socket.on("disconnect", () => {
      console.log("ユーザーが切断しました");
    });
  });

  httpServer.listen(port, () => {
    console.log(`> Ready on http://${hostname}:${port}`);
  });
});

3. package.json の更新

{
  "scripts": {
    "dev": "node server.js",
    "build": "next build",
    "start": "NODE_ENV=production node server.js"
  }
}

4. クライアントサイドの実装

// components/Chat.tsx
"use client";

import { useEffect, useState } from "react";
import { io } from "socket.io-client";

const socket = io();

export default function Chat() {
  const [message, setMessage] = useState("");
  const [messages, setMessages] = useState<string[]>([]);
  const [isConnected, setIsConnected] = useState(false);

  useEffect(() => {
    function onConnect() {
      setIsConnected(true);
    }

    function onDisconnect() {
      setIsConnected(false);
    }

    function onMessage(msg: string) {
      setMessages((prev) => [...prev, msg]);
    }

    socket.on("connect", onConnect);
    socket.on("disconnect", onDisconnect);
    socket.on("message", onMessage);

    return () => {
      socket.off("connect", onConnect);
      socket.off("disconnect", onDisconnect);
      socket.off("message", onMessage);
    };
  }, []);

  const sendMessage = () => {
    if (message.trim()) {
      socket.emit("message", message);
      setMessage("");
    }
  };

  return (
    <div style={{ padding: "20px" }}>
      <p>接続状態: {isConnected ? "接続中" : "切断中"}</p>

      <div style={{ marginBottom: "20px" }}>
        {messages.map((msg, index) => (
          <div key={index} style={{ marginBottom: "5px" }}>
            {msg}
          </div>
        ))}
      </div>

      <div>
        <input
          type="text"
          value={message}
          onChange={(e) => setMessage(e.target.value)}
          placeholder="メッセージを入力..."
          style={{ marginRight: "10px", padding: "5px" }}
        />
        <button onClick={sendMessage}>送信</button>
      </div>
    </div>
  );
}

このシンプルなコードで、リアルタイムチャット機能が実装できます!

もちろん、公式ドキュメントを参考にして、
さらに、部分的にカスタマイズしていくことが、可能です!

ただし、注意点もあります:

  • Socket.IO を Next.js で使用する場合、カスタムサーバーの作成が必要になります。
  • また、Vercel にはデプロイできない点に注意が必要です。
    • Vercel は WebSocket 接続をサポートしていないため

なぜ Socket.IO なのか?

個人的には、Socket.IO の魅力は、
リアルタイム通信の複雑さを隠蔽してくれる点にあると、考えています!

必要な機能を、簡単に実装できる点が、大きな魅力です:

  • 自動フォールバック:WebSocket が使えない環境では HTTP long-polling に自動切り替え
  • 自動再接続:接続が切れても自動的に再接続を試行
  • 豊富なエコシステム:様々なプラットフォームで利用可能

ただし、Next.js で使用する場合の注意点もあります:

  • カスタムサーバーが必要:Next.js の最適化機能の一部が使えなくなる
  • Vercel にデプロイ不可:WebSocket 接続がサポートされていない
  • サーバーレス環境不対応:永続的な接続を維持する必要がある

https://socket.io/docs/v4/

公式ドキュメントも充実しているのも、使いやすいポイントです!

おわりに

最後まで読んでいただき、ありがとうございます 🥳

ハンズオン形式で、
実際に手を動かしてリアルタイム実装を学習したい場合は、以下の教材もチェックしてみてください!!

https://zenn.dev/kazzyfrog/books/sakana-office

この記事が、少しでも参考になれば嬉しいです!

そして、もし、間違いや補足情報などがありましたら、ぜひコメントを追加してください!

Happy Hacking :)

参考

https://socket.io/how-to/use-with-nextjs
https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API
https://nextjs.org/docs/pages/building-your-application/configuring/custom-server
https://zenn.dev/knockknock/articles/518ce150336703
https://www.issoh.co.jp/tech/details/2990/

b13o Tech Blog

Discussion