이 게시물에는 코드작성이 포함되어 있습니다. 소스코드를 받으신 후 진행해 주세요. MEAN Stack/개발 환경 구축에서 설명된 프로그램들(git, npm, atom editor)이 있어야 아래의 명령어들을 실행할 수 있습니다.
- Github에서 소스코드 보기: https://github.com/a-mean-blogger/node-js-socket-io-chatting-site/tree/6886b9d3b73d9bd2b70a3f869d4df8e8dc09d318
Node.js와 웹소켓(web socket)을 사용해서 아래와 같은 간단한 채팅사이트를 만들어 봅시다.
이 강의는 node js, Express로 아주 기초적인 서버를 제작할 수 있는 분들을 대상으로 합니다. MEAN Stack/개발 환경 구축 및 Node JS 첫걸음/Hello World!를 공부하신 후에 이 포스트를 진행하시기 바랍니다.
웹소켓은 웹 서버와 웹 브라우저 간의 양방향 통신을 위한 프로토콜입니다.
일반적으로 웹사이트는 요청(request)과 회신(response)로 작동합니다. 브라우저(클라이언트)에서 주소창에 주소를 입력하거나, 웹사이트의 링크를 클릭하거나 혹은 브라우저의 javascript에서 ajax 요청 등에 의해 request가 발생하는데 이 request가 서버로 전달되면 서버는 response를 만들어서 보내주게 됩니다. 이처럼 서버는 클라이언트의 request 가 없으면 데이터를 전달하지 않습니다. 항상 request가 클라이언트쪽에서 먼저 시작되고, 그 request에 대한 response만 전달됩니다.
하지만 웹 소켓은 클라이언트와 서버의 연결을 항상 유지합니다. 연결이 유지된 상태에서 서버 혹은 클라이언트상의 event(이벤트)가 발생하면(triggered) event listener에 의해 서버에서 클라이언트로, 혹은 클라이언트에서 서버로 데이터의 전달이 이루어 집니다.
event listener의 개념을 모르는 사람들을 위해 간단히 설명을 하자면, 우리가 웹사이트에서 버튼을 클릭하게 되면 'click'이라는 event가 발생됩니다. 브라우저에는 기본적으로 버튼을 클릭하는 event에 대한 코딩이 되어있어서 버튼을 누르면 form이 제출되거나 함수가 실행되거나 합니다. 이 event를 직접 만들 수도 있는데, 만약 그림이 클릭되는 경우 어떤 함수를 실행되게 하고 싶다면 직접 이 event를 코딩해 주어야 합니다. 이렇게 event와 event가 발생하는 경우 어떤 일을 할지를 코딩하여 실행하게 되면, 이 코드는 event가 발생할 때까지 기다리는데, 이를 event listener 라고 합니다.
Node.js에서 web socket를 사용하기 위해 socket.io package를 사용합니다. 클라이언트나 서버에서 socket.io를 이용하여 event를 발생시킬 수 있고 이 event는 서로에게 전달됩니다. 그러면 반대편의 event listener가 이 event에 대한 반응을 하게 됩니다.
socket.io 에는 누군가 웹사이트에 접속하면 발생하는 'connection', 접속자가 웹사이트에서 이탈하면 발생하는 'disconnect' 등의 기본 event도 있고 사용자 정의 event도 만들 수 있습니다. 예를 들어 브라우저에서 A라는 버튼을 클릭하면 'A button clicked'라는 event를 발생하게 코드를 작성하고, 서버에는 'A button clicked'에 대한 event listener를 작성하여 할일을 처리하게 할 수 있습니다. 반대로 서버에서 event를 발생시키고, 브라우저에 event listener를 작성하여 서버에서 브라우저로 데이터를 전송하게 할 수도 있습니다. 이때 서버에서 발생시키는 event는 특정한 클라이언트에게 전달하게 할 수도 있고, 접속된 모든 클라이언트들에게 전달 하게 할 수도 있습니다.
위 두가지 방법을 연결하면 다음과 같은 방식으로 채팅사이트를 구현할 수 있습니다.
설명이 어려운데, 예제를 살펴보면 쉽게 이해가 됩니다.
프로젝트 생성을 위해 새 폴더를 생성합니다. 해당 폴더에서 command line(cmd, git bash 등)에 npm init을 입력하여 node.js 프로젝트를 생성합니다.
$ npm init
프로젝트에 필요한 package도 설치해줍니다.
$ npm install express socket.io --save
프로젝트 생성과정이 이해가 잘 안되시는 분들은 MEAN Stack/개발 환경 구축을 다시 한번 읽어 주시기 바랍니다.
프로젝트 폴더(chatting)를 만들고 server.js파일과 client파일을 생성해 줍니다. 회색 파일들은 git과 github을 위한 파일들이며, package.json은 npm init을 하면 자동으로 생성됩니다.
// 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를 사용하는 경우 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임에 유의합시다.
다음은 client.html입니다. 강좌를 socket.io에 집중하기 위해 style css, script를 html 안에 작성하였습니다.
<!-- 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를 사용하기 위해 반드시 필요한 과정입니다. 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에서 발생합니다.
nodemon을 사용해서 서버를 실행합시다
$ nodemon
http://localhost:3000 로 접속합니다. 탭을 두개 띄우고 서로 각각의 탭에 메세지를 입력해 봅시다.
이때 콘솔을 보면 아래와 같이 메세지가 표시되는 것을 확인할 수 있습니다.
코드는 그렇게 많지도 않고 어렵지도 않죠? 아래 내용을 잘 숙지합시다.
io.on('connection', 함수)
가 event listersocket.on('disconnect', 함수)
가 event listenersocket.on('disconnect', 함수)
가 event listenersocket.emit
io.to(socket.id).emit
io.emit
댓글
이 글에 댓글을 다시려면 SNS 계정으로 로그인하세요. 자세히 알아보기