Next.js+Express+Socket.ioを使ってソケット通信を行う

# 初めに
研究でソケット通信を行う必要があったので、Socket.ioを使って実装しました。
クライアントはNext.js、サーバーはExpressを用いています。

この記事は、Next,jsとExpressを使ったことがある人を対象としています。

今回の記事は以下のサイトを参考にしています。
[Socket.IO + Express + TypeScriptでリアルタイム通信を始めよう](https://bgtu-techblog.com/2022/11/25/socket-io-express-typescript%E3%81%A7%E3%83%AA%E3%82%A2%E3%83%AB%E3%82%BF%E3%82%A4%E3%83%A0%E9%80%9A%E4%BF%A1%E3%82%92%E5%A7%8B%E3%82%81%E3%82%88%E3%81%86/ "Socket.IO + Express + TypeScriptでリアルタイム通信を始めよう")


# Socket.ioとは？
WebSocket技術によるリアルタイム双方向通信処理を簡単に実装するためのnode.js用サーバー側ライブラリとブラウザ用JavaScriptライブラリ。

# モデルの実装
今回はTypeScriptを使っているので、事前に型定義が必要となります。
そのため、サーバー、クライアント共に使えるモデルを作成しておきます。
```model.ts
//イベントの送信およびブロードキャスト時に使用される型定義
export type ServerToClientEvents = {
  hello: (message: string) => void;
};

//イベント受信時に使用する型定義
export type ClientToServerEvents = {
  message: (message: string) => void;
  player:(name:string) => void;
};
```

# サーバーサイドの実装
まず初めに以下のパッケージをインストールします
```
npm install typescript express socket.io ts-node
npm install -D @types/express @types/node
```
次にコードを記述します。
```server.ts
import express from "express";
import { createServer } from "http";
import { Server } from "socket.io";
//モデル読み込み
import { ClientToServerEvents, ServerToClientEvents } from "./models";

const app = express();
const httpServer = createServer(app);
const io = new Server<ClientToServerEvents, ServerToClientEvents>(httpServer,
//CORS対策
{
  cors: {
    origin: ["http://localhost:3000"],
  },
});
// コネクション確立
io.on("connection", (socket) => {
  console.log(socket.id);

  // イベント発行
  socket.emit("hello", "from server");

  // イベント受信
  socket.on("message", (message) => {
    console.log(`from client: ${message}`);
  });

  // 切断イベント受信
  socket.on("disconnect", (reason) => {
    console.log(`user disconnected. reason is ${reason}.`);
  });
});

// サーバーを5000番ポートで起動
httpServer.listen(5000, () => {
  console.log("Server start on port 5000.");
});
```
参考記事にはCORS対策が記載されていなかったので、追加してあります。
CORSについて知りたい方は以下のサイトをご覧ください。
[オリジン間リソース共有 (CORS)](https://developer.mozilla.org/ja/docs/Web/HTTP/CORS "オリジン間リソース共有 (CORS)")

次に起動用コマンドを`package.json`に記述します
```package.json
"scripts": {
  "server": "ts-node server.ts"
}
```
`npm run server`で実行できます。

:::note warn
警告
MacOS Monterey 12以降 、ポート5000を利用しようとすると`Address already in user`が発生することがあります。これは、「AirPlayサーバー」がポート5000を利用していることにより発生する問題です。設定 > Airplayから「AirPlayレシーバー」のチェックを外してください。その後、ターミナルで`lsof -i:5000`を実行後、`kill -9 表示されたPID`を実行してください。
:::

# クライアントサイドの実装
Next.jsのプロジェクトは既に作成してあるものとして進めます。
パッケージをインストール
```
npm install socket.io-client
```
`pages/index.tsx　`にコードを追加します。(追加したコードのみ記載)
```index.tsx
//パスは変更してください
import { ClientToServerEvents, ServerToClientEvents } from "../src/models";
import { io, Socket } from "socket.io-client";

export default function Home(){
  useEffect(()=>{

    const socket: Socket<ServerToClientEvents, ClientToServerEvents> = io(
      // サーバーのURLを指定
      "http://localhost:5000"
    );

    socket.on("connect", () => {
      console.log(socket.connected);
    });
    
    socket.on("hello", (message) => {
      console.log(message);
    });
    
    socket.emit("message", "hello world");

  },[])
}
```
`npm run dev`で実行できます。

# 終わりに
Socket.ioを使えば非常に簡単にソケット通信ができるので、ぜひ使ってみてください。
CORS対策はお忘れないように！