[Node.js] Node.js 채팅방 제작[Node.js] Node.js 채팅방 제작

Posted at 2019. 2. 12. 00:44 | Posted in Node.js
반응형





해당 채팅은 의 코드를 필자의 사용목적에 맞게 변경한 것이다.

 

 -. 채팅방 기능 추가

 -. 채팅방 입장기능 추가

 -. 전역변수에 접속한 사용자 값을 담고 퇴장시 socket.io의 값을 대조하여 퇴장 이벤트 실행

    (퇴장이벤트를 위해 사용하는데 실 운영시에는 DB 및 저장소 기능을 사용하는것을 권장한다.)




 socket.chatting.app.js

// 모듈을 추출한다. - http://localhost:52273
var http = require("http");
var fs = require("fs");
var socketio = require("socket.io");

// 웹 서버를 생성한다.
var server = http.createServer(function(request, response) {

    // HTMLPage.html 파일을 읽는다.
    fs.readFile("node_chatting.html", function(error, data) {

        response.writeHead(200, {"Content-Type" : "text/html"});
        response.end(data);
    });
}).listen(52273, function() {

    console.log("Server Running at http://127.0.0.1:52273");
});

// 소켓 서버를 만든다.
var io = socketio.listen(server);

// 접속한 사용자의 방이름, 사용자명, socket.id값을 저장할 전역변수
const loginIds = new Array();

io.sockets.on("connection", function(socket) {

    // 채팅방 입시 실행
    socket.on("access", function(data) {

        // console.log(io.sockets.adapter.rooms);

        socket.leave(socket.id);
        socket.join(data.room);

        loginIds.push({
              socket : socket.id  // 생성된 socket.id
            , room : data.room  // 접속한 채팅방의 이름
            , user : data.name   // 접속자의 유저의 이름
        });

        // 사용자가 페이지 새로고침시 loginIds 변수에 값이 누적되지 않게 동일한 사용자의 socket.id 값을 삭제한다.
        for(var num in loginIds) {

            // 사용자 이름이 같으면서, 기존소켓아이디와 현재 소켓아이디가 다른 값이 있는지 찾아낸다.
            if(loginIds[num]['user'] == data.name && loginIds[num]['socket'] != socket.id) {
               
                // loginIds의 해당 순서의 값을 삭제한다.
                loginIds.splice(num, 1);
            }
        }

        // 클라이언트의 Contact 이벤트를 실행하여 입장한 사용자의 정보를 출력한다.
        io.sockets.in(data.room).emit("contact", {
              count : io.sockets.adapter.rooms[data.room].length
            , name : data.name
            , message : data.name + "님이 채팅방에 들어왔습니다."
        });
    });

    // 채팅방 퇴장시 실행(Node.js에서 사용자의 Disconnect 이벤트는 사용자가 방을 나감과 동시에 이루어진다.)
    socket.on("disconnect", function() {

        var room = "";
        var name = "";
        var socket = "";
        var count = 0;
       
        // disconnect 이벤트중 socket.io의 정보를 꺼내는데는 에러가 발생하고,
        // 실행중인 node.js Application이 종료된다.
        // 이에따라 try ~ catch ~ finally 로 예외처리해준다.
        try {
           
            // 생성된 방의 수만큼 반복문을 돌린다.
            for(var key in io.sockets.adapter.rooms) {

                // loginIds 배열의 값만큼 반복문을 돌린다.
                var members = loginIds.filter(function(chat) {
                    return chat.room === key;
                });
   
                // 현재 소켓 방의 length와 members 배열의 갯수가 일치하지 않는경우
                if(io.sockets.adapter.rooms[key].length != members.length) {
               
                    // 반복문으로 loginIds에 해당 socket.id값의 존재 여부를 확인한다.
                    for(var num in loginIds) {

                        // 일치하는 socket.id의 정보가 없을경우 그 사용자가 방에서 퇴장한것을 알 수 있다.
                        if(io.sockets.adapter.rooms[key].sockets.hasOwnProperty(loginIds[num]['socket']) == false) {

                            // 퇴장한 사용자의 정보를 변수에 담는다.
                            room = key;
                            name = loginIds[num]['user'];

                            // loginIds 배열에서 퇴장한 사용자의 정보를 삭제한다.
                            loginIds.splice(num, 1);
                        }
                    }
                   
                    // 해당 방의 인원수를 다시 구한다.
                    count = io.sockets.adapter.rooms[key].length;
                }
            }

        } catch(exception) {

            console.log(exception);

        } finally {

            // 클라이언트의 Contact 이벤트를 실행하여 이탈한 사용자가 누군지 알린다.
            io.sockets.in(room).emit("contact", {
                  count : count
                , name : name
                , message : name + "님이 채팅방에서 나갔습니다."
            });
           
        }
      });

    // 메세지 전송 이벤트
    socket.on("message", function(data) {

        // 클라이언트의 Message 이벤트를 발생시킨다.
        io.sockets.in(data.room).emit("message", data);
    });
});





 node_chatting.html

<html>
<head>
<title>:: socket.io.chat ::</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<link rel="stylesheet" href="https://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css" />
<script type="text/javascript" src="https://code.jquery.com/jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="https://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>
<script type="text/javascript" src="/socket.io/socket.io.js"></script>
<script type="text/javascript">

    // HTML 문서가 모두 준비되면
    jQuery(document).ready(function() {

        var socket = io.connect();

        // 채팅 창으로 접속 및 전환
        jQuery("#startChatting").click(function() {
            socket.emit("access", {
                  room : jQuery("#inputRoom").val()
                , name : jQuery("#inputName").val()
            });

            location.href = "#chatpage";
            jQuery("#roomName").html(jQuery("#inputRoom").val());
        });

        // 이벤트를 연결합니다.
        socket.on("message", function(data) {

            pushMessage(data.name, data.message, data.date);
        });

        // 채팅방 접속 or 퇴장시 실행되는 알림 메세지
        socket.on("contact", function(data) {
        
            jQuery("#userCount").html(data.count);
            pushMessage(data.name, data.message, new Date().toUTCString());
        });

        // 채팅 메시지를 전송한다.
        jQuery("#submit").click(function() {

            socket.emit("message", {
                  room : jQuery("#inputRoom").val()
                , name : jQuery("#inputName").val()
                , message : jQuery("#inputMessage").val()
                , date : new Date().toUTCString()
            });

            jQuery("#inputMessage").val("");
        });
        });

    function pushMessage(pushName, pushMsg, pushDate) {

        // 입력할 문자 메시지
        var output = "";
        output += "<li>";
        output += "<h3>" + pushName + "</h3>";
        output += "<p>" + pushMsg + "</p>";
        output += "<p>" + pushDate + "</p>";
        output += "</li>";

        // 문서 객체를 추가합니다.
        jQuery(output).prependTo("#content");
        jQuery("#content").listview('refresh');
    };
</script>
</head>
<body>
<div data-role="page">
    <div data-role="header">
        <h1>Socket.io Chat</h1>
    </div>
    <div data-role="content">
        <h3>입장할 방의 이름과 닉네임을 지정해 주세요</h3>
        <div data-role="fieldcontain">
            <label for="textinput">방 이름</label>
            <input id="inputRoom" value=""/>
        </div>
        <div data-role="fieldcontain">
            <label for="textinput">닉 네임</label>
            <input id="inputName" value=""/>
        </div>
        <input type ="button" id="startChatting" value="채팅 시작"/>
        </div>
</div>
<div data-role="page" id="chatpage">
    <div data-role="header">
        <h1>
            <span id="roomName"></span>
            &nbsp;:&nbsp;
            <span id="userCount">0</span>
        </h1>
    </div>
    <div data-role="content">
              <input id="inputMessage" value=""/>
              <input type="button" id="submit" value="입력"/>
              <ul id="content" data-role="listview" data-inset="true"></ul>
        </div>
</div>
</body>
</html>





※ 위 설명에서 말한대로 모던 웹을 위한 Node.js 프로그래밍 코드의 내용을 바탕으로 만들었습니다.

   실 서비스에서는 접속한 사용자의 값을 DB 및 따로 저장소를 만들어 운영하는것을 권장합니다.


※ 혹 개선의 여지가 있음을 아시는 분은 댓글로 남겨주시면 참고하겠습니다.






반응형
//

[Node.js] PM2 프로세스 관리 도구[Node.js] PM2 프로세스 관리 도구

Posted at 2019. 1. 22. 11:52 | Posted in Node.js
반응형





API : https://pm2.keymetrics.io/docs/usage/pm2-doc-single-page

참고 : https://blog.outsider.ne.kr/1197

참고 : https://jybaek.tistory.com/716






■ PM2( Process Manager )란?




· PM2는 노드 프로세스를 관리해주는 Node.js의 프로세스 매니저( Process Manager )이다.


· 서비스중인 Node.js 프로세스가 예상하지 못한 오류로 종료( exit ) 이벤트가 발생하면 해당 프로세스를 자동으로 재시작해준다.

  ( 주로 채팅이나 웹 서비스같은 계속 서버가 종료되지 않고 유지되어야 하는 경우에 사용된다. )


· 재시작이라는 점에서 유사한 매니저 프로그램으로 qmail에서 사용하는 svscna이 존재하지만, svscna은 그냥 죽으면 프로세스만 살리는 역할만 수행한다.


· PM2는 프로세스 재시작 이외에 로그처리등의 기능을 추가적인 기능을 제공한다.








■ PM2 설치



PM2는 한곳이 아니라 여러 곳에서 사용하게 되는 경우가 많아


기본적으로 글로벌( --global ) 셋팅으로 설치하는 것을 권장한다.



 $ npm install --global pm2




설치가 완료되면 아래 명령어를 실행하여


PM2가 정상적으로 설치가 되었는지 확인해 보자.




 $ pm2 version




위와같이 PM2가 정상적으로 설치된 모습을 확인할 수 있다.











■ PM2 실행




진행에 앞서 필요한 예제 파일을 하나 생성해보자.


간단한 http 모듈을 프로세스를 생성하여 예제로 사용할 것이다.



 pm2_test.js( 예제노드.js )

const http = require("http");


http.createServer(function(request, response) {

    response.writeHead(200, {"Content-Type":"text/html"});

    response.end("Hello World\n");

}).listen(52275, function() {

    console.log("Server Running at localhost:52273");

});




이제 PM2를 실행해볼 것이다.


위에서 작성한 예제노드.js 파일을 등록시켜보자.




#01. PM2에 프로스세 등록




먼저 PM2에 프로세스를 등록시켜 보자.


위에서 작성한 예제파일.js 파일을 실행시켜보려고 한다.


start 명령을 통해 진행할 수 있다.



 $ pm2 start 예제파일.js




start 옵션 명령을 통해 프로세스를 등록하면


PM2 리스트에 해당 프로세스가 등록된것을 바로 확인할 수 있다.


여기서 name 부분을 보면 예제노드의 이름이 자동으로 들어간 것을 확인 할 수 있는데.


--name "프로세스명" or "앱이름" 을 지정하여 원하는 명칭을 지정하여 지정하는것도 가능하다.




 $ pm2 start 예제노드.js --name "APP이름"



그럼 위와같이 name 항목에 예제파일( pm2_test )명이 아닌 직접 지정한 프로세스명( http_server )가 등록된 것을 확인할 수 있다.





#02. PM2에 등록된 프로세스 확인




그럼 위와같이 PM2에 해당 프로세스가 등록된 것을 확인 할 수 있다.


실행중인 PM2 목록을 다시 확인 하기 위해서는 list 명령으로 다시 확인 할 수 있다.



 $ pm2 list






#03. PM2 프로세스 정지




이제 등록된 예제노드를 정지 시켜 보자.


stop 명령을 통해 정지시킬 수 있는데 방법은 2가지 이다.


id 번호를 입력하거나, name으로 지정된( 예제노드.js  ) 파일명을 입력하면 정지 시킬 수 있다.



 $ pm2 stop 인덱스번호

 $ pm2 stop APP이름





#04. PM2 정지시킨 프로세스 재시작



정지시킨 PM2 항목은 다시 실행 시킬 수 있다.


start 명령 옵션을 통해 정시켰던 프로세스를 다시 실행 할 수 있다.


정지시켰던 프로세스를 다시 실행시켜보자.




 $ pm2 start 인덱스번호

 $ pm2 start APP이름



그럼 PM2 리스트 목록에 stoppedonline으로 변경된 것을 확인 할 수 있다.





#05. PM2 프로세스 삭제




위에서 등록된 PM2 프로세스를 정지시켜 보았다.


그럼 이번에는 그 반대로 아예 프로세스를 제거시켜 보도록 하겠다.


삭제는 delete 옵션을 통해 진행할 수 있다.



 $ pm2 delete 인덱스번호

 $ pm2 delete APP이름



그럼 위의 이미지와 같이 PM2 실행 리스트 창에


프로세스가 존재하지 않는 모습을 확인 할 수 있다.






#06. PM2 프로세스 상세 정보 확인



등록된 프로세서의 상세정보를 확인 해보자.


show 명령을 통해 확인할 수 있다.



 $ pm2 show 인덱스번호
 $ pm2 show APP이름










■ PM2 로그확인




#01. 로그파일( *.log )의 위치 변경



위 show 명령 옵션을 통해 PM2에 등록된 프로세스의 *.log파일의 위치가


기본적으로 자동으로 진행되는 것을 확인 할 수 있다.



정보

경로

 error log path

 기본경로$home .. /logs/프로세스명-error.log

 out log path

 기본경로$home .. /logs/프로세스명-out.log

 pid path

 기본경로$home .. /logs/프로세스명-0.log



이제 각 로그 파일의 위치를 로그를 직접 지정해보자.



# Sample - 로그 경로 변경

 $ pm2 start 예제노드.js --name "APP이름" --ouput ./로그파일.log

 $ cat 로그파일.log




# Sample - 로그 경로 변경

 $ pm2 start 예제노드.js --name "APP이름--ouput ./로그파일.log --error ./로그파일.log --merge-logs

 $ cat 로그파일.log





#02. 로그 옵션 설명



명령 옵션

설명

 --log | -l

 · 출력 및 오류 로그를 모두 출력할 파일의 경로 지정

 --outer | -o

 · 출력 로그 파일 지정 

 --error | -e

 · 오류 로그 파일 지정

 --time

 · 표준 형식 타임스탬프를 포함한 로그 출력

 --log-data-format

 · 사용자 지정 형식 지정 타임스탬프로 있는 로그 출력

 --merge-logs

 · 동일한 앱 이름( name )으로 여러 프로세스를 실행할 때 ID로 파일을 분할하지 않게 지정





#03. 로그 표시


 $ pm2 logs 인덱스번호

 $ pm2 logs APP이름








■ PM2 모니터링




PM2를 사용하면 애플리케이션의 리소스 사용을 간편하게 모니터링 할 수 있다.


터미널에서 메모리 및 CPU를 쉽고 직접적으로 모니터링이 가능하다.



 $ pm2 monit








반응형
//

[Node.js] Ubuntu에 Node.js 설치하기[Node.js] Ubuntu에 Node.js 설치하기

Posted at 2018. 10. 25. 18:50 | Posted in Node.js
반응형




※ 우분투에 Node.js를 설치하기 위해서는 우선 CURL이 설치되어 있어야 한다.

    CURL이 설치되어 있지 않다면 아래링크를 참조하여 CURL을 먼저 설치하도록 한다.

    http://magic.wickedmiso.com/137





■ 우분투에 Node.js 설치하기




01. 가장 먼저 Node.js 모듈을 컴파일 하기 위해 build-essential을 설치한다.

 $ sudo apt-get install -y build-essential





02. 이제 curl을 이용해서 https://deb.nodesource.com 접속하여 Node.js를 다운받는다.

 $ curl -sl https://deb.nodesource.com/setup_버전 숫자.x | sudo -E bash -





03. 이제 다운받은 Node.js를 설치한다.

 $ sudo apt-get install -y nodejs





04. 설치가 완료되었다면 Node.js가 정상적으로 실행되는지 확인해 보자.

  $ node

 > console.log('Hello Wicked Miso .. !');






※ 기본적으로 Node.js에 관련된 node_modules 디렉토리는 우분투에서는 아래 경로에 자동 생성된다.

 $ cd /usr/lib/node_modules/npm/node_modules

 :/usr/lib/node_modules/npm/node_modules$ ls -al








반응형
//