반응형

구름 EDU에서 수강한 Node.js 강의 내용을 토대로 만든 socket.io를 이용한 채팅 앱 구현 프로젝트입니다.  

저번 포스팅에서는 채팅을 입력하고 전송 시 채팅 로그에 닉네임과 채팅 내용이 표시되도록 구현하였고 이번 포스팅에서는 사용자가 접속, 종료, 닉네임 변경에 대한 알림을 채팅 로그에 표시하는 것을 구현하는 방법에 대한 내용입니다.

 

<구현할 기능>

  • 사용자 간의 채팅
  • 사용자 접속 혹은 종료 시 알림
  • 닉네임 변경과 알림

 

 

로컬서버에 접속 후 채팅 & 닉네임 변경
새로고침 후 채팅

 

<전체 코드>

<chat.pug>

// chat.pug

doctype 5
html
    head
        title= 'Chat'
        link(rel='stylesheet', href='/stylesheets/style.css')
        meta(name="viewport" content="width=device-width, initial-scale=1")

        // socket.io와 jquery를 사용하기 위한 script src
        script(src='/socket.io/socket.io.js')
        script(src='//code.jquery.com/jquery-1.11.1.js')

        // 부트스트랩을 사용하기 위한 link와 script
        link(rel="stylesheet", href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css", integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous")
        script(src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js", integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous")  

    body
        // top
        div 
            button#top.btn.btn-warning(type='button') Chatting-App  
        // textarea
        div
            textarea#chatLog.form-control(readonly='')  
        // name, text, submit
        div
            form#chat
                input#name.form-control(type='text')
                input#message.form-control(type='text')
                button#submit.btn.btn-warning(type='submit') Submit
        
        script.
            var socket = io();

            // 메시지 전송
            $('#chat').on('submit', function(e){
                // 'send message 이벤트 발생(name, message input 전달)
                socket.emit('send message', $('#name').val(), $('#message').val());
                // message input에 들어있는 값 초기화
                $('#message').val('');
                // 사용자가 링크를 클릭하거나 폼을 제출해도 계속 같은 페이지에 머무르게 함
                e.preventDefault();
            });

            // 이름 생성
            socket.on('create name', function(name){
                // 전달받은 name input값으로 name 설정
                $('#name').val(name);
            });

            // 이름 변경
            socket.on('change name', function(oldname, name){
                $('#chatLog').append('[알림] ' + oldname + '님이 ' + name + '으로 닉네임을 변경하였습니다.\n');
            });

            // 메세지 수신
            socket.on('receive message', function(msg){
                // 메세지 출력
                $('#chatLog').append(msg+'\n');
                // 채팅창 스크롤이 생길 시 제일 아래쪽으로 스크롤을 내려줌
                $('#chatLog').scrollTop($('#chatLog')[0].scrollHeight);
            });
	  
            // 채팅방 접속
            socket.on('new connect', function(name){
                $('#chatLog').append('[알림] ' + name + '님이 채팅창에 입장하였습니다.\n');
            });

            // 채팅방 종료
            socket.on('new disconnect', function(name){
                $('#chatLog').append('[알림] ' + name + '님이 채팅창을 떠났습니다.\n');
            });
            
            

 

<Code Description>

// 메시지 전송
$('#chat').on('submit', function(e){
  // 'send message 이벤트 발생(name, message input 전달)
  socket.emit('send message', $('#name').val(), $('#message').val());
  // message input에 들어있는 값 초기화
  $('#message').val('');
  // 사용자가 링크를 클릭하거나 폼을 제출해도 계속 같은 페이지에 머무르게 함
  e.preventDefault();
});

채팅 입력 후 form에서 제출(submit) 시 'send message' 이벤트가 발생하며 name input값과 message input값을 서버로 전달해줍니다. e.preventDefault(); 명령어가 없을 경우 submit 하는 순간 페이지가 새로고침 되면서 모두 초기화되고 새로운 id를 발급받게 됩니다.  

 

// 이름 생성
socket.on('create name', function(name){
  // 전달받은 name input값으로 name 설정
  $('#name').val(name);
});

// 이름 변경
  socket.on('change name', function(oldname, name){
  $('#chatLog').append('[알림] ' + oldname + '님이 ' + name + '으로 닉네임을 변경하였습니다.\n');
});

create name의 경우 서버에서 부여해주는 이름(count 수에 따른 익명 1, 익명 2와 같은 닉네임들)으로 name input값을 변경해주며 

change name의 경우 서버에서 oldname(이전 닉네임)과 name(현재 닉네임)을 받아서 채팅 로그에 표시해줍니다.

 

// 메세지 수신
socket.on('receive message', function(msg){
  // 메세지 출력
  $('#chatLog').append(msg+'\n');
  // 채팅창 스크롤이 생길 시 제일 아래쪽으로 스크롤을 내려줌
  $('#chatLog').scrollTop($('#chatLog')[0].scrollHeight);
});

receive message는 서버에서 msg를 받아서 채팅 로그에 표시해주며
$('#chatLog').scrollTop($('#chatLog')[0].scrollHeight); 명령어를 통해 채팅창에 스크롤이 생겨도 가장 최신의 채팅 보이도록 제일 아래쪽으로 스크롤을 내려줍니다.

 

// 채팅방 접속
socket.on('new connect', function(name){
	$('#chatLog').append('[알림] ' + name + '님이 채팅창에 입장하였습니다.\n');
});

// 채팅방 종료
socket.on('new disconnect', function(name){
  $('#chatLog').append('[알림] ' + name + '님이 채팅창을 떠났습니다.\n');
});

new connect와 new disconnect는 서버에서 name값을 전달받아 접속자가 채팅방에 입장하거나 떠났음을 채팅 로그에 알림으로 표시합니다.

 

 

<server.js>

// server.js

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

app.set('views', './views');
app.set('view engine', 'pug');
app.use(express.static(path.join(__dirname, 'public')));

app.get('/', (req, res) => {
	// 루트 페이지로 접속시 chat.pug 렌더링
	res.render('chat');
});

var count = 1; // 참여자 번호 발급

// 채팅방 접속시
io.on('connect', function(socket){
    //id는 영어와 숫자를 사용한 문자열로 자동생성
    console.log('user connected: ', socket.id);
    var name = "익명" + count++;
    socket.name = name;
    // 서버가 해당 socket id에만 이벤트 전달
    io.to(socket.id).emit('create name', name);
    io.emit('new connect', name);

    // 채팅방에서 나갔을 때
    socket.on('disconnect', function(){
        console.log('user disconnected: ' + socket.id + ' ' + socket.name);
        io.emit('new disconnect', socket.name);
    });

    // 채팅을 보냈을 때
    socket.on('send message', function(name, text){
        var msg = name + ' : ' + text;
        // 닉네임 변경 시
        if(socket.name != name){
            io.emit('change name', socket.name, name);
            socket.name = name; // name 업데이트
        }
        console.log(msg);
        io.emit('receive message', msg);
    });
});


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

 

<Code Description>

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

app.set('views', './views');
app.set('view engine', 'pug');
app.use(express.static(path.join(__dirname, 'public')));

app.get('/', (req, res) => {
	// 루트 페이지로 접속시 chat.pug 렌더링
	res.render('chat');
});

express를 이용하여 http 서버를 열고 socket.io 서버를 http에 연결해줍니다. 루트 페이지(localhost:3000/)로 접속 시 클라이언트에 chat.pug를 전송합니다. 

 

var count = 1; // 참여자 번호 발급

// 채팅방 접속시
io.on('connection', function(socket){
    //id는 영어와 숫자를 사용한 문자열로 자동생성
    console.log('user connected: ', socket.id);
    var name = "익명" + count++;
    socket.name = name;
    // 서버가 해당 socket id에만 이벤트 전달
    io.to(socket.id).emit('create name', name);
    io.emit('new connect', name);

    // 채팅방에서 나갔을 때
    socket.on('disconnect', function(){
        console.log('user disconnected: ' + socket.id + ' ' + socket.name);
        io.emit('new disconnect', socket.name);
    });

    // 채팅을 보냈을 때
    socket.on('send message', function(name, text){
        var msg = name + ' : ' + text;
        // 닉네임 변경 시
        if(socket.name != name){
            io.emit('change name', socket.name, name);
            socket.name = name; // name 업데이트
        }
        console.log(msg);
        io.emit('receive message', msg);
    });
});

채팅방 서버에 접속 시 접속자는 새로운 id와 "익명+count"(익명 1, 익명 2,...)라는 닉네임을 발급받습니다. 이때 접속 카운트를 세기 위한 변수로 count변수를 사용합니다.

서버에서 count 변수를 통해 닉네임을 만들어 name이란 변수에 담고 해당 socket.id를 가진 클라이언트에만 create name 이벤트를 name과 함께 전달합니다. 그리고 모든 클라이언트 측에 new connect 이벤트를 발생시킵니다. 

disconnect 이벤트의 경우 이미 예약되어있는 이벤트로(이름 변경 x) 클라이언트와 연결이 끊어졌을 때 발생합니다. disconnect 이벤트가 발생하면 서버에서 클라이언트로 new disconnect 이벤트를 발생시킵니다.

클라이언트에서 화면 하단에 메시지를 입력하고 submit을 하게 되면 서버로 send message 이벤트를 발생시키고 서버에서는 name과 text를 조합하여 msg로 만들고 다시 클라이언트로 receive message 이벤트를 발생시킵니다. 

여기서 만약 해당 소켓의 현재 name(socket.name)과 전달받은 name이 다르다면 클라이언트로 change name 이벤트도 발생시키고 name도 업데이트해주게 됩니다. 

 

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

localhgost 3000번 포트로 서버를 엽니다.

 

<stylesheets/style.css>

#top{ /*최상단 프로젝트 이름*/
  width: 100%;
}
#chatLog{ /*채팅 기록*/
  height: calc(100% - 76px);
}
#chat{ /*이름, 메시지, 전송버튼*/
  display: flex;
}
#name{
  width: 20%;
}
#message{
  width: 60%;
}
#submit{
  width: 20%;
}

화면에 채팅창이 꽉 차게 표시되게 하기 위해 상단 top높이 38px, 하단 메시지 전송 부분 높이 38px이기 때문에 채팅 로그의 height를 100%-76px로 설정하였습니다.

그리고 name, text, submit 박스가 일렬로 정렬되게 하기 위해 displaymode를 flex로 지정하였습니다.

 

<다른 기기로 채팅방에 참여하는 방법>

https://cocoon1787.tistory.com/556

 

[WEB] PC에서 실행중인 localhost 페이지를 모바일에서 확인하는 방법

채팅 앱을 개발하면서 다른 전자기기로 서로 채팅을 주고받는 것이 가능한지 확인해보기 위해 pc에서 실행한 localhost페이지에 모바일로 접속을 하는 방법을 찾아서 알아낸 내용을 포스팅하려고

cocoon1787.tistory.com

이번 프로젝트에서 만약 pc에서 서버를 열었다면 스마트폰으로 서버에 접속하여 서로 채팅을 할 수 있게 하기 위해 방화벽에서 새로운 규칙을 설정해야 한다는 것을 알게되었습니다. 해당 내용은 따로 포스팅하여 정리하였으니 위의 포스팅을 참고하시기 바랍니다.

 

<실행 영상>

https://www.youtube.com/watch?v=JfAwL7EjLzg 

 

 

 

 

 

참고자료 - https://edu.goorm.io/lecture/557/한-눈에-끝내는-node-js

 

한 눈에 끝내는 Node.js - 구름EDU

이미 모두 갖추어진 실습환경에서 직접 코드를 작성하고 실행하며 Node.js(노드)의 기본을 다질 수 있는 프로그래밍 강좌입니다.

edu.goorm.io

 

반응형

+ Recent posts