[NODE 강의] Socket.io를 이용한 간단한 실시간 채팅

2021. 4. 24. 17:38카테고리 없음

반응형

※ 해당 포스팅은 패스트캠퍼스의 [Node 웹 프로그래밍 올인원 패키지 Online] 수강후기입니다.

 

[클립명]

1. 채팅구현하기 1

2. 채팅구현하기 2

3. 서버 확장 고려하기

이번에는 패캠 강의 중에서 Node.js를 이용하여 실시간 채팅 프로그램을 구현하는 강의를 보면서 공부해보았다.

실시간 채팅은 Socket.io를 이용하여 구현하는데, 일단 Socket.io가 뭔지부터 알아보자.

Socket.io 

Socket.io는 기존의 클라이언트의 요청에 의해 서버의 응답을 돌려받는 단방향 통신을 벗어나 실시간으로 양방향 메시지 송수신을 위해 만들어진 WebSocket을 Node.js에서 쉽게 사용할 수 있도록 해주는 모듈이다. 웹소켓이 가진 단점 중의 하나인 웹브라우저 호환성 문제도 해결할 수 있다. 

일반적인 웹사이트 이용방식은 사용자가 버튼을 클릭하거나 웹페이지 주소를 입력함으로써 서버에 요청을 하고 서버는 해당 요청에 대한 응답을 보내주어 웹브라우저에서는 그 결과값을 화면에 출력하는 방식으로 동작한다. 이런 방법을 단방향 통신이라고 한다. 서버는 클라이언트에서 어떤 요청이 오기 전까지는 메시지를 보내기가 어려웠다.  

 

그런데, 웹소켓이 나오면서 클라이언트와 서버 간에 실시간으로 메시지를 송수신하는 것이 편리해졌다.

클라이언트에서는 서버에서 보내주는 메시지를 Event listener로 등록하여 대기하고 있다가 해당 이벤트가 발생하면 메시지를 즉시 수신하여 사용자에게 해당 정보를 업데이트하여 보여줄 수 있다. 

 

웹소켓과 Socket.io에 대해 좀 더 알아보고 싶다면 여기를 읽어보자! ╰(*°▽°*)╯

 

실시간 채팅 구현하기

■ 모듈 설치

실시간 채팅 구현에 필요한 express, socket.io 모듈을 설치한다.

//npm 초기화
$ npm init -y

//익스프레스 모듈 설치
$ npm install express

//socket.io 모듈 설치
$ npm install socket.io

package.json 내용

{
  "name": "node",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "npx nodemon app.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.17.1",
    "socket.io": "^4.0.1"
  }
}

 

■ 코딩해보기

app.js

const express = require('express');
const listen = require('socket.io');
const app = express();
const port = 3000;

app.get('/', ( _ ,res) => {
    res.sendFile(__dirname + '/index.html');
});

const server = app.listen( port, () => {
    console.log('Express listening on port', port);
});

//소켓서버를 웹서버에 붙인다.
const io = listen(server);

//사용자명 랜덤 생성을 위한 배열
const color = [
    "yellow",
    "green",
    "red",
    "blue",
    "white",
    "black",
]

//소켓서버에 접속
io.on('connection', (socket) => {
    console.log('소켓 서버 접속');
	//소켓서버 접속 시, 사용자명 랜덤 생성
    const username = color[ Math.floor(Math.random() * 6) ];
	//사용자가 방에 들어왔음을 접속자 본인을 제외한 나머지 사람들에게 알려준다.
    socket.broadcast.emit('join', { username });

    //client message 라는 이벤트명으로 대기
    socket.on('client message', data => {
        //연결된 모든 사람에게 메시지 송신
        io.emit('server message', { 
            username : username,
            message : data.message 
        });
        //나를 제외한 다른 사람에게 메시지 송신
        //socket.broadcast.emit('server message', { message : data.message });
    });
	
    //소켓서버 연결 종료  ( disconnect는 예약어 )
    socket.on('disconnect', () => {
    	//사용자가 퇴장했음을 퇴장자 본인을 제외한 다른 사람들에게 알려준다.
        socket.broadcast.emit('leave', { username });
    });

});

index.html

<!doctype html>
<html>
<head>
<title>채팅 프로젝트</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<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: 0.5%; }
    form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }
    #chatLog { list-style-type: none; margin: 0; padding: 0; }
    #chatLog li { padding: 5px 10px; }
    #chatLog li:nth-child(odd) { background: #eee; }
</style>
</head>
<body>
<ul id="chatLog"></ul>
<form action="" id="sendForm">
    <input name="message" autocomplete="off" /><button>전송</button>
</form>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="/socket.io/socket.io.js"></script> 
<script>
//소켓서버에 접속시킨다.
var socket = io();    

//사용자 참가
socket.on('join', data => {
    $('#chatLog').append('<li>' + data.username + '님이 방에 들어왔어유!</li>');
});

//사용자 종료
socket.on('leave', data => {
    $('#chatLog').append('<li>' + data.username + '님이 방에서 나갔어유!</li>');
});

//server message 라는 이벤트명으로 대기
socket.on('server message', function(data){
    console.log(data);
    //소켓서버로부터 수신한 메시지를 화면에 출력한다.
    $('#chatLog').append('<li>' + data.username + ':' + data.message + '</li>');
});

$(document).ready(function(){
    $('#sendForm').submit(function(){
        var message = $('#sendForm input[name=message]');
        //소켓 서버의 'client message' 라는 이벤트명으로 메세지를 송신한다.
        socket.emit('client message', { message : message.val()});
        //input 박스 초기화
        message.val('');
        return false;
    });
});

</script>
</body>
</html>

위에 클라이언트(index.html) 및 서버(app.js) 코딩 파일 전부를 올렸다.  

■ 프로그램 흐름

프로그램 흐름은 아래와 같다.

1. 웹서버에 소켓서버를 붙인다.

2. 사용자가 채팅 페이지에 접근하면, 소켓서버에 접속시킨다.

3. 사용자에게 임의의 이름을 부여하고, 다른 사람들에게 사용자가 참가했음을 알린다.

4. 사용자가 채팅메시지를 입력하면, 메시지를 소켓서버의 특정 이벤트로 전달(emit)한다. 

5. 소켓서버에서 특정 이벤트명을 통해 클라이언트로부터 전달된 메시지를 수신(on)한다.

6. 소켓서버에서 수신한 메시지를 모든 사람들에게 전달(emit)한다.

7. 클라이언트에서 소켓서버로부터 수신(on)한 메시지를 사용자의 화면에 출력해준다.

 

이제 단계별로 하나하나 뜯어서 보자.

 

1. 웹서버에 소켓서버를 붙인다.

// app.js

const listen = require('socket.io');

//라우팅 설정
app.get('/', ( _ ,res) => {
    res.sendFile(__dirname + '/index.html');
});

const server = app.listen( port, () => {
    console.log('Express listening on port', port);
});

//소켓서버를 웹서버에 붙인다.
const io = listen(server);

app.listen 으로 생성한 express server를 'server'라는 변수에 대입하여 저장하고, 'io' 변수에 lisrten(server)를 사용하여 express 서버에 socket.io 서버를 붙인다.

동일 포트에서 2개의 서버는 동시에 사용할 수 없는 것이 일반적이지만, websocket과 HTTP는 같은 포트에서 사용할 수 있다.

socket서버를 붙이면서 'io' 변수에 대입한 이유는 'io'에 소켓서버에서 일어나는 모든 이벤트를 매핑하기 위해서이다.

 

2. 사용자가 채팅 페이지에 접근하면, 소켓서버에 접속시킨다.

// index.html

<script>
//소켓서버에 접속시킨다.
var socket = io();  
</script>

-------------------------------------------------
// app.js

//소켓서버에 접속 시
io.on('connection', (socket) => {
    console.log('소켓 서버 접속');
});

npm start 명령어로 서버를 실행한 후, http://localhost:3000/ 으로 접근하면 라우팅 설정에 의해 index.html 페이지가 출력된다. 따라서, index.html 페이지의 스크립트 부분에 var socket = io(); 를 이용하여 소켓서버에 접속시킨다.

진짜 간단하다.

 

3. 사용자에게 임의의 이름을 부여하고, 다른 사람들에게 사용자가 참가했음을 알린다.

// app.js 

const color = [
    "yellow",
    "green",
    "red",
    "blue",
    "white",
    "black",
]

//소켓서버에 접속
io.on('connection', (socket) => {
	//사용자명 랜덤 생성
    const username = color[ Math.floor(Math.random() * 6) ];
	//소켓서버에서 클라이언트 쪽으로 join이라는 이벤트명을 이용하여 메시지 송신
    socket.broadcast.emit('join', { username });
    
    ....중략
    
    -----------------------------------------
    
// index.html

//join이라는 이벤트명으로 메시지 수신 대기
socket.on('join', data => {
    $('#chatLog').append('<li>' + data.username + '님이 방에 들어왔어유!</li>');
});

소켓서버에 접속한 사용자에게 임의의 이름을 랜덤하게 부여하고, 다른 사람들에게 알려준다. 참가자 본인에게는 자신이 방에 들어왔다는 사실은 알려줄 필요가 없으므로 socket.broadcast.emit 으로 메시지를 송신한다.

 

4. 사용자가 채팅메시지를 입력하면, 메시지를 소켓서버의 특정 이벤트로 전달(emit)한다. 

<form action="" id="sendForm">
    <input name="message" autocomplete="off" /><button>전송</button>
</form>

<script>
$(document).ready(function(){
    $('#sendForm').submit(function(){
        var message = $('#sendForm input[name=message]');
        //소켓 서버의 'client message'라는 이벤트로 메시지를 송신한다.
        socket.emit('client message', { message : message.val()});
        //input박스에 입력한 텍스트 초기화
        message.val('');
        return false;
    });
});
</script>

사용자가 input 박스에 메시지를 입력하고 submit이 발생하면, 메시지를 socket.emit('이벤트명', {키:밸류}) 를 이용하여 소켓서버로 송신한다.

 

5. 소켓서버에서 특정 이벤트명을 통해 클라이언트로부터 전달된 메시지를 수신(on)한다.

//소켓서버에 접속
io.on('connection', (socket) => {
    console.log('소켓 서버 접속');
    //client message 라는 이벤트명으로 대기
    socket.on('client message', data => {
        console.log(data);
    });
});

4번에서 전달된 메시지를 소켓서버에서 수신하는 부분이다.

socket.on('이벤트명', callback(data)); 형식으로 해당 이벤트명으로 이벤트가 발생하는 것을 listen하고 있다.

 

6. 소켓서버에서 수신한 메시지를 모든 사람들에게 전달(emit)한다.

socket.on('client message', data => {
  console.log(data);
  //연결된 모든 사람에게 메시지 송신
  io.emit('server message', { 
  	message : data.message
  });
});

5번에서 수신한 메시지를 현재 소켓서버에 접속한 모든 사람들에게 다시 송신하는 부분이다.

이벤트를 보낼 때는 emit 으로 처리한다는 것을 잊지 말자.

 

7. 클라이언트에서 소켓서버로부터 수신(on)한 메시지를 사용자의 화면에 출력해준다.

//index.html 

<ul id="chatLog"></ul>

<script>
//소켓서버로부터 수신한 메시지를 화면에 출력한다.
socket.on('server message', function(data){
    //소켓서버로부터 수신한 메시지를 화면에 출력한다.
    $('#chatLog').append('<li>' + data.username + ':' + data.message + '</li>');
});
</script>

6번에서 소켓서버에서 클라이언트로 송신한 메시지를 수신하는 부분이다.

이벤트를 수신할 때는 on 으로 처리한다는 것을 잊지 말자.

수신한 메시지를 받아서 chatLog 엘리멘트에 제이쿼리를 이용하여 append 하여 채팅 메시지를 화면에 출력한다.

 

크롬과 엣지 브라우저를 띄워서 테스트해봤다. 

 

여기까지가 진짜 간단한 실시간 채팅을 구현하는 소스이다.

여기에 채팅방이라는 기능을 도입하려면 Socket.io의 Namespace와 Room이라는 개념에 대해 공부해야 한다.

 

 

 

https://bit.ly/39FFmk0

 

Node 웹 프로그래밍 올인원 패키지 Online. | 패스트캠퍼스

자바스크립트로 서버 개발까지 완벽 커버하는 온라인 과정입니다.

fastcampus.co.kr

 

 

반응형