본문으로 바로가기

앞서 가장 기본적인 핑퐁 서버에서는 다음과 같이 작성한 바 있습니다만 더 다양한 기능을 부여하기에는 부족했습니다.

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이 실행될 때마다 실행되는 미들웨어라고 한다.

 


darren, dev blog
블로그 이미지 DarrenKwonDev 님의 블로그
VISITOR 오늘 / 전체