참고자료: 노마드 코더 줌 클론코딩 https://nomadcoders.co/noom/lobby
1. HTTP vs WebSockets
👉🏻HTTP 서버는 유저에게 먼저 말을 걸 수 없다. client가 요청하면 response 과정 이후 유저가 누구인지 잊어버린다.
👉🏻웹소켓(ws)에서는 서버와 유저가 연결되어 있기 때문에 유저가 요청하면 서버가 답장할 수 있고 먼저 말을 걸 수도 있다.
2. WebSockets in NodeJS
server.js
const server = http.createServer(app);
const wss = new WebSocketServer({ server });
👉🏻express는 http 프로토콜을 다루는데 우리는 둘 다 다루기 위해 위와 같은 작업을 해분다.
- http 서버를 만든다. 이렇게 해야 서버에 접근 가능하다.
- 웹소켓 서버를 만들고 http 서버와 함께 둘 다 작동시킨다. 웹소켓 서버만 구동시키고 싶으면 {server} 인자를 안 넘겨주면 된다.
3. WebSockets Events
server.js
function handleConnection(socket) {
console.log(socket);
}
wss.on("connection", handleConnection);
✍🏻on: JS의 addEventListener와 같은 역할을 한다. connection이라는 행동을 하면 해당 함수를 실행한다.
👉🏻socket에 이벤트에 대한 정보를 담아준다.
app.js
const socket = new WebSocket(`ws://${window.location.host}`);
👉🏻서버로 접속한다.
4. WebSockets Messages
server.js
wss.on("connection", (socket) => {
console.log("Connected to Browser ✅");
socket.on("close", () => console.log("Disconnected from the Browser ❌")); //브라우저에서 열결이 끊기면 실행한다.
socket.on("message", (message) => {
console.log(message);
});
socket.send("hello!!!"); //send로 데이터를 보낸다.
});
👉🏻connection listener를 정의하면 브라우저와 연결이 되었을 때 실행한다.
👉🏻close listener를 정의하면 브라우저와 연결이 끊겼을 때 실행한다.
👉🏻message listener를 정의하면 브라우저에서 메시지가 왔을 때 실행한다.
👉🏻socket.send를 이용해 브라우저로 데이터를 보낸다.
app.js
socket.addEventListener("open", () => {
console.log("Connected to Server!")
})
socket.addEventListener("message", (message) => {
console.log("New message: ", message.data);
});
socket.addEventListener("close", () => {
console.log("Disconnected from Server ❌");
});
setTimeout(() => {
socket.send("hello from the browser!");
}, 10000);
👉🏻open listener를 정의하면 서버와 연결이 되었을 때 실행한다.
👉🏻close listener를 정의하면 서버와 연결이 끊겼을 때 실행한다.
👉🏻message listener를 정의하면 서버에서 메시지가 왔을 때 실행한다.
👉🏻socket.send를 이용해 서버로 데이터를 보낸다.
💡이 연결은 alive하다!
6. Chat Completed
home.pug
main
ul
form
input(type="text", placeholder="write a msg", required)
button Send
server.js
const sockets = [];
function onSocketClose() {
console.log("Disconnected from the Browser ❌");
}
wss.on("connection", (socket) => {
sockets.push(socket);
console.log("Connected to Browser ✅");
socket.on("close", onSocketClose);
socket.on("message", (message) => {
sockets.forEach((aSocket) => aSocket.send(message.toString()));
});
});
👉🏻브라우저에서 받은 메시지를 다시 돌려보낸다.
👉🏻연결된 모든 소켓에 접근하기 위해 forEach를 사용한다. 크롬에게 받으면 크롬에게만 보내고 파이어 폭스에게 받으면 파이어폭스에게만 돌려보내는 현상을 방지하기 위함이다.
app.js
const messageList = document.querySelector("ul");
const messageForm = document.querySelector("form");
...
function handleSubmit(event) {
event.preventDefault();
const input = messageForm.querySelector("input");
socket.send(input.value);
input.value = "";
}
messageForm.addEventListener("submit", handleSubmit);
👉🏻폼의 제출버튼을 누르면 handlSubmit 함수를 실행한다. input의 value를 server로 보낸다.
7-8. Nicknames part One-Two
home.pug
main
form#nick
input(type="text", placeholder="choose a nickname", required)
button Save
ul
form#message
input(type="text", placeholder="write a msg", required)
button Send
👉🏻닉네임을 저장할 form과 메시지를 보낼 form을 만든다.
app.js
const messageList = document.querySelector("ul");
const nickForm = document.querySelector("#nick");
const messageForm = document.querySelector("#message");
const socket = new WebSocket(`ws://${window.location.host}`);
socket.addEventListener("open", () => {
console.log("Connected to Server!")
});
socket.addEventListener("close", () => {
console.log("Disconnected from Server ❌");
});
👉🏻폼들을 변수로 지정하고 소켓에 접속한다.
function makeMessage(type, payload) {
const msg = { type, payload };
return JSON.stringify(msg);
}
👉🏻닉네임이나 메시지를 보내면 서버에 보낼 json으로 변환하는 함수를 만든다.
socket.addEventListener("message", (message) => {
const li = document.createElement("li");
li.innerText = message.data;
messageList.append(li);
});
👉🏻서버에서 받은 메시지를 li tag에 담아 브라우저에서 출력해준다.
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));
input.value = "";
}
messageForm.addEventListener("submit", handleSubmit);
nickForm.addEventListener("submit", handleNickSubmit);
👉🏻각 폼들의 저장 버튼을 누르면 각 폼의 input 값을 server에 보내준다.
server.js
wss.on("connection", (socket) => {
sockets.push(socket);
socket["nickname"] = "Anon";
console.log("Connected to Browser ✅");
socket.on("close", onSocketClose);
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;
break;
}
});
});
👉🏻메시지를 받으면 먼저 json 형태로 파싱해준다. 그리고 메시지 타입에 따라 구별해 닉네임이면 socket["nickname"]에 저장하고 메시지면 브라우저로 보내준다.
💡소켓에 정보를 저장할 수 있다.
🐰성공! :)