Node.js에서 WebSocket을 이용한 채팅엔진 만들기

이진우·2022년 3월 2일
0
post-thumbnail
post-custom-banner

node.js를 기반으로 클라이언트와 서버간의 채팅프로그램을 만드는 코드를 작성해 보겠습니다.

우선 필요한 npm은 세가지를 설치해 주세요

"ejs": "^3.1.6",
"express": "^4.17.2",
"ws": "^7.5.3"

express는 서버오픈을 위해 설치하고,
ejs는 노드js에서 html,css,js를 유기적으로 사용하는 npm,
ws는 사용자의 브라우저와 서버 사이의 인터액티브 통신 세션을 설정할 수 있게 하기위해 설치해 줍니다.

그후 server.js에서 설치한 두가지를 import 해줍니다. 그후 기본 서버를 오픈해줍니다

import express from 'express';
import Websocket from 'ws';
const app = express();
const port = 3400;

const handleListening = () => console.log(`Server opened on port ${port}`)
app.listen(port,handleListening)

그후 기본 서버(app)를 기본 http프로토콜로 잡기위해 server 변수에 담아주고
WebSocket에 연결됐을때 사용하기 위해 새로운 wss변수에 담아주고 server변수를 넣어준다
그다음 app.listen으로 오픈한 서버를 server.listen으로 변경해준다

const server = http.createServer(app);
const wss = new WebSocket.Server({server})

server.listen(port,handleListening)
// 기존 app.listen(port,handleListening)에서 변수를 새로 담았기때문에 바꿔준다.
(바꾸지 않을시 에러발생)

그다음 .ejs 파일에서 기본 채팅 구조를 만들어 줍니다

<div id="chatbox">
    <div id="chatform">
        <form id="nickname">
            <input type="text" placeholder="닉네임을 만들어 주세요."><button>저장</button>
        </form>
        <ul>
        </ul>
        <form id="message">
            <input type="text" placeholder="메세지를 입력하세요."><button>전송</button>
        </form>
        <a href="#" class="close">x</a>
    </div>
</div>

이번 채팅창은 ul에 li를 쌓아가며 채팅이 되는 방식으로 실행을 할것 이므로 ul은 비워둡니다.

내부스크립트에서 코드를 작성해 주면 되는데 버튼에 클릭 발생시 서버에 전송이 되는 코드를 만들어 줍니다.

<script>
	const messageList = document.querySelector("#chatform ul")
	const messageForm = document.querySelector("#message")
	const nickForm = document.querySelector("#nickname")
    
    // 서버와 사용자의 인터액티브 세션을 설정해 주는 코드
    const socket = new WebSocket(`ws://${window.location.host}`)
    
    // 버튼 클릭시 submit이벤트 발생후 핸들러 설정
    messageForm.addEventListener("submit", handleSubmit);
	nickForm.addEventListener("submit", handleNickSubmit);
    
    // 각 핸들러의 함수를 만들어 socket에 input.value를 send한다
function handleSubmit(e){
		e.preventDefault();
		const input = messageForm.querySelector("input");
		socket.send(makeMessage("new_message", input.value));
		input.value = "";
	}

	function handleNickSubmit(e){
		e.preventDefault();
		const input = nickForm.querySelector("input");
		socket.send(makeMessage("nickname", input.value));
		input.value = "";
	}
    // 여기서 makeMessage함수는 각각 { nickname : input.value },{ new_message: input.value} 이다
    function makeMessage(type, payload) {
		const msg = {type, payload};
		return JSON.stringify(msg)      
	}
    
    // 그후 socket에 message 이벤트 발생시 message의 data를 변수에 담아 ul안에 append해준다
    socket.addEventListener("message", (message)=>{
		const li = document.createElement("li")
		li.textContent = message.data
		messageList.append(li)
	})	
   
</script>

여기까지 내부 스크립트로 socket을 서버와 연결하고 메세지 이벤트 발생시 서버에 객체를 보내고 유저페이지에 li를 쌓아서 채팅창을 만들어주는 코드입니다.

주의하실점은 서버간의 데이터가 오갈땐 반드시 text형태로 주고 받아야 하기 때문에 return JSON.stringify(msg) 객체로 만들어진것을 문자열로 바꿔서 makeMessage에 던져줘야 합니다.

다시 server.js에서 유저가 보낸 데이터를 받아주는 코드를 작성합니다.

const sockets = [];

빈 배열을 sockets변수에 담아 connection이 되었을때 sockets변수에 push해주기위해 준비해 둡니다.

wss.on("connection",(socket)=>{
    sockets.push(socket)
    socket["nickname"] = "유저";
    console.log("Connected to Browser");
    socket.on("close", ()=>console.log("Disconnected to Browser"));
    socket.on("message", (msg)=>{
        const message = JSON.parse(msg);
        switch(message.type) {
            case "new_message":sockets.forEach((aSocket) => aSocket.send(`${socket.nickname}: ${message.payload}`)); break;
            case "nickname":socket["nickname"]=message.payload; 
        }
    });
})

첫줄부터 차근차근 이해해 보겠습니다 (숫자는 코드 줄수입니다.)
1: connection이 되었을때 콜백함수(화살표함수)의 매개변수는 아무렇게 정해주시면되고 저는 socket이라는 이름으로 정했습니다

2: 그래서 미리만들어둔 sockets 배열에 socket데이터를 push해주기위한 코드이고

3: socket["nickname"] = "유저"; 이코드는 유저가 닉네임을 정하지 않았을 경우를 대비해 이름을 미리넣어주는 코드입니다.

4,5: 콘솔영역에 성공,실패를 찍어주는 코드입니다(없어도 무관)

6: connect가 되고 socket에 message 이벤트가 발생 했을때 매개변수(이것도 임의로 설정)msg에 실제 유저가 보낸 텍스트(객체를 텍스트로 바꿨기때문) 데이터가 넘어와 담겨있는 상태입니다.

7: text로 들어온 데이터를 다시 객체로 바꿔주기 위해 새로운 message변수에 파싱을 해주었습니다.

8,9,10 : message의 타입을 뒤져 new_message일 경우 메세지만 왔다고 간주하고 이미 만들어놓은 '유저'라는 이름으로 메세지를 출력하고
두번째 nickname이 함께 들어온 경우 닉네임과 메세지를 함께 출력해주는 코드입니다.
이경우는 if문으로도 해결할 수 있어서 더 편하신 방법으로 문제를 푸시면 좋을것 같습니다

첫글이라 부족한 부분도 많을것 같고 빠진 부분도 있을 수 있지만 좋게 봐주시면 감사하곘습니다 😎
(글에 오류가 있다면 댓글 부탁드립니다‼️)

profile
초보개발자의 개발일기
post-custom-banner

0개의 댓글