1. 소켓 polling Error

cors문제

Untitled

const dotenv = require('dotenv');
dotenv.config();
const { PORT } = process.env;
const { createServer } = require('http');
const { Server } = require('socket.io');
const { sequelize } = require('./models/index');
const path = require('path');
const express = require('express');

const connect = require('./schemas/index');
const cookieParser = require('cookie-parser');
const indexRouter = require('./routes/index');
const cors = require('cors');
const app = express();

// EJS 뷰 엔진 설정
app.set('views', path.join(__dirname, './views'));
app.set('view engine', 'ejs');

// 정적 파일 서빙
app.use(express.static(path.join(__dirname, 'views/client')));

// 몽고 DB 연결
connect();

// cors 미들웨어
app.use(
  cors({
    origin: process.env.NODE_ENV !== 'production' ? true : ['<http://13.124.233.17>', '<https://13.124.233.17>'],
  })
);
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use(cookieParser()); // 서버에서 클라이언트로 응답을 보낼 때, 쿠키를 설정하려면 Express 응답 객체(res.cookie())를 사용할 수 있도

// 정적 파일 서빙
app.use('/api/static', express.static(path.join(__dirname, 'static')));
app.use(express.static(path.join(__dirname, './views/client')));
// 라우터
app.use('/', indexRouter);
app.get('/client', (req, res) => {
  res.render('client');
});

// 에러처리 하는 미들웨어를 사용하는걸로 업그레이드 해보기..
app.get('*', (req, res) => {
  res.status(404).send({
    reqURL: req.url,
    msg: '요청경로를 찾을 수 없습니다.',
  });
});
const httpServer = createServer(app);
const io = new Server(httpServer, {
  cors: { origin: process.env.NODE_ENV !== 'production' ? true : ['<http://13.124.233.17>', '<https://13.124.233.17>'] },
});

// 소켓 커넥션 연결
require('./sockets/io')(io); //채팅
require('./sockets/timer')(io); //타이머
require('./sockets/notification')(io); //알림

// 서버 연결
sequelize.sync({ force: false }).then(() => {
  app.listen(PORT, () => {
    console.log(`server open on port ${PORT}`);
  });
});

디벨롭 과정에서 기존의 socket 서버와 api서버를 합치게 되면서 cors문제가 발생해서 같은 포트 번호를 쓰고 있음에도 소켓 연결이 되지 않는 polling 에러가 발생함

서버를 생성하고 시작하는 방법에서 원본 코드에서 app.listen(PORT, …)’를 사용했다면, 업데이트된 코드에서는 ‘httpServer.listen(PORT,…)’를 사용했다.

‘http’모듈의 ‘createServer’를 사용하여 명시적으로 HTTP서버를 생성하고, 이 서버를 사용하여 연결을 수신하도록 하였다.⇒ socket.io의 작동방식과 관련이 있는데, socket.io는 기존 HTTP서버와 함께 작동할 수 있다. socket.io서버를 만들 때 const io=new Server(httpServer) 기존의 HTTP 서버에 첨부한다.

그래서!!! Express와 socket.io가 동일한 HTTP서버 인스턴스를 사용하도록 한다.

const dotenv = require('dotenv');
dotenv.config();
const { PORT } = process.env;
const { createServer } = require('http');
const { Server } = require('socket.io');
const { sequelize } = require('./models/index');
const path = require('path');
const express = require('express');

const connect = require('./schemas/index');
const cookieParser = require('cookie-parser');
const indexRouter = require('./routes/index');
const cors = require('cors');
const app = express();

// EJS 뷰 엔진 설정
app.set('views', path.join(__dirname, './views'));
app.set('view engine', 'ejs');

// 정적 파일 서빙
app.use(express.static(path.join(__dirname, 'views/client')));

// 몽고 DB 연결
connect();

// cors 미들웨어
app.use(
  cors({
    origin: process.env.NODE_ENV !== 'production' ? true : ['<http://13.124.233.17>', '<https://13.124.233.17>'],
  })
);
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use(cookieParser());

// 정적 파일 서빙
app.use('/api/static', express.static(path.join(__dirname, 'static')));
app.use(express.static(path.join(__dirname, './views/client')));

// 라우터
app.use('/', indexRouter);
app.get('/client', (req, res) => {
  res.render('client');
});

// 에러처리 하는 미들웨어를 사용하는걸로 업그레이드 해보기..
app.get('*', (req, res) => {
  res.status(404).send({
    reqURL: req.url,
    msg: '요청경로를 찾을 수 없습니다.',
  });
});

const httpServer = createServer(app);
const io = new Server(httpServer);

// 소켓 커넥션 연결
require('./sockets/io')(io); //채팅
require('./sockets/timer')(io); //타이머
require('./sockets/notification')(io); //알림

// 서버 연결
sequelize.sync({ force: false }).then(() => {
  httpServer.listen(PORT, () => {
    console.log(`server open on port ${PORT}`);
  });
});

Untitled

해결!

// 원본 코드
app.listen(PORT, () => {
console.log(server open on port ${PORT});
});
// 업데이트된 코드
httpServer.listen(PORT, () => {
console.log(server open on port ${PORT});
});

2. mongoDB change stream

const { Notification } = require('../schemas/schema');
module.exports = function (io) {
  //Notification 등록시 소켓 이벤트 발생
  const notificationChangeStream = Notification.watch([], { fullDocument: 'updateLookup' });

  // $match stage를 사용하여 notification_kind가 'new_notification'인 변경 사항만 감지
  const matchStage = {
    $match: {
      'fullDocument.notification_kind': 'new_notice',
      operationType: 'insert',
    },
  };
  notificationChangeStream.on('change', change => {
    if (change.operationType === 'insert') {
      const newNotification = change.fullDocument;
      io.emit('newNotice', newNotification);
    }
  });
};

공지사항이 추가되면 몽고db의 notification 스키마에도 notification_kind에 ‘new_notice’라는 스트링으로 notification 종류의 document가 생성된다.

새로운 document가 생성될 때, 이를 감지하고 소켓이벤트를 발생시키기 위해서 mongoDB의 Change Streams라는 기능을 사용해보았다.

⇒ ‘watch’라는 메서드를 사용해서 컬렉션에서 변경사항을 감지할 수 있다!

change Stream 설정