https://nomadcoders.co/noom/lobby
노마드코더의 줌 클론코딩 강의를 보고 정리한 내용입니다.
HTTP
http는 모든 서버들이 작동하는 방식
유저가 req를 보내면 서버가 res로 반응한다.
http에서 기억해야할 중요한 것은 stateless, 즉 백엔드가 유저를 기억하지 못한다. 유저와 백엔드 사이에 연결이 없다.
서버는 오직 req을 받을 때만 답장을 해준다.
Websocket
webSocket을 사용해서 연결하고 싶고, 서버가 지원한다면 wss(secure web socket)으로 접속하면 된다.
ex) https://nomadcoders.co => wws://nomadcoders.co
과정
브라우저가 서버로 webSocket request를 보내면 서버가 받거나 거절한다. 수락하면 연결이 성립된다.
연결되어있기 때문에 서버는 사용자가 누구인지 기억할 수 있다. 그리고 요청이 없어도 유저에게 메시지를 보낼 수 있다.
유저는 언제든지 서버에 메시지를 보낼 수 있고, 서버도 언제든지 유저에게 메시지를 보낼 수 있다.
bi-directional 연결이 성립하고, webSocket은 브라우저 -백엔드간, 백엔드-백엔드 간에 연결에서 사용된다.
클라이언트 측
const messageList = document.querySelector("ul");
const messageForm = document.querySelector("form");
//socket : 서버로의 연결
const socket = new WebSocket(`ws://${window.location.host}`);
//서버와 연결
socket.addEventListener("open", () => {
console.log("Connected to Server ✅");
});
//메시지
socket.addEventListener("message", (message) => {
console.log("New message : ", message.data, "from the Server");
});
//서버 연결 종료
socket.addEventListener("close", () => {
console.log("Disconnected from Server ❌");
});
setTimeout(() => {
socket.send("hello from the browser!");
}, 5000);
function handleSumbit(event) {
event.preventDefault();
const input = messageForm.querySelector("input");
socket.send(input.value);
input.value = "";
}
messageForm.addEventListener("submit", handleSumbit);
서버측
import express from "express";
import http from "http";
import WebSocket from "ws";
const app = express();
app.set("view engine", "pug");
app.set("views", __dirname + "/views");
app.use("/public", express.static(__dirname + "/public"));
app.get("/", (req, res) => res.render("home"));
//어떤 경로를 들어와도 항상 /로 리다이렉트
app.get("/*", (req, res) => res.redirect("/"));
const handleListen = () => console.log("Listening ong http://localhost:3000");
const server = http.createServer(app); //http 서버
// http서버 위에 wss만들기
const wss = new WebSocket.Server({ server }); //webSocket 서버 + http 서버를 합쳐서 하나의 포트를 사용하기 위해서
wss.on("connection", (socket) => {
//socket : 연결된 브라우저
console.log("Connected to browser ✅");
console.log(socket);
//브라우저와 연결 종료
socket.on("close", () => {
console.log("Disconnected from the Browser ❌");
});
//브라우저에서 메세지 받기
socket.on("message", (message) => {
socket.send(message.toString());
});
//브라우저에 메시지 보내기
// socket.send("hello");
});
server.listen(3000, handleListen);
그러나 위의 코드는 다른 사람들과의 소통은 안되고 클라이언트와 서버와의 소통만 가능한 상태이다.
예를 들어 크롬에서 접속하고, 파이어폭스에서 접속을 한 상태에서 크롬에서 메시지를 보내도 파이어폭스에게는 해당 메시지를 전달이 안된다.
각각 다른 브라우저와 소통을 하기 위해서 브라우저와 서버가 연결 됐을때 소켓 배열에서 해당 소켓을 추가해준다.
const sockets = [];
wss.on("connection", (socket) => {
//socket : 연결된 브라우저
//연결된 브라우저들을 sockets 배열에 넣어주기
sockets.push(socket);
}
그리고 서버에서 메시지를 받으면 해당 메시지을 sockets에 있는 다른 브라우저에게 보내준다.
//브라우저에서 메세지 받기
socket.on("message", (message) => {
sockets.forEach((aSocket) => aSocket.send(message.toString()));
});
그리고 브라우저에서는 메시지를 받으면 콘솔이 아닌 li태그로 보여주도록 수정해준다.
//메시지 받기
socket.addEventListener("message", (message) => {
const li = document.createElement("li");
li.innerText = message.data;
messageList.append(li);
});
닉네임 설정
닉네임을 설정해주도록 해보자
메시지가 올 때 닉네임, 메시지 등의 종류를 구별해주기 위해서 타입을 구별해야한다. 그러기 위해서 string으로 보냈던 메시지를 json으로 보내보도록 하자
메시지의 타입과 내용을 서버에게 json으로 보내기
function makeMessage(type, payload) {
const msg = { type, payload };
return JSON.stringify(msg);
}
json객체로 변환하는 함수 선언해주고
function handleSubmit(event) {
event.preventDefault();
const input = messageForm.querySelector("input");
socket.send(makeMessage("new_message", input.value));
input.value = "";
}
function handleNickSubmit(event) {
event.preventDefault();
const input = nickForm.querySelector("input");
socket.send(makeMessage("nickname", input.value));
}
다음과 같이 서버에게 메시지를 전송할 때는 위에서 만들었던 함수를 사용해서 json 객체로 보내준다.
이상태에서 서버에 메시지를 받을 때 콘솔을 출력해보면
다음과 같은 형태의 스트링으로 출력이 된다. 그래서 이걸 JSON객체로 변환 후 사용해주도록 한다.
//브라우저에서 메세지 받기
socket.on("message", (message) => {
const parsed = JSON.parse(message);
console.log(parsed);
console.log(message.toString());
sockets.forEach((aSocket) => aSocket.send(message.toString()));
});
서버에서는 메시지 type에 따라서 다른 처리를 해준다.
//브라우저에서 메세지 받기
socket.on("message", (msg) => {
const message = JSON.parse(msg);
switch (message.type) {
case "new_message":
sockets.forEach((aSocket) =>
aSocket.send(`${socket.nickname} : ${message.payload}`)
);
case "nickname":
//socket 객체에 nickname을 추가
socket["nickname"] = message.payload;
}
});
타입이 "new_message"인 경우에는 다른 소켓들에게 닉네임과 같이 메시지를 보내주고,
"nickname"인 경우에는 socket에 nickname을 설정해준다.
'개발 공부 > Web' 카테고리의 다른 글
[Web] WebRTC (0) | 2022.07.29 |
---|---|
[Web]SocketIO (0) | 2022.07.27 |
라디오 버튼을 이미지로 구현하기 [이전 블로그 게시글] (0) | 2022.02.16 |
[정리] 웹 폰트란? [이전 블로그 게시글] (0) | 2022.02.16 |