メインコンテンツにスキップ

Next.js での使用法

このガイドでは、Next.js アプリケーション内で Socket.IO を使用する方法を説明します。

サーバー

Socket.IO サーバーは、Next.js と同じ基盤となる HTTP サーバーを共有できます。プロジェクトのルートに 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;
// when using middleware `hostname` and `port` must be provided below
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) => {
// ...
});

httpServer
.once("error", (err) => {
console.error(err);
process.exit(1);
})
.listen(port, () => {
console.log(`> Ready on http://${hostname}:${port}`);
});
});

server.js ファイルはアプリケーションのエントリポイントになります。

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

これで完了です!

参考: https://nextjs.dokyumento.jp/docs/pages/building-your-application/configuring/custom-server

ヒント

これは、App Router と Pages Router の両方で動作します。

注意

Next.js のドキュメントより

  • カスタムサーバーを使用する前に、Next.js の統合ルーターがアプリの要件を満たせない場合にのみ使用することを検討してください。カスタムサーバーを使用すると、サーバーレス関数や自動静的最適化などの重要なパフォーマンス最適化が無効になります。
  • カスタムサーバーは、Vercel にデプロイできません。
  • スタンドアロン出力モードでは、カスタムサーバーファイルはトレースされず、代わりに個別の最小限の server.js ファイルが出力されます。

クライアント

クライアント側では、React ガイド のすべてのヒントが有効です。

唯一の違いは、サーバーサイドレンダリング (SSR) から Socket.IO クライアントを除外する必要があることです。

構造

├── src
│ ├── app
│ │ └── page.js
│ └── socket.js
└── package.json
src/socket.js
"use client";

import { io } from "socket.io-client";

export const socket = io();
注記

"use client" は、ファイルがクライアントバンドルの一部であり、サーバーレンダリングされないことを示します。

参考: https://nextjs.dokyumento.jp/docs/app/building-your-application/rendering/client-components

src/app/page.js
"use client";

import { useEffect, useState } from "react";
import { socket } from "../socket";

export default function Home() {
const [isConnected, setIsConnected] = useState(false);
const [transport, setTransport] = useState("N/A");

useEffect(() => {
if (socket.connected) {
onConnect();
}

function onConnect() {
setIsConnected(true);
setTransport(socket.io.engine.transport.name);

socket.io.engine.on("upgrade", (transport) => {
setTransport(transport.name);
});
}

function onDisconnect() {
setIsConnected(false);
setTransport("N/A");
}

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

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

return (
<div>
<p>Status: { isConnected ? "connected" : "disconnected" }</p>
<p>Transport: { transport }</p>
</div>
);
}
注記

次のように使用することもできます。

const [isConnected, setIsConnected] = useState(socket.connected);

ではなく

const [isConnected, setIsConnected] = useState(false);

useEffect(() => {
if (socket.connected) {
onConnect();
}
// ...
});

しかし、これはクライアントレンダリングされたページがサーバーレンダリングされた出力と一致しない可能性があるため、Next.js コンパイラからいくつかの警告が発生します。

キャッチされないエラー: テキストコンテンツがサーバーレンダリングされた HTML と一致しません。

上記の例では、transport 変数は Socket.IO 接続を確立するために使用される低レベルのトランスポートであり、次のいずれかになります。

すべてがうまくいけば、次のように表示されるはずです。

Status: connected
Transport: websocket

その後、Socket.IO サーバーとクライアント間でメッセージを交換できます。

  • メッセージを送信するには socket.emit() を使用します。
socket.emit("hello", "world");
  • メッセージを受信するには socket.on() を使用します。
socket.on("hello", (value) => {
// ...
});

これで全部です。読んでいただきありがとうございます!

サンプル一覧に戻る