관리 메뉴

나만을 위한 블로그

[PHP] Node.js와 socket I/O를 활용한 웹 실시간 전체 채팅 기능 구현 본문

PHP

[PHP] Node.js와 socket I/O를 활용한 웹 실시간 전체 채팅 기능 구현

참깨빵위에참깨빵 2020. 1. 5. 21:15
728x90
반응형

참고한 사이트 : https://www.a-mean-blog.com/ko/blog/%EB%8B%A8%ED%8E%B8%EA%B0%95%EC%A2%8C/_/Node-JS-Socket-io-%EC%B1%84%ED%8C%85%EC%82%AC%EC%9D%B4%ED%8A%B8-%EB%A7%8C%EB%93%A4%EA%B8%B0

 

단편강좌: Node.JS&Socket.io 채팅사이트 만들기 - A MEAN Blog

Node.js와 웹소켓(web socket)을 사용해서 아래와 같은 간단한 채팅사이트를 만들어 봅시다. 이 강의는 node js, Express로 아주 기초적인 서버를 제작할 수 있는 분들을 대상으로 합니다. MEAN Stack/개발 환경 구축 및 Node JS 첫걸음/Hello World!를 공부하신 후에 이 포스트를 진행하시기 바랍니다. Web Socket 웹소켓은 웹 서버와 웹 브라우저 간의 양방향 통신을 위한 프로토콜입니다. 일반적으로 웹사이트는

www.a-mean-blog.com

 

※ 이 글은 Centos 7 기준으로 씀. 즉, 윈도우 기반이 아닌 리눅스 기반으로 쓰여짐

 이 글은 아파치 웹 서버를 설치했다는 가정하에 진행됨.

※ 이 글에서 사용한 문서 편집기는 아톰임. SFTP 연결로 아파치 웹 서버의 웹 루트 디렉토리와 연결한 상태

 

 

TCP/IP를 활용해 웹 사이트에서 실시간 채팅을 만들고 싶었다. 그래서 필요한 것들이 뭔지 검색하니 대충 아래와 같은 키워드들이 나왔다.

 

- Node.js,  socket I/O

 

그래서 둘을 공부했고, 그 다음 위 사이트를 참고해 웹 페이지에서 실시간 채팅이 이뤄지는 걸 구현했다.

참고 사이트에서 제공한 예시 이미지는 아래의 그림이다. 구현 결과 이러한 UI로 채팅이 이뤄진다.

 

 

일단 이 화면을 띄우려면 Node.js가 필요하다.

먼저 Node.js 홈페이지에 들어가서 현재 공식 사이트에서 배포되고 있는 버전은 무엇인지 확인해봤다.

 

https://nodejs.org/ko/

 

Node.js

Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.

nodejs.org

 

20.01.05 기준 12.14.0 LTS 버전이 배포되고 있다는 것만 확인하고, 설치글을 찾아다니며 따라해보고 싶은 사이트를 찾았다.

그 결과 찾은 사이트가 이 곳이다. 읽어본 결과 Node.js를 설치하면 npm이란 게 같이 깔린다고 한다.

이게 뭔지는 검색해보자.

 

https://linuxize.com/post/how-to-install-node-js-on-centos-7/

 

How to install Node.js and npm on CentOS 7

This tutorial walks you through the steps to install Node.js and npm on a CentOS 7 machine. Node.js is a cross-platform JavaScript run-time environment that allows server-side execution of JavaScript code.

linuxize.com

 

이 글을 보면 아래의 명령어로 Nodesource yum 저장소를 시스템에 추가하라고 한다.

글에선 12가 아니라 10으로 돼 있었지만 20.01.05 기준 최신 버전은 12라서 10을 12로 바꾼 뒤 엔터를 쳐 봤다.

문제없이 잘 됐다.

 

# curl -sL https://rpm.nodesource.com/setup_12.x | sudo bash -

 

그 다음 Node.js를 설치했다.

설치 과정 중 y를 누르는 순간이 있는데 치기 귀찮다면 -y를 넣고 실행한다.

-y 명령어는 설치 도중 Y/N을 눌러 설치 이어서 진행하거나 설치를 종료하는 순간이 있는데, 그 때 y를 치지 않아도 자동으로 설치할 수 있게 해주는 옵션 명령어다.

 

# yum install -y nodejs

 

그 다음은 내가 설치한 Node.js 버전이 내 의도대로 12.x 버전이 깔렸는지 확인할 차례다.

아래의 명령어를 터미널에 치면 내가 설치한 Node.js의 버전을 출력해준다.

 

# node -v

 

 

내가 의도한 12.14.0 버전이 설치된 걸 확인했다. 위 명령어 말고도 node --version을 치면 된다고 설치 글에서 적혀있지만, 버전 확인하는 데 굳이 긴 걸 치기는 너무 귀찮다. 짧은 걸로도 확인 되는데 굳이...

 

npm 버전도 확인해봤다. 위의 코드에서 node를 npm으로만 바꿔준 뒤 엔터를 치면 나온다.

난 6.13.4 버전이 설치됐다.

이걸로 Node.js와 npm 설치 끝.

 

 

이제 실시간 전체 채팅 예제를 구현할 차례다.

그 전에 웹 루트 디렉토리로 이동해야 한다. 내 경우는 htdocs가 웹 루트 디렉토리라 그곳으로 이동 후, chatting이라는 폴더를 하나 만들었다. 만들었으면 cd 폴더명을 쳐서 그 폴더 안으로 이동한다.

방금 만들었으니 chatting 폴더 안에는 아무것도 없는 상태다. 그 상태에서 아래 명령어를 친다.

주의할 점은, 터미널을 통해 chatting 폴더 안에 들어온 상태에서 아래의 명령어를 쳐야 한다.

 

# npm init

 

이건 Package.json 이라는 파일을 만들기 위해 필요한 명령어다.

Package.json이 무슨 파일인지에 대해선 구글링하면 나오지만 난 이 곳을 보고 Package.json이란 뭔지에 대해서 알았다.

 

https://www.journaldev.com/7553/importance-of-package-json-in-node-js-applications

 

Importance of package.json in Node JS Applications - JournalDev

Before starting Node JS applications development, we should learn some basics and importance of package.json file. Every Node JS application or module or

www.journaldev.com

아래는 본문의 내용을 구글 번역 플러그인으로 돌린 내용이다.

 

package.json은 Node JS Project 또는 애플리케이션에 대한 모든 메타 데이터 정보를 포함하는 일반 JSON (Java Script Object Notation) 텍스트 파일입니다. 모든 Node JS 패키지 또는 모듈은 메타 데이터를 일반 JSON 오브젝트 형식으로 설명하기 위해 루트 디렉토리에 이 파일을 가지고 있어야합니다. 대소 문자는 "package"이고 파일 확장자는 "* .json"과 동일한 파일 이름을 사용해야합니다.

 

그렇다고 한다. 이것을 vi 명령어로 까보면 아래와 같이 생겼다.

 

 

이름, 버전 등의 정보가 JSON 형태로 쓰여진 게 보인다.

# npm init 명령어를 치면 엔터를 치면서 위 항목들을 입력할 수 있는데, main 부분의 기본값은 index.js라고 써져 있다.

이걸 그림처럼 server.js로 바꿔야 한다. 과정 중 바꾸는 걸 깜박했다면 # vi package.json 명령어로 수정할 수 있으니 걱정할 필요 ㄴ

저렇게 써 줘야 예제가 정상적으로 굴러간다. server.js라고 써놓지도 않았는데 왜 안되냐 하면 나도 할 말이 없다.

 

그 다음은 예제에 필요한 패키지들을 설치해준다. 아래 명령어로 express와 socket I/O를 설치한다.

 

# npm install express socket.io --save

 

그 다음 client.html, server.js 파일을 각각 만들었다.

client.html은 사용자 ID를 표시하며 채팅 메시지 입력 및 메시지 전송을 담당하는 파일이고, server.js는 socket I/O를 써서 메시지가 전송되는 기능을 구현하는 파일이다.

코드 밑의 글은 참고 사이트에 있던 본문의 코드를 설명하는 내용들이다.

 

// client.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Chat</title>
    <style>
      .chat_log{ width: 95%; height: 200px; }
      .name{ width: 10%; }
      .message{ width: 70%; }
      .chat{ width: 10%; }
    </style>
  </head>
  <body>
    <div>
      <textarea id="chatLog" class="chat_log" readonly></textarea>
    </div>
    <form id="chat">
      <input id="name" class="name" type="text" readonly>
      <input id="message" class="message" type="text">
      <input type="submit" class="chat" value="chat"/>
    </form>
    <div id="box" class="box">
    <script src="/socket.io/socket.io.js"></script> <!-- 1 -->
    <script src="//code.jquery.com/jquery-1.11.1.js"></script>
    <script>
      var socket = io(); //1
      $('#chat').on('submit', function(e){ //2
        socket.emit('send message', $('#name').val(), $('#message').val());
        $('#message').val('');
        $('#message').focus();
        e.preventDefault();
      });
      socket.on('receive message', function(msg){ //3
        $('#chatLog').append(msg+'\n');
        $('#chatLog').scrollTop($('#chatLog')[0].scrollHeight);
      });
      socket.on('change name', function(name){ //4
        $('#name').val(name);
      });
    </script>
  </body>
</html>

 

1. socket.io를 사용하는 경우 Node JS 첫걸음/Hello World!에서와는 다르게 app를 http에 연결시키고, 이 http를 다시 socket.io에 연결시키는 과정이 필요하다. 이는 socket.io가 express를 직접 받아들이지 못하기 때문이다. socket.io는 io라는 변수명으로 서버에서 사용된다.

 

2. 모든 request는 client.html를 response하도록 설정했다.

 

3. 사용자가 웹사이트에 접속하게 되면 socket.io에 의해 'connection' event가 자동으로 발생된다. io.on(EVENT,함수)는 서버에 전달된 EVENT를 인식하여 함수를 실행시키는 event listener다. 이때 함수에는 접속한 사용자의 socket이 parameter로 전달된다. 해당 접속자(socket)에 관련한 event들은 이 'connection' event listener 안에 작성되어야 한다.

 

3-1. 'connection' event listener에 event가 발생하면 한번만 일어나는 코드들이다. console.log로 접속자의 socket.id를 출력하고 사용자 이름을 만든 후 'change name'이란 event를 발생시킨다. emit는 '(빛 따위를) 발하다'라는 뜻으로 event를 발생시키는 함수다. 이 event는 client.html의 해당 event listener에서 처리된다. io.to(socket.id).emit을 사용하여 해당 socket.id에만 event를 전달한다.

 

3-2. socket.io(EVENT,함수)는 해당 socket에 전달된 EVENT를 인식하여 함수를 실행시키는 event listener다. 접속자의 접속이 해제되는 경우 socket.io에 의해 'disconnect' event가 자동으로 발생된다. console.log로 socket.id를 출력한다.

 

3-3. 3-2와 처럼 socket.io를 사용한 'send message' event의 event listener다. 이 event는 client.html에 작성된 사용자 정의 event로 접속자가 채팅메세지를 전송하는 경우에 발생한다. 이 event는 채팅메세지를 보낸 접속자의 이름과 채팅메세지를 parameter로 함께 전달한다. 'send message' event listener는 이 event를 받은 후 io.emit을 사용하여 모든 클라이언트들에게 event를 전달한다.

 

4. app.listen이 아닌 http.listen임에 유의.

 

// server.js

var express = require('express');
var app = express();
var http = require('http').Server(app); //1
var io = require('socket.io')(http);    //1

app.get('/',function(req, res){  //2
  res.sendFile(__dirname + '/client.html');
});

var count=1;
io.on('connection', function(socket){ //3
  console.log('user connected: ', socket.id);  //3-1
  var name = "user" + count++;                 //3-1
  io.to(socket.id).emit('change name',name);   //3-1

  socket.on('disconnect', function(){ //3-2
    console.log('user disconnected: ', socket.id);
  });

  socket.on('send message', function(name,text){ //3-3
    var msg = name + ' : ' + text;
    console.log(msg);
    io.emit('receive message', msg);
  });
});

http.listen(3000, function(){ //4
  console.log('server on!');
});

 

1. socket.io를 사용하기 위해 반드시 필요한 과정이다. socket.io.js를 가져오고, socket 변수를 설정한다.

 

2. jQuery의 'submit' event listener다. 입력창이 submit되면 서버로 'send message' 사용자 정의 event와 이름, 채팅메세지를 전달(emit)한다. 이후 message에 내용를 지워주고 focus를 해 준 후 event를 정지한다.

 

3. socket의 'receive message' event listener다. 서버에서 'receive message' event가 emit되면 message를 '#chatLog'에 추가하고 스크롤한다. 이 event는 server.js의 #3-3에서 발생한다.

 

4. socket의 'change name' event listener다. 서버에서 'change name' event가 emit되면 '#name'에 이름을 변경한다. 이 event는 server.js의 #3-1에서 발생한다.

 

막줄에 보면 3000 포트를 사용한다는 걸 볼 수 있다.

가상 머신의 왼쪽에 있는 센토스 우클릭 -> 설정 -> 네트워크 -> 고급, 포트 포워딩(한 화면 안에 다 있다) -> 포트 하나를 만들고 적당한 이름을 하나 만들고 호스트 / 게스트 포트를 전부 3000으로 맞춰서 3000 포트를 열어준다.

 

이렇게 chatting 폴더 안에 두 파일을 모두 만들었다면 다음으로 nodemon이라는 걸 설치해야 한다.

nodemon은 Node.js의 패키지인데 프로젝트의 실행, 재실행에 관여한다.

일반적으로 Node.js 프로젝트는 node 자바스크립트_파일_이름 명령어로 프로젝트 프로그램을 실행할 수 있는데, 프로그램이 실행되고 난 후 프로젝트 코드를 바꾸면 프로그램을 재실행해야 바뀐 코드가 적용된다.

하지만 nodemon을 써서 프로그램을 실행하면 nodemon이 해당 프로젝트 폴더 내의 파일들을 보고 있다가 프로젝트 코드의 수정, 저장이 감지되면 자동으로 프로그램을 재시작해 새 코드를 바로 적용할 수 있게 해준다.

아직 이것의 덕을 보진 않았지만 일단 설치하라니 설치한다. 설치는 아래 명령어를 치면 바로 된다.

명령어 출처는 nodemon의 공식 깃헙 사이트다. 여러 주제의 글들이 있는데 나중에 읽어보자.

 

https://github.com/remy/nodemon

 

remy/nodemon

Monitor for any changes in your node.js application and automatically restart the server - perfect for development - remy/nodemon

github.com

# npm install -g nodemon

 

설치했다면 터미널에 # nodemon을 입력해주기만 하면 아래와 같은 화면이 나온다.

 

 

서버가 돌아간다는 건지, 아니면 server.js 파일이 실행됐다는 건지 모르겠지만 암튼 뭔가 됐다.

이 상태에서 localhost:3000을 입력해주면 이런 화면이 나온다.

 

탭을 하나 더 만들어서 똑같이 localhost:3000으로 접속한 뒤 메시지를 쳐보면 아래와 같이 실시간 채팅을 하는 게 보인다.

 

 

그리고 센토스 터미널을 보면 아래와 같이 내가 입력한 메시지들이 터미널에 출력되고 있는 걸 볼 수 있다.

 

 

nodemon의 종료는 리눅스상에서 강제 프로세스 종료처럼 Ctrl + C를 눌러주면 된다.

그럼 아래와 같은 화면이 나오면서 nodemon이 종료된다.

 

 

^C는 컨트롤 + C를 누른 영향으로 나오는 것이고, 그 다음에 명령어를 칠 수 있는 공간이 마련된다.

엔터를 한번 더 누르면 원래대로 돌아오니 ^C는 무시해도 된다.

이것으로 Node.js, socket I/O를 활용한 실시간 채팅 구현 끝.

반응형
Comments