Next.js + Socket.ioチャット機能を実現1編
Next.jsベースのパーソナルプロジェクトで
実装に先立ち,チャットルームUI,DBに格納されたChat構造などを考慮したが,実装後も引き続き修正を行う.第1話では,1パスに接続されたユーザがチャットで情報を送受信できる程度のコンテンツを実現する.
第2編では、MongoDBに基づいてチャットルームを作成し、参加してみます.
Socket.io
応答タイプ設定 socket.io接続 ルーティング接続
Next.jsにおけるrequestおよびresponoeのタイプは、主に
現在のプロジェクトのサーバとsocket.ioを接続します.
これで、/chatルーティングから接続されたユーザーにチャット情報を送信する応答コードを作成できます.
Client
socket.io
を用いてチャット機能を実現した.想像以上に簡単なコード実装と応用で驚きました!👀実装に先立ち,チャットルームUI,DBに格納されたChat構造などを考慮したが,実装後も引き続き修正を行う.第1話では,1パスに接続されたユーザがチャットで情報を送受信できる程度のコンテンツを実現する.
第2編では、MongoDBに基づいてチャットルームを作成し、参加してみます.
Socket.io
JSライブラリは、すべてのプラットフォームの双方向および低遅延通信をサポートします.Webクライアントとサーバ間のリアルタイム双方向通信を可能にする.
ブラウザで実行されるクライアントライブラリとノード.jsには2つのサーバ側リポジトリがあり、2つのコンポーネントのAPIはほぼ同じです.
Server
サーバ側の構成は大きく3つに分けられます.
サーバ側の構成は大きく3つに分けられます.
1.応答タイプの設定
Next.jsにおけるrequestおよびresponoeのタイプは、主に
NextApiRequest
およびNextApiResponse
を用いる.ここはソケットioリクエストに対してresonseタイプを個別に設定できます.types/chat.d.ts
import { Server as NetServer, Socket } from "net";
import { NextApiResponse } from "next";
import { Server as SocketIOServer } from "socket.io";
export type NextApiResponseServerIO = NextApiResponse & {
socket: Socket & {
server: NetServer & {
io: SocketIOServer;
};
};
};
2. socket.接続io
現在のプロジェクトのサーバとsocket.ioを接続します.
/pages/api/chat/socketio.ts
import { NextApiRequest } from "next";
import { NextApiResponseServerIO } from "../../../types/chat";
import { Server as ServerIO } from "socket.io";
import { Server as NetServer } from "http";
export const config = {
api: {
bodyParser: false,
},
};
export default async (req: NextApiRequest, res: NextApiResponseServerIO) => {
if (!res.socket.server.io) {
console.log("New Socket.io server...✅");
const httpServer: NetServer = res.socket.server as any;
const io = new ServerIO(httpServer, {
path: "/api/chat/socketio",
});
res.socket.server.io = io;
}
res.end();
};
3.ルーティング接続
これで、/chatルーティングから接続されたユーザーにチャット情報を送信する応答コードを作成できます.
/pages/api/chat/index.ts
import { NextApiRequest } from "next";
import { NextApiResponseServerIO } from "../../../types/chat";
export default async (req: NextApiRequest, res: NextApiResponseServerIO) => {
if (req.method === "POST") {
const message = req.body;
res.socket.server.io.emit("message", message);
res.status(201).json(message);
}
};
サーバー側の構成が完了しました.クライアントsocketです.io構成は/api/chat/socketio
パスに接続され、クライアントルーティングで発生したsocketイベントを応答として受信し、出力する.Client /chat
パス出力の<Chatting.tsx>
ファイルを生成し、コードを記述する.
components/views/chat/Chatting.tsx
import React, { useState, useRef, useEffect, useCallback } from "react";
import { useSelector } from "../../../store";
import axios from "../../../lib/api";
// * Socket.io
import SocketIOClient from "socket.io-client";
// * MUI
import { Stack, TextField, Alert, Button, Paper } from "@mui/material";
import SendIcon from "@mui/icons-material/Send";
interface IMessage {
user: string;
message: string;
}
const Chatting: React.FC = () => {
const [sendMessage, setSendMessage] = useState<string>("");
const [connected, setConnected] = useState<boolean>(false);
const [chat, setChat] = useState<IMessage[]>([]);
const username = useSelector((state) => state.user.name);
useEffect((): any => {
// connect to socket server
const socket = SocketIOClient.connect(process.env.NEXT_PUBLIC_API_URL, {
path: "/api/chat/socketio",
});
// log socket connection
socket.on("connect", () => {
console.log("SOCKET CONNECTED!", socket.id);
setConnected(true);
});
// update chat on new message dispatched
socket.on("message", (message: IMessage) => {
chat.push(message);
setChat([...chat]);
});
// socket disconnect on component unmount if exists
if (socket) return () => socket.disconnect();
}, []);
const sendMessageHandler = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
setSendMessage(event.target.value);
},
[sendMessage]
);
const enterKeyPress = (event: React.KeyboardEvent<HTMLInputElement>) => {
if (event.key === "Enter" && !event.shiftKey) {
// send message
event.preventDefault();
submitSendMessage(event);
}
};
const submitSendMessage = async (
event: React.FormEvent<HTMLButtonElement>
) => {
event.preventDefault();
if (sendMessage) {
const message: IMessage = {
user: username,
message: sendMessage,
};
const response = await axios.post("/api/chat", message);
setSendMessage("");
}
};
return (
<>
<Stack spacing={2} direction="column">
<Alert severity="info">
채팅 기능은 로그인된 유저에게만 제공됩니다.
</Alert>
{/* 채팅 메시지 출력 영역 */}
<Stack spacing={2} direction="column">
<Paper variant="outlined" sx={{ minHeight: "300px" }}>
{chat.length ? (
chat.map((chat, index) => (
<div className="chat-message" key={index}>
{chat.user === username ? "Me" : chat.user} : {chat.message}
</div>
))
) : (
<div className="alert-message">No Chat Messages</div>
)}
</Paper>
</Stack>
{/* 채팅 메시지 입력 영역 */}
<Stack spacing={1} direction="row">
<TextField
id="chat-message-input"
label="enter your message"
variant="outlined"
value={sendMessage}
onChange={sendMessageHandler}
margin="normal"
autoFocus
multiline
rows={2}
fullWidth
onKeyPress={enterKeyPress}
placeholder={connected ? "enter your message" : "Connecting...🕐"}
/>
<Button
type="submit"
variant="contained"
color="primary"
endIcon={<SendIcon />}
onClick={submitSendMessage}
>
Send
</Button>
</Stack>
</Stack>
</>
);
};
export default Chatting;
useEffect
を介してクライアントが/chat
ルーティングにアクセスしたときにsocketに接続する.生成されたメッセージがある場合、サーバは応答として送信し、受信したメッセージ情報はuseState
を介して保存され、チャットウィンドウに更新された情報が出力されます.
ユーザが/chat
ルーティングを離れると、cleanup関数によってsocket接続が無効になります.
次のjsを起動し、ブラウザでテストします.
左側のChromeブラウザではTester 1ユーザーが接続されています.右側のSafariブラウザではTester 2ユーザーが接続されています.
チャットメッセージ出力UIコンポーネントは構成されていないため、ユーザー名とメッセージのみが出力されます.
整理する
socket.io構成サーバとクライアントコードによってパスが接続され、このパスに接続されたルーティングは、ユーザが作成および送信したコンテンツをリアルタイムで受信することができる.
ソケットサーバに接続し、メッセージを送信するには、socketが提供するon
、emit
の方法を使用します.
第2編では、MongoDBでチャットドキュメントを作成し、チャットルーム別にチャットコンテンツを格納し、チャットコンテンツのロードと出力を試みます.👋
Reference
この問題について(Next.js + Socket.ioチャット機能を実現1編), 我々は、より多くの情報をここで見つけました
https://velog.io/@carrot/Next.js-Socket.io-채팅-기능-구현하기-1편
テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol
import React, { useState, useRef, useEffect, useCallback } from "react";
import { useSelector } from "../../../store";
import axios from "../../../lib/api";
// * Socket.io
import SocketIOClient from "socket.io-client";
// * MUI
import { Stack, TextField, Alert, Button, Paper } from "@mui/material";
import SendIcon from "@mui/icons-material/Send";
interface IMessage {
user: string;
message: string;
}
const Chatting: React.FC = () => {
const [sendMessage, setSendMessage] = useState<string>("");
const [connected, setConnected] = useState<boolean>(false);
const [chat, setChat] = useState<IMessage[]>([]);
const username = useSelector((state) => state.user.name);
useEffect((): any => {
// connect to socket server
const socket = SocketIOClient.connect(process.env.NEXT_PUBLIC_API_URL, {
path: "/api/chat/socketio",
});
// log socket connection
socket.on("connect", () => {
console.log("SOCKET CONNECTED!", socket.id);
setConnected(true);
});
// update chat on new message dispatched
socket.on("message", (message: IMessage) => {
chat.push(message);
setChat([...chat]);
});
// socket disconnect on component unmount if exists
if (socket) return () => socket.disconnect();
}, []);
const sendMessageHandler = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
setSendMessage(event.target.value);
},
[sendMessage]
);
const enterKeyPress = (event: React.KeyboardEvent<HTMLInputElement>) => {
if (event.key === "Enter" && !event.shiftKey) {
// send message
event.preventDefault();
submitSendMessage(event);
}
};
const submitSendMessage = async (
event: React.FormEvent<HTMLButtonElement>
) => {
event.preventDefault();
if (sendMessage) {
const message: IMessage = {
user: username,
message: sendMessage,
};
const response = await axios.post("/api/chat", message);
setSendMessage("");
}
};
return (
<>
<Stack spacing={2} direction="column">
<Alert severity="info">
채팅 기능은 로그인된 유저에게만 제공됩니다.
</Alert>
{/* 채팅 메시지 출력 영역 */}
<Stack spacing={2} direction="column">
<Paper variant="outlined" sx={{ minHeight: "300px" }}>
{chat.length ? (
chat.map((chat, index) => (
<div className="chat-message" key={index}>
{chat.user === username ? "Me" : chat.user} : {chat.message}
</div>
))
) : (
<div className="alert-message">No Chat Messages</div>
)}
</Paper>
</Stack>
{/* 채팅 메시지 입력 영역 */}
<Stack spacing={1} direction="row">
<TextField
id="chat-message-input"
label="enter your message"
variant="outlined"
value={sendMessage}
onChange={sendMessageHandler}
margin="normal"
autoFocus
multiline
rows={2}
fullWidth
onKeyPress={enterKeyPress}
placeholder={connected ? "enter your message" : "Connecting...🕐"}
/>
<Button
type="submit"
variant="contained"
color="primary"
endIcon={<SendIcon />}
onClick={submitSendMessage}
>
Send
</Button>
</Stack>
</Stack>
</>
);
};
export default Chatting;
socket.io構成サーバとクライアントコードによってパスが接続され、このパスに接続されたルーティングは、ユーザが作成および送信したコンテンツをリアルタイムで受信することができる.
ソケットサーバに接続し、メッセージを送信するには、socketが提供する
on
、emit
の方法を使用します.第2編では、MongoDBでチャットドキュメントを作成し、チャットルーム別にチャットコンテンツを格納し、チャットコンテンツのロードと出力を試みます.👋
Reference
この問題について(Next.js + Socket.ioチャット機能を実現1編), 我々は、より多くの情報をここで見つけました https://velog.io/@carrot/Next.js-Socket.io-채팅-기능-구현하기-1편テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol