Socket.io Chat

시작하기: 채팅 애플리케이션

이 가이드에서 우리는 기본적인 채팅 애플리케이션을 만들 것입니다. Node.JS 또는 Socket.IO에 대한 거의 모르고 있어도 되기 때문에, 일반적인 지식 수준이라면 충분합니다.

소개

채팅 애플리케이션을 만들 때, LAMP(PHP)같이 일반적인 웹 애플리케이션 스택으로 만드는 것은 전통적으로 매우 어려운 일이었습니다. 서버에서 변경사항을 가져오는 방식이고, 타임스탬프를 계속 추적하고, 생각보다 매우 많이 느립니다.

소켓은 전통적으로 클라이언트와 서버 간의 양방향 통신을 제공하는, 거의 모든 실시간 채팅 시스템이 설계되는 해법이었습니다.

서버는 메시지를 클라이언트에게 푸시할 수 있다는 것을 의미합니다.

채팅 메시지를 쓸 때마다, 서버가 그것을 다른 연결된 모든 클라이언트에게 푸시할 것이라는 개념입니다.

웹 프레임워크

첫번째 목표는 폼과 메시지 목록을 제공하는 간단한 HTML 웹페이지를 만드는 것입니다. 우리는 Node.JS 웹 프레임워크 express를 사용할 것입니다. Node.JS가 설치되어 있어야 합니다.

우선 우리 프로젝트를 설명하는 package.json 매니페스트 파일을 만듭니다. 파일의 위치는 완전히 빈 디렉토리를 추천합니다 (저는 chat-example 이라고 하겠습니다).

{
  "name": "socket-chat-example",
  "version": "0.0.1",
  "description": "my first socket.io app",
  "dependencies": {}
}

이제, 필요한 것을 쉽게 dependencies에 추가하기 위해서, npm install --save 를 사용하겠습니다:

npm install --save express

express가 설치되었으므로 우리 애플리케이션을 만들기 위한 index.js 파일을 만들 수 있습니다.

var app = require('express')();
var http = require('http').Server(app);

app.get('/', function(req, res){
  res.send('<h1>Hello world</h1>');
});

http.listen(3000, function(){
  console.log('listening on *:3000');
});

코드의 뜻은 다음과 같습니다:

  1. Express가 app을 초기화해서 여러분이 HTTP 서버를 제공할 수 있도록 함수 핸들러가 되게 합니다(2번째 줄).
  2. 경로 핸들러 /를 선언해서 웹사이트 홈에 접속할 주소를 선언합니다.
  3. 서버가 대기하는 포트를 3000번으로 정합니다.

여러분이 node index.js 라고 실행하면 다음과 같이 보일 것입니다:

그리고 브라우저에서 http://localhost:3000 로 접속합니다:

HTML 제공하기

지금까지 index.js에서 res.send를 호출해서 HTML 문자열을 전달했습니다. 만약 전체 애플리케이션의 HTML 전체를 거기에 추가한다면 우리 코드를 알아보기 힘들게 될 것입니다. 대신, 우리는 index.html 파일을 만들어서 제공합니다.

우리의 경로 핸들러를 리팩터해서 대신 sendfile을 사용합니다:

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

그리고 index.html 파일을 다음과 같이 만듭니다:

<!doctype html>
<html>
  <head>
    <title>Socket.IO chat</title>
    <style>
      * { margin: 0; padding: 0; box-sizing: border-box; }
      body { font: 13px Helvetica, Arial; }
      form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
      form input { border: 0; padding: 10px; width: 90%; margin-right: .5%; }
      form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }
      #messages { list-style-type: none; margin: 0; padding: 0; }
      #messages li { padding: 5px 10px; }
      #messages li:nth-child(odd) { background: #eee; }
    </style>
  </head>
  <body>
    <ul id="messages"></ul>
    <form action="">
      <input id="m" autocomplete="off" /><button>Send</button>
    </form>
  </body>
</html>

만약 프로세스를 재시작(Ctrl+C 입력하고 다시 node index)하고 페이지를 새로고침하면 이렇게 보일 것입니다:

Socket.IO와 통합하기

Socket.IO는 두 가지로 이루어져 있습니다:

개발하는 동안, socket.io는 클라이언트를 자동으로 제공해주기 때문에, 곧 알게 될 것입니다. 지금은 모듈 하나를 더 추가하기만 하면 됩니다:

npm install --save socket.io

모듈이 설치되고, package.json에 의존성이 추가될 것입니다. 이제 index.js 파일을 수정해서 다음을 추가합니다:

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

app.get('/', function(req, res){
  res.sendfile('index.html');
});

io.on('connection', function(socket){
  console.log('a user connected');
});

http.listen(3000, function(){
  console.log('listening on *:3000');
});

주목할 것은 socket.io의 새로운 인스턴스를 초기화해서, http (HTTP 서버) 객체에 전달했다는 것입니다. 그리고 들어오는 소켓을 위한 connection 이벤트를 대기시키고, 그것을 콘솔에 기록하게 했습니다.

이제 index.html에서 </body> 앞에 다음 코드 조각을 추가합니다:

<script src="/socket.io/socket.io.js"></script>
<script>
  var socket = io();
</script>

io를 전역 변수로 노출하고, 연결하는 역할을 하는 socket.io-client를 불러오기 위한 전부입니다.

io() 호출할 때 어떤 URL도 명시하지 않은 것을 명심하세요, 왜냐하면 기본적으로 페이지를 제공하는 호스트에 연결하려고 하기 때문입니다.

이제 서버와 웹 사이트를 재시작하면 콘솔에 "a user connected"라고 찍히는 것을 볼 수 있을 것입니다. 서버 창을 열어보면 다음처럼 여러 개의 메시지를 보게 될 것입니다.

각 소켓은 특별히 disconnect 이벤트를 발생합니다:

io.on('connection', function(socket){
  console.log('a user connected');
  socket.on('disconnect', function(){
    console.log('user disconnected');
  });
});

그리고 여러 번 새로 고침하면 다음과 같은 결과를 볼 것입니다:

이벤트 보내기

Socket.IO의 중심 사상은 여러분이 원하는 어떤 데이터이든지, 어떤 이벤트 형태로라도 보내고 받을 수 있다는 것입니다. JSON같이 형태도 가능하고, 바이너리 데이터 역시 지원됩니다.

사용자가 메시지를 타이핑할 때 서버가 그것을 채팅 메시지 이벤트로 가져갈 수 있도록 만들어 봅시다. index.html에 있는 스크립트 섹션은 다음과 같이 보일 것입니다:

<script src="https://cdn.socket.io/socket.io-1.2.0.js"></script>
<script src="http://code.jquery.com/jquery-1.11.1.js"></script>
<script>
  var socket = io();
  $('form').submit(function(){
    socket.emit('chat message', $('#m').val());
    $('#m').val('');
    return false;
  });
</script>

그리고 index.js 에서 우리는 chat message 이벤트를 출력합니다:

io.on('connection', function(socket){
  socket.on('chat message', function(msg){
    console.log('message: ' + msg);
  });
});

결과는 다음 영상과 같을 것입니다:

브로드캐스팅하기

다음 목표는 서버에서 다른 사용자들에게 이벤트를 전송하는 것입니다.

모든 사람에게 이벤트를 보내기 위해서, Socket.IO는 io.emit을 제공합니다:

io.emit('some event', { for: 'everyone' });

만약 여러분이 특정 소켓을 제외하고 모든 사람에게 메시지를 보내려고 한다면, broadcast 플래그를 사용합니다:

io.on('connection', function(socket){
  socket.broadcast.emit('hi');
});

이번에는, 단순히 하기 위해서 우리는 보낸 사람을 포함해서 모든 사람에게 메시지를 보내겠습니다.

io.on('connection', function(socket){
  socket.on('chat message', function(msg){
    io.emit('chat message', msg);
  });
});

그리고 클라이언트 쪽에서 chat message이벤트를 받았을 때 페이지에 표시합니다. 클라이언트 쪽의 자바스크립트 코드는 이제 이렇게 됩니다:

<script>
  var socket = io();
  $('form').submit(function(){
    socket.emit('chat message', $('#m').val());
    $('#m').val('');
    return false;
  });
  socket.on('chat message', function(msg){
    $('#messages').append($('<li>').text(msg));
  });
</script>

그리고 우리의 채팅 애플리케이션을 완성했는데, 코드가 20줄 정도입니다! 대박! 이렇게 보일 것입니다:

과제

애플리케이션을 개선할 몇 가지 아이디어들입니다:

이 예제 구하기

GitHub 여기에서 찾을 수 있습니다.

$ git clone https://github.com/guille/chat-example.git