Polling
: 응답할 때까지 지속적으로 주기에 맞게 요청함
Long Polling
: 요청을 보내고 응답받을 때까지 연결을 끊지 않고 대기함
io
: 서버 측 객체socket
: 클라이언트 측 객체emit
: 송신 이벤트 발생on
: 수신 이벤트 등록connect
: 연결될 때 실행되는 이벤트connection
disconnect
: 연결이 끊길 때 실행되는 이벤트disconnecting
socket.io
에서 사용할 타입 ( 클라이언트와 서버 모두 공유해서 사용 )// 여기 정의한 타입은 모두 필요에 의해서 수정해서 사용해야 한다.
type MessageReceiveAndSend = {
message: string;
};
// 이벤트 수신 시 사용 ( 서버 -> 클라이언트 )
export type ServerToClientEvents = {
onReceive: ({ message }: MessageReceiveAndSend) => void;
};
// 이벤트 송신 시 사용 ( 클라이언트 -> 서버 )
export type ClientToServerEvents = {
onSend: ({ message }: MessageReceiveAndSend) => void;
};
// 서버측 socket.io("")
// ?
export type InterServerEvents = {
ping: () => void;
};
// 서버측 socket.data ( socket 객체들이 공유할 데이터 )
export type SocketData = {
name: string;
age: number;
};
npm i express socket.io
npm i -D types/express types/socket.io
app.ts
// ClientToServerEvents, ServerToClientEvents 등은 import 했다고 가정
import express from "express";
import { createServer } from "http";
import { Server } from "socket.io";
const app = express();
app.set("PORT", 8080);
const httpServer = createServer(app);
const io = new Server<
ClientToServerEvents,
ServerToClientEvents,
InterServerEvents,
SocketData
>(httpServer, {
// options ...
cors: {
origin: "http://localhost:3000",
methods: ["GET", "POST"],
credentials: true,
},
});
// 미들웨어 등록 ( 들어오는 모든 연결 이전에 실행 )
// 여러 개의 미들웨어를 등록할 수 있으며, 이전 미들웨어에서 에러가 발생한다면 이후 미들웨어는 모두 실행하지 않음
io.use((socket, next) => {
try {
console.log("미들 웨어 실행!!");
socket.data.age = 25;
next();
} catch (error) {
console.error("error >> ", error);
next(new Error("socket.io middleware error"));
}
});
// 소켓에 연결될 경우 실행
io.on("connection", async (socket) => {
// 소켓마다 고유의 식별자를 가짐 ( 20자 )
console.log("소켓 연결 완료 >> ", socket.id);
// 연결된 소켓의 개수
console.log("연결 횟수 >> ", socket.engine.clientsCount);
// 소켓이 입장한 모든 방들 이름 ( 소켓 생성 시 각자의 식별자의 이름과 같은 방에 입장된다. ( 개인 메시지 구현에 이용하면 좋음 ) )
console.log("소켓이 들어간 방 >> ", socket.rooms); // ["식별자"]
// "room123"방에 입장
socket.join("room123");
// "room123"방에서 퇴장
socket.leave("room123");
// 응답 이벤트 등록... "on( 이벤트명, callback )" 순으로 입력 typescript를 사용한다면 자동 완성 지원
// ㉮ 클라이언트 측에서 "onSend"라는 응답을 보내면 callback에 전송된 인자가 매개변수로 들어가고 실행
socket.on("onSend", ({ message }) => {
console.log(message);
});
// ㉯ 전송 이벤트 실행... "emit( 이벤트명, 매개변수, 매개변수, ... )" 순으로 입력 typescript를 사용한다면 자동 완성 지원
// 원래는 특정 이벤트에 대한 응답으로 넣는 것이 일반적이기 때문에 위쪽 ㉮내부에서 사용하는 것이 더 맞는 예시지만 보기 더 편하게 하기 위해서 분리함
socket.emit("onReceive", { message: "서버에서 클라이언트로 메시지 전달!!!" });
// 응답 이벤트인 "onSend" 등록 해제
socket.off("onSend", (...args) => console.log(args));
// "room123"에 입장된 클라이언트들에게 "onReceive"이벤트 실행
socket.in("room123").emit("onReceive", { message: "메시지 전송 by in" });
socket.to("room123").emit("onReceive", { message: "메시지 전송 by to" });
// 본인을 제외한 클리이턴트들에게 "onReceive"이벤트 실행
socket.broadcast.emit("onReceive", { message: "본인 제외 메시지 전송 by broadcast" });
// 위의 in/to, broadcase, emit ( + of ) 등을 적절하게 조합해서 사용하면 된다.
});
httpServer.listen(app.get("PORT"), () => console.log(`${app.get("PORT")}번 대기중`));
npm i socket.io-client
npm i -D @types/socket.io-client
// ClientToServerEvents, ServerToClientEvents는 import했다고 가정
import React, { useEffect, useState } from "react";
import { io, Socket } from "socket.io-client";
function App() {
// socket을 담을 변수
const [socket, setSocket] =
useState<Socket<ServerToClientEvents, ClientToServerEvents>>();
// 서버와 연결
useEffect(() => {
// 연결 및 socket 생성
const socket: Socket<ServerToClientEvents, ClientToServerEvents> = io(
"http://localhost:5000",
{
withCredentials: true,
}
);
// 연결에 성공 시 실행
socket.on("connect", () => {
console.log("클라이언트 연결됨");
});
// "onReceive"로 요청오면 실행 ( 서버측의 ㉯로 전송 )
socket.on("onReceive", ({ message }) => {
console.log("서버에서 보낸 메시지 >> ", message);
});
// socket 등록
setSocket(socket);
}, [setSocket]);
// 전송 버튼 클릭 시 실행
const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
// "onSend"로 응답 ( 서버측의 ㉮에서 받음 )
socket?.emit("onSend", { message: "전송" });
};
return (
<main>
<h1>Socket.io</h1>
<form onSubmit={onSubmit}>
<button type="submit">전송</button>
</form>
</main>
);
}
export default App;
예시에서 보여준 것 외에도 많은 유용한 기능들을 구현해놔서 공식 문서 읽어보고 그대로 사용하면 됩니다.
<React.StrictMode>
를 제거하면 됩니다. ( 개발 모드일 경우 오류를 제대로 잡기 위해 두 번 실행됨 )