앞서 가장 기본적인 핑퐁 서버에서는 다음과 같이 작성한 바 있습니다만 더 다양한 기능을 부여하기에는 부족했습니다.
const webSocket = require("./socket");
// listen
const server = app.listen(process.env.PORT, () => {
return console.log(`success on : http://localhost:${process.env.PORT}`);
});
// ws는 http와 동일 포트를 사용함
webSocket(server);
app.set()을 이용한 변수 설정
라우터에서 사용하기 위해서 필요한 작업입니다. 익스프레스 객체를 통째로 웹소켓에 넘겨줍니다.
const express = require("express");
const app = express();
// listen
const server = app.listen(PORT, () => {
return console.log(`success on : http://localhost:${process.env.PORT}`);
});
... 중략
// 웹소켓에 app을 넘겨줌
webSocket(server, app);
./socket.js
여기에서는 express 서버에서 전달받은 express 객체를 이용해 app.set("io", io)를 설정해줍니다.
이는 익스프레스에서 변수를 설정하는 방식입니다. 자세한 내용은 아래 공식 문서를 참고합시다.
(https://expressjs.com/en/api.html#app.set)
app.set('title', 'My Site')
app.get('title') // "My Site"
app.set("io", io)로 저장 후 컨트롤단에서 req.app.get("io")로 사용할 수 있게 됩니다.
const SocketIO = require("socket.io");
module.exports = (server, app) => {
const io = SocketIO(server, { path: "/socket.io" });
// express에서 변수를 저장하는 방법
// req.app.get("io")로 사용 가능합니다.
app.set("io", io)
... 이하 중략
이렇게 io 소켓 객체를 io라는 변수에 저장해두는 이유는 라우트에서 사용하기 위함입니다.
room이라는 네임 스페이스에세 emit 메서드를 사용한다고 가정한다면 다음과 같이 작성해야 합니다.
req.app.get("io").of("/room").emit(key, value)
이를 실제로 이용한 예시를 가져와보았습니다. POST /room을 날리고, 소켓 관련 로직을 처리하고 싶다면 다음과 같이 합니다.
router.post("/room", async (req, res, next) => {
try {
// mongoose를 이용해 데이터를 생성 후 저장
const room = new Room({
title: req.body.title,
max: req.body.max,
owner: req.session.color,
password: req.body.password,
});
const newRoom = await room.save();
// io 객체를 가져와서 room 네임스페이스에 newRoom 키를 가진 메세지를 emit합니다.
const io = req.app.get("io");
io.of("/room").emit("newRoom", newRoom);
// 소켓 통신을 처리했으니 리다이렉트처리합니다.
res.redirect(`/room/${newRoom._id}?password=${req.body.password}`);
} catch (error) {
console.error(error);
next(error);
}
});
router.delete("/room/:id", async (req, res, next) => {
try {
// 내용물 삭제
await Room.remove({ _id: req.params.id });
await Chat.remove({ room: req.params.id });
res.send("ok");
// 2초 후 removeRoom 키를 가진 이벤트를 작동시키도록.
setTimeout(() => {
req.app.get("io").of("/room").emit("removeRoom", req.params.id);
}, 2000);
} catch (error) {
console.error(error);
next(error);
}
});
io.use() : 미들웨어 (정확히는, express 미들웨어를 소켓IO에서 쓰는 법)
app.js에서 app.use()를 통해 미들웨어를 사용했듯이 socket.io에도 미들웨어를 사용할 수 있다. 여기서는 익스프레스 미들웨어를 socketIO에서 사용하는 방법을 알아보겠습니다. 그 중에서도 세션을 사용하기 위한 미들웨어 세팅을 해보도록합니다.
세션을 socket.js에서 사용하기 위해서는 우선 app.js의 미들웨어를 따로 분리해서 websocket에게 변수로 전달해야 한다. 여기서는 session을 분리해서 사용해보도록 하겠습니다.
// express 미들웨어 단
app.use(morgan("dev"));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(flash());
app.use(cookieParser(process.env.COOKIE_SECRET));
const sessionMiddleware = session({
resave: false,
saveUninitialized: false,
secret: process.env.COOKIE_SECRET,
cookie: {
httpOnly: true,
secure: false,
},
});
app.use(sessionMiddleware);
... 중략
// 위에서 설정한 미들웨어를 통째로 넘겨줍니다.
webSocket(server, app, sessionMiddleware);
분리 후에는 socket.js에서 io.use를 통해 미들웨어를 사용할 수 있습니다.
app.use로 express에서 미들웨어를 썼듯 소켓에서는 io.use를 사용한다고 보면 됩니다.
또, 소켓 통신에서는 req, res가 없고 socket 하나만 사용하므로 (없기 보다는 socket에서 빼다가 사용하는 거죠)
(socket, next) => ... 로 사용합시다.
물론 당연히 express 미들웨어로 사용하고 싶다면 (req, res,next) 사용하셔도 됩니다만 여기는 소켓을 사용하려는 거니까...
module.exports = (server, app, sessionMiddleware) => {
const io = SocketIO(server, { path: '/socket.io' });
app.set('io', io);
const room = io.of('/room');
const chat = io.of('/chat');
// socket 실행시 거쳐가는 미들웨어
io.use((socket, next) => {
sessionMiddleware(socket.request, socket.request.res, next);
});
... 중략
io.use 함수의 설명글을 읽어보면, Socket이 실행될 때마다 실행되는 미들웨어라고 한다.
'Node, Nest, Deno > ⚡ ws , socket.io' 카테고리의 다른 글
Socket.IO Namespace, Room (0) | 2020.04.07 |
---|---|
⚡ Socket.IO 간단한 개념 이해 및 ping pong 구현 (0) | 2020.04.04 |
ws 모듈 이용해 웹 소켓 맛보기 (0) | 2020.04.04 |