여러 챗봇과 실시간 채팅을 만들어야 했다.
기획이 굉장히 납득가지 않았지만, 기획자는 기획자의 일이 있는거니까 일단 이해하고 만들었다..
socket.model.ts
const socketConnect = (httpServer: any) => {
const ioConnect = new socketIo.Server(httpServer, {
cors: {
origin: '*',
methods: ['GET', 'POST'],
credentials: true,
},
});
const io = ioConnect.of('/socket/chat');
return io;
};
export = socketConnect;
소켓을 여는 함수를 만든다.
app.ts
import express from 'express';
import { createServer } from 'http';
const app = express();
const httpServer = createServer(app);
const io = socketConnect(httpServer);
const socketCheckInterceptor: RequestHandler = async (req, res, next) => {
const originUrl = req.originalUrl;
const originUrlSplit = originUrl.split('/');
if (특정 조건) res.locals.socketIo = io;
if (특정 조건) res.locals.socketIo = io;
return next();
};
app.use(socketCheckInterceptor);
app.use('/', routes);
httpServer.listen(process.env.PORT, () => {
console.log(`🛡️ Server listening on port ${procee.env.PORT}`);
});
만든 함수를 app에서 불러오고 socket을 res.locals에 특정 상황일 때만 집어넣어주고 리턴시킨다.
socket.module.ts
const socketIoModule = {
emit: async (io: any, room: string, emit: string, content: { [key: string]: any }) => {
io.in(room).socketsJoin(room);
io.to(room).emit(emit, content);
},
count: async (io: any, room: string) => {
const count = io.engine.clientsCount;
return count;
},
leave: async (io: any, room: string) => {
return true;
},
};
export = socketIoModule
쓸 것들만 대강 모듈화 시켜놓았다.
실제로 쓸 때는 이것보단 세분화 해놨다.
chat.controller.ts
const createBattleChatController: RequestHandler = async (req, res, next) => {
try {
const memberInfo: MemberInfo = res.locals.member;
const { battleInviteCode, message, imageYn, socketId } = req.body;
const result = await createBattleChatService(
battleInviteCode,
message,
imageYn,
memberInfo.member_idx,
memberInfo.profile_img,
memberInfo.member_name,
res.locals.socketIo,
socketId,
);
if (!result) res.locals.responseResult = { msg: 'noneInviteCode', status: 'err' };
else res.locals.responseResult = { msg: 'success' };
return next();
} catch (err) {
res.locals.errorContent = err;
return errorGlobalFilter(req, res, next);
}
};
app에서 받아온 io를 이용해 서비스로 넘겼다..
chat.service.ts
const chatService = {
createChat: async (
roomName: string,
message: string,
io: any,
socketId: string,
) => {
const sockets = await io.in(roomName).fetchSockets();
if (sockets.length < 1) return false;
await io.in(socketId).socketsJoin(roomName);
await io.to(roomName).emit('emitName', content);
await createChatMessageToDynamoDb(battleInviteCode, message, imageYn, memberIdx, 1);
return true;
}},
실제 프로덕트에선 이 안에서 분기처리가 어..엄청나게.. 많다.. 눈물없인 볼 수가 없다..
일단 만든 채팅은 aws dynamodb에 집어 넣었다..
그리고 전체 접속 중 클라이언트는 알 수 있어도, 특정 room에 접속 중인 클라이언트의 수를 파악하는게, 어떤 레퍼런스를 봐도 명쾌하게 떨어지는 것이 없었다..
공식문서를 뒤져도 안나왔다..
여튼 io.in(방이름).fetchSockets() 의 길이로 해당 방에 접속 중인 소켓id를 파악할 수가 있었다..
여기서 문제는 방을 나가도 소켓이 연결되어 있는 경우가 있었는데,
소켓을 끄거나 백그라운드로 가거나 등, 소켓을 활용하지 않을 때
await io.in(socketId).socketsLeave(RoomName);
이렇게 방을 나가게 해주면 정확하게 파악할 수 있다.
사이드에서 몇번 써봤긴 했지만,
실프로덕트에서 챗봇이랑 채팅 알람 등 여러 상황에 맞게끔 구현해내는게 쉽지 않았다.
살려줘
'Back > Node.js' 카테고리의 다른 글
[NestJs, Prisma] Prisma CRUD (0) | 2022.07.19 |
---|---|
[Nodejs] Slack Message 슬렉 메시지 보내기 slack-freinds (0) | 2022.06.23 |
[Node.js] Express-typescript 전환 (0) | 2022.05.27 |
[Node.js] FCM Notification 알람 자동화 작업 (0) | 2022.05.01 |
[Node.js, Redis] Nodejs AWS Elasticache Redis (0) | 2022.04.14 |