[Node.js] 혼자 만들어본 채팅 웹 앱(서버 부분)

이 포스트는 “[React] 혼자 만들어본 채팅 웹 앱”의 서버 부분 설명입니다.

관련 글을 보고싶은 분은 글 최하단의 태그 Toy project 링크를 눌러 확인해보세요!


소켓의 개념이 조금 어려울 수 있으나 막상 코드는 몇줄 되지 않는다.

저장소 주소

설치

1
2
3
4
cd workspace
git clone https://github.com/dev-sawd/chat-server
cd chat-server
npm install

실행

1
node app.js

또는

1
nodemon app.js

개발할때는 코드의 변경사항을 바로바로 체크할 수 있게 nodemon으로 돌리는것이 편리하다.

설명

1
2
const userNameToSocketId = {}
const socketIdToUserName = {}

로그인한 유저의 소켓 아이디와 소켓 아이디의 유저 이름을 관리하는 Dictionary 형태의 변수이다.
로그인때 해당 변수 안에 정보를 담고 로그아웃했을때 해당 변수들에서 정보를 삭제한다.

1
2
3
io.on('connection', (socket) => {
// ...
})

React에서 로그인화면이 열렸을때 소켓의 연결 먼저 실행한다.

로그인과는 다른 개념으로 로그인 전에 소켓이 연결 된 후 해당 소켓을 이용해 로그인, 메세지 보내기, 로그아웃 등의 이벤트를 보낼 수 있다.

해당 소켓과 이 블록 안에 정의된 이벤트를 이용해 각 로직을 처리한다.

connection 안의 이벤트

1
2
3
4
5
6
7
8
9
10
11
socket.on('login', ({userName}) => {
// 이미 존재하는 아이디 체크
if (!userNameToSocketId.hasOwnProperty(userName)) {
userNameToSocketId[userName] = socket.id
socketIdToUserName[socket.id] = userName
socket.broadcast.emit('loginUser', userName)
socket.emit('returnLoginResponse', true, Object.keys(userNameToSocketId))
} else {
socket.emit('returnLoginResponse', false, null)
}
})

로그인 이벤트가 발생했을때 이미 존재하는 아이디인지 확인 후 returnLoginResponse 이벤트를 클라이언트에게 발생시킨다.

returnLoginResponse 이벤트를 받은 클라이언트는 Alert을 이용해 아이디가 중복되었다고 사용자에게 알리거나 로그인 처리를 진행한다.

기존에 로그인한 아이디가 아니라면 모든 클라이언트에 loginUser 이벤트를 발생시킨다.

loginUser 이벤트를 수신한 모든 클라이언트는 좌측 로그인한 유저 리스트에 이 유저 정보를 추가한다.

1
2
3
4
5
6
7
8
9
socket.on('sendMessage', ({sendUserName, targetUserName, message}) => {
// 다른사람에게 보내는 메세지일때
if (sendUserName === targetUserName) {
io.to(userNameToSocketId[targetUserName]).emit('message', ({sendUserName, targetUserName, message}))
} else {
io.to(userNameToSocketId[sendUserName]).emit('message', ({sendUserName, targetUserName, message}))
io.to(userNameToSocketId[targetUserName]).emit('message', ({sendUserName, targetUserName, message}))
}
})

클라이언트로 부터 메세지를 보내는 이벤트가 발생했을때 처리한다.

자신에게 보내는 메세지인지, 다른사람에게 보내는 메세지인지를 확인한다.

자신에게 보내는 메세지라면 자신에게 message 이벤트를 발생시킨다.

다른사람에게 메세지라면 자신과 수신자에게 message 이벤트를 각각 발생시킨다.

message 이벤트를 받은 클라이언트는 메모리에 이 정보를 저장하고 채팅방이 활성화 되었을때 말풍선형태의 UI로 보여준다.

1
2
3
4
5
6
7
8
9
socket.on('disconnect', () => {
// 로그아웃 처리
var userName = socketIdToUserName[socket.id]

delete userNameToSocketId[userName]
delete socketIdToUserName[socket.id]

socket.broadcast.emit('logoutUser', userName)
})

클라이언트가 로그아웃(또는 강제 종료) 했을때 userNameToSocketIdsocketIdToUserName 변수에서 해당 정보를 삭제하고 logoutUser 이벤트를 접속한 모든 클라이언트에 발생시킨다.

logoutUser 이벤트를 수신한 모든 클라이언트는 좌측 로그인 유저 리스트에서 해당 유저를 삭제한다.

정리

간단한 소켓을 이용한 채팅 서버는 이정도 수준에서 개발했다.

이것을 토대로 조금 더 개발한다면 DB를 붙여서 회원관리, 이전 메세지 관리 등의 기능을 확장할 수 있다.