본문 바로가기
Javascript

Visual Studio Node.js 1:1 채팅(2) Server.js

by NaHyungMin 2020. 7. 30.

2020/07/30 - [Linux] - Centos7 Node.js 배포하기

2020/07/30 - [Javascript] - Visual Studio Node.js 1:1채팅(3) Client

2020/07/30 - [Javascript] - Visual Studio Node.js 1:1 채팅(2) Server.js

2020/07/30 - [Javascript] - Visual Studio Node.js 1:1 채팅하기(1)

2020/07/30 - [Javascript] - Node.js 윈도우 코드 환경 Visual Studio 2019

 

 

node.js에서 서버로 사용될 코드다.

app, http, io, url을 사용 선언하고, userRooms라는 채팅방 객체를 선언해 놓는다.

app.use(hander)를 등록하여, 객체가 들어오면 핸들링하도록 했다.

 

'use strict';
var port = process.env.PORT || 1337;
var app = require('express')();
var http = require('http').createServer(app);
var io = require('socket.io')(http);
var url = require('url');
var userRooms = {};

app.use(handler);

function handler(req, res) {
    var location = url.parse(req.url);
    var findString = "chat";
    var result = false;

    if (location.pathname.replace('/', '').split(' ').indexOf(findString) > -1)
        result = true;

    if (result === false) {
        res.writeHead(500);
        return res.end('Error');
    }

    var product = getParam(location.search, "product");
    var userName = getParam(location.search, "username");

    if (product === '' || userName === '') {
        res.writeHead(500);
        return res.end('Error');
    }

    fs.readFile(__dirname + '/index2.html',
        function (err, data) {
            if (err) {
                res.writeHead(500);
                return res.end('Error loading index.html');
            }

            res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
            data = data.toString('utf-8').replace('<%=host%>', req.headers.host);
            res.end(data);
        }
    );
};

/*
app.get('/', (req, res) => {
    res.sendFile(__dirname + '/index.html');
});*/
 
io.on('connection', (socket) => {
    var roomKey = "";

    socket.on('disconnect', () => {
        if (typeof userRooms[roomKey] !== "undefined") {
            console.log('disconnected');
            io.to(userRooms[roomKey]).emit('disconnect');
            //delete userRooms[room];
        }
    });

    //socket.on('leaveRoom', (room, name) => {
    //    socket.leave(userRooms[room], () => {
    //        console.log(name + ' leave a ' + userRooms[room]);
    //        io.to(userRooms[room]).emit('leaveRoom', room, name);
    //    });
    //});

    socket.on('joinRoom', (search) => {
        search = decodeURIComponent(search);
        var userName = getParam(search, "username");
        roomKey = getRoomKey(search);
        var room = userRooms[roomKey];

        if (typeof room === "undefined") {
            socket.join(socket.id);
            userRooms[roomKey] = socket.id;
        } else {
            var socketId = userRooms[roomKey];
            socket.join(socketId);
        }

        io.to(userRooms[roomKey]).emit('joinRoom', userName); //클라이언트 callback
    });

    socket.on('chat message', (room, name, msg) => {
        var chatRoomKey = getRoomKey(room);
        io.to(userRooms[chatRoomKey]).emit('chat message', name, msg);
    });

    socket.on('endChat', (room) => {
        if (typeof room === "undefined") {
            return;
        }

        var chatRoomKey = getRoomKey(room);

        if (typeof userRooms[chatRoomKey] !== "undefined") {
            delete userRooms[chatRoomKey];
        }
    });
});

http.listen(port, () => {
    console.log('listening on *:' + port);
});

function getParam(parameters, name) {
    var params = parameters.replace("?", "");

    if (params) {
        var paramList = params.split("&");

        for (var i = 0; i < paramList.length; i++) {
            var param = paramList[i].split('=');

            if (param[0].toLowerCase() === name.toLowerCase()) {
                return param[1];
            }
        }
    }

    console.log('Query variable %s not found', name);
    return "";
};

function getRoomKey(parameters) {
    var product = getParam(parameters, "product");
    var username = getParam(parameters, "username");

    return product.concat("-", username);
}

 

이미 구현해놓은 코드에 민감한 정보 한두개만 살짝 수정을 했다.

handler에 http://주소/chat 이라는 정보가 없거나 get 매개변수에 product나 username이 없으면 Error를 표출하고 끝난다.

 

io.on('connection', (socket) => {
    var roomKey = "";

    socket.on('disconnect', () => {
        if (typeof userRooms[roomKey] !== "undefined") {
            console.log('disconnected');
            io.to(userRooms[roomKey]).emit('disconnect');
            //delete userRooms[room];
        }
    });

    //socket.on('leaveRoom', (room, name) => {
    //    socket.leave(userRooms[room], () => {
    //        console.log(name + ' leave a ' + userRooms[room]);
    //        io.to(userRooms[room]).emit('leaveRoom', room, name);
    //    });
    //});

    socket.on('joinRoom', (search) => {
        search = decodeURIComponent(search);
        var userName = getParam(search, "username");
        roomKey = getRoomKey(search);
        var room = userRooms[roomKey];

        if (typeof room === "undefined") {
            socket.join(socket.id);
            userRooms[roomKey] = socket.id;
        } else {
            var socketId = userRooms[roomKey];
            socket.join(socketId);
        }

        io.to(userRooms[roomKey]).emit('joinRoom', userName); //클라이언트 callback
    });

    socket.on('chat message', (room, name, msg) => {
        var chatRoomKey = getRoomKey(room);
        io.to(userRooms[chatRoomKey]).emit('chat message', name, msg);
    });

    socket.on('endChat', (room) => {
        if (typeof room === "undefined") {
            return;
        }

        var chatRoomKey = getRoomKey(room);

        if (typeof userRooms[chatRoomKey] !== "undefined") {
            delete userRooms[chatRoomKey];
        }
    });
});

 

위에 코드에서 socket 부문만 보도록 하자.

유저가 해당 경로에 접속하면c onnection 이벤트가 발생한다.

여기서 다음과 같은 이벤트를 만들어 준다.

 

  socket.on('chat message', (room, name, msg) => {
        var chatRoomKey = getRoomKey(room);
        io.to(userRooms[chatRoomKey]).emit('chat message', name, msg);
    });

 

자바스크립트가 그렇듯 chat message라는 이벤트명으로 직접 만들어 준다.

chat message라는 키워드로 클라이언트에서 접근해서 사용하므로 중복되지 않아야 한다.

io.to 명령어는 해당 방에 접속하여 있는 유저에게 메세지를 보낸다.

 

 socket.on('joinRoom', (search) => {
        search = decodeURIComponent(search);
        var userName = getParam(search, "username");
        roomKey = getRoomKey(search);
        var room = userRooms[roomKey];

        if (typeof room === "undefined") {
            socket.join(socket.id);
            userRooms[roomKey] = socket.id;
        } else {
            var socketId = userRooms[roomKey];
            socket.join(socketId);
        }

        io.to(userRooms[roomKey]).emit('joinRoom', userName); //클라이언트 callback
    });

 

유저가 접속하면 joinRoom 명령어를 받아 방을 만들어 준다.

 

  socket.on('disconnect', () => {
        if (typeof userRooms[roomKey] !== "undefined") {
            console.log('disconnected');
            io.to(userRooms[roomKey]).emit('disconnect');
            //delete userRooms[room];
        }
    });

접속을 끊으면 방에 남아있는 유저에게 대화방 나갔다고 알려준다.

 

 socket.on('endChat', (room) => {
        if (typeof room === "undefined") {
            return;
        }

        var chatRoomKey = getRoomKey(room);

        if (typeof userRooms[chatRoomKey] !== "undefined") {
            delete userRooms[chatRoomKey];
        }
    });

 

방에 남은 유저가 상대방이 나갔네?를 체크하는 순간 그럼 나도 나가야지. 하면서 방을 없애면서 나가는 형식이다.

 

function getParam(parameters, name) {
    var params = parameters.replace("?", "");

    if (params) {
        var paramList = params.split("&");

        for (var i = 0; i < paramList.length; i++) {
            var param = paramList[i].split('=');

            if (param[0].toLowerCase() === name.toLowerCase()) {
                return param[1];
            }
        }
    }

    console.log('Query variable %s not found', name);
    return "";
};

function getRoomKey(parameters) {
    var product = getParam(parameters, "product");
    var buyer = getParam(parameters, "buyer");

    return product.concat("-", buyer);
}

매개변수와 방 키를 획득하는 자바스크립트 코드

 

N:N 채팅일 경우 leaveRoom을 활성화 하여 다르게 코딩하여야 한다.