구름 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
이번 프로젝트에서 만약 pc에서 서버를 열었다면 스마트폰으로 서버에 접속하여 서로 채팅을 할 수 있게 하기 위해 방화벽에서 새로운 규칙을 설정해야 한다는 것을 알게되었습니다. 해당 내용은 따로 포스팅하여 정리하였으니 위의 포스팅을 참고하시기 바랍니다.
<실행 영상>
https://www.youtube.com/watch?v=JfAwL7EjLzg
참고자료 - https://edu.goorm.io/lecture/557/한-눈에-끝내는-node-js
'👨🏻💻Project' 카테고리의 다른 글
[Project] Todo List App 개발 (0) | 2023.09.04 |
---|---|
[Project] ToDo List 앱 만들기 #1 (메인 페이지 제작) (0) | 2022.02.28 |
[Project] socket.io를 이용한 채팅 앱 구현 <#3 채팅 입력 시 채팅로그에 표시하기> (0) | 2021.06.04 |
[Project] socket.io를 이용한 채팅 앱 구현 <#2 채팅창 디자인하기> (0) | 2021.06.03 |
[Project] socket.io를 이용한 채팅 앱 구현 <#1 express 새프로젝트 생성> (0) | 2021.06.02 |