pusher 모듈을 대신하기 위한 웹 소켓 서버
pusher에 메세지 전송 기능만을 사용하고 있어서 Socket.io를 사용하여 직접 구현해 보기로 하였다.
클라이언트와 웹소켓 연결을 위한 라이브러리
index.js // express 서버구동
src
- api // socket API
- socket // socket 연결, init
- initializers // 공통모듈 (미들웨어 등등)
서버구동을 위하여 node의 프레임워크인 express를 사용하기로 하였다
소켓 API 사용을 위한 서버
Express - Node.js web application framework
index.js (express 실행 파일)
import express from 'express';
import socket from './src/socket/mainSocket';
import http from 'http';
import scoketio from 'socket.io';
const app = express();
const server = http.createServer(app);
const io = scoketio(server);
// 네임스페이스 이름
const Io = io.of('{{nameSpaceName}}');
// 해당 네임스페이스 기본 설정
app.use(socket({ Io, newRedis }));
// Router
app.use('/pusher', apiRouter({ io, newRedis }));
app.use('/other', otherRouter({ io, newRedis }));
server.listen(settings.PORT, (err) => {
if (err) throw err;
console.log(`listening on port ${settings.PORT}`);
});
index.js 에서 'app.use(socket({ Io, newRedis }));' 으로 원하는 소켓 서버를 설정 해준다.
/socket/mainSocket.js (socket)
import redis from '../config/redis';
const clients = {};
const socket = ({ Io, newRedis }) => {
Io.on('connection', (socket) => {
socket.on('login', (data) => {
// 로그인시 룸네임 설정
socket.join(`presence-${{UserRomName}}`, () => {
const [socketId, roomName] = Object.keys(socket.rooms);
const redisData = {
socketId,
roomName,
setDate: Date(),
user: data.user,
};
// 정보 확인을 위한 redis 저장
newRedis.set(`{{nameSpaceName}}-${data.user.m_no}-${socketId}`, JSON.stringify(redisData), 'ex', redis.redisConfig.ttl);
clients[socketId] = {
...data,
roomName
};
});
});
// ... 필요한 기능들을 추가 하시면 됩니다.
socket.on('disconnect', () => {
const data = clients[socket.id];
// 연결이 끊어졌을때 room , redis 삭제
socket.leave(`presence-${{UserRomName}}`);
newRedis.del(`{{nameSpaceName}}-${data.m_no}-${socket.id}`);
delete clients[socket.id];
});
socket.on('error', (err) => {
console.log('received error from socket:', socket.id);
});
});
return (req, res, next) => next();
};
export default socket;
pusher 의 기능을 대신하는 api로 사용
// API 주소 : {URL}/pusher/....
app.use('/pusher', apiRouter({ io, newRedis }));
// 다른 기능의 API필요할 경우
app.use('/other', otherRouter({ io, newRedis }));
해당 라우터에서 프론트로 전송처리
하나는 지정 룸 전송, 하나는 네임스페이스 전체에 처리
import express from 'express';
const apiRouter = (api) => {
const router = express.Router();
// roomName 메세지 전달
router.post('/sendMessage', (req, res, next) => {
/*
nameSpace
roomName
sendMessage
sendData
*/
const { data } = req;
api.io.of(data.nameSpace).to(data.roomName).emit(data.sendMessage, data.sendData);
res.json('success').end();
});
// nameSpace 전체 전송
router.post('/allMessage', (req, res, next) => {
const { data } = req;
api.io.of(data.nameSpace).emit(data.sendMessage, data.sendData);
res.json('success').end();
});
router.all('/*', (req, res) => {
res.status(404)
.json({ code: 404, message: '없는 API' })
.end();
});
return router;
};
export default apiRouter;
위의 설정으로 정상적으로 소캣서버를 구현하였다.
현재 프론트에서 angularjs 를 사용하고 있어 해당기준으로 생성하였다
bower sokcet.io-client
// 모듈 설치
$ bower install socket.io-client
// index.html
<script src="/socket.io/socket.io.js"></script>
socket.Io 서비스 생성
'use strict';
angular.module('App')
.factory('socketIo', function (userInfo) {
const m_no = userInfo.user.m_no;
return {
// 소켓 첫 연결
socketConnect: () => {
if (userInfo != {}) {
const socket = io.connect(`{{Your Socket server}}`);
socket.on('connect', () => {
socket.emit('login', userInfo);
});
socket.on('disconnect', () => {
console.log(`socket client disconnect...(ws-${m_no}-${socket.id})`);
});
return socket;
}
},
// 수신 메세지 설정
init: (socket) => {
socket.on('logout', (data) => {
if (data.m_no === m_no) {
// 확인 버튼 클릭하기전에 새로고침 할수도 있으니 토큰 먼저 지움
socket.emit('logout', userInfo);
}
});
//... pusher 에서 사용하던 기능들 등록
}
};
});
메시지를 추가해야 할 경우 새로운 메세지 이름을 설정하고 사용
socket.on('new_messasge_nema', (data) => {
// .. 기능
})
socket Initializer 생성
프론트에서 선언한 메세지명 전달 데이터로 소켓 서버에 request 호출하여 사용
// 소켓 서버 API 호출 (romm)
sendMessage: async (data) => {
const url = `${config.socket.url}/pusher/allMessage`;
const params = {
nameSpace,
roomName,
sendMessage,
sendData
};
await api.request.sendUrlJson(
'POST',
url,
params
);
},
// 소켓 서버 API 호출 (전체)
sendAllMessage: async (data) => {
//..
}
이렇게 socket 기능을 구현해 보았다.