1. home.pug 수정하기
채팅룸을 구별할 수 있게 되었으니 같은 채팅룸에 접속한 사용자들끼리만 메시지를 주고받을 수 있게 만들어보자.
다음의 코드를 home.pug에 추가하자
div#room
ul
form
input(placeholder="message", required, type="text")
button Send
<결과화면>
2. app.js 수정하기
메시지를 입력하는 폼을 추가했지만 처음에는 이 폼을 숨겨야 한다.
왜냐하면 채팅룸에 아직 접속하지 않은 상태이기 때문.
룸 이름을 입력해 채팅룸에 접속하고 나면 반대로 룸 이름 폼이 사라지고 메시지 폼이 보이도록 app.js를 수정하자.
const socket = io();
const welcome = document.getElementById("welcome");
const form = welcome.querySelector("form");
const room = document.getElementById("room");
room.hidden = true;
function showRoom(){
welcome.hidden = true;
room.hidden = false;
}
function handleRoomSubmit(event){
event.preventDefault();
const input = form.querySelector("input");
socket.emit("enter_room", input.value, showRoom);
input.value = "";
}
form.addEventListener("submit", handleRoomSubmit);
처음에는 id가 room인 메시지 입력 폼을 숨겨 두었다가, socket의 emit 메서드를 통해 enter_room 이벤트가 발생하면 showRoom함수를 실행시켜 메시지 입력 폼을 보여주고, 이때 거꾸로 id가 welcome인 영역 안의 룸 이름 입력 폼을 숨김.
3. server.js 수정
emit을 통해 showRoom 함수를 서버로 전달 했으니 서버에서 enter_room 이벤트가 발생했을 때,
서버에서 이를 호출해 주어야 한다.
다음의 코드 추가
여기서 done();은 브라우저(app.js)가 요청한 콜백 매개체!
→ showRoom() 메서드를 실행시켜준다는 의미임을 이해해야함.
4. 채팅 룸 이름 표시하기
4-1)home.pug 코드를 다음과 같이 변경
doctype html
html(lang="en")
head
meta(charset="UTF-8")
meta(http-equiv="X-UA-Compatible", content="IE=edge")
meta(name="viewport", content="width=device-width, initial-scale=1.0")
title Zoom
//MVP.css는 우리가 태그에 class나 id같은 특성을 추가하지 않아도 자동으로 스타일을 적용해주는 라이브러리다.
body
//h1 It works!
header
h1 Zoom
main
div#welcome
form
input(placeholder="room name", required, type="text")
button Enter Room
div#room
h3
ul
form
input(placeholder="message", required, type="text")
button Send
script(src="/socket.io/socket.io.js")
script(src="/public/js/app.js")
h3요소를 추가하여 채팅룸 이름을 표시할 예정.
4-2) app.js코드 수정하기
const socket = io();
const welcome = document.getElementById("welcome");
const form = welcome.querySelector("form");
const room = document.getElementById("room");
room.hidden = true;
let roomName;
function showRoom(){
welcome.hidden = true;
room.hidden = false;
const h3 = room.querySelector("h3");
h3.innerText =`Room ${roomName}`;
}
function handleRoomSubmit(event){
event.preventDefault();
const input = form.querySelector("input");
socket.emit("enter_room", input.value, showRoom);
roomName = input.value;
input.value = "";
}
form.addEventListener("submit", handleRoomSubmit);
roomName 변수를 만들고 룸 이름을 입력하면 값이 대입되어 h3에 표시하도록 했음
<결과화면>
룸 이름을 입력하면
5. 채팅룸 안에서 메시지 교환하기
5-1) server.js 수정하기
socket.io가 제공하는 to 메서드를 사용할 예정
to 메서드는 이벤트를 통해 데이터를 전달하고 싶은 대상을 지정할 수 있게 해줌.
server.js 코드를 다음과 같이 수정
import http from 'http';
import SocketIO from "socket.io";
import express from 'express';
const app = express();
app.set("view engine", "pug");
//dirname은 Node.js기본 전역변수로, 현재 실행되는 폴더의 경로를 의미
app.set("views", __dirname + "/views");
app.use("/public", express.static(__dirname + "/public"));
// '/'를 받으면 home으로 가게 설정
app.get("/", (req, res) => res.render("home"));
//만약홈이 아닌 다른 주소로 get요청을 보내더라도 홈으로 리다이렉션하게 예외처리함
app.get("/*", (req, res) => res.redirect("/"));
const httpServer = http.createServer(app);
const wsServer = SocketIO(httpServer);
wsServer.on("connection", (socket) => {
socket.on("enter_room", (roomName,done) => {
//브라우저가 요청한 콜백을 의미 - showRoom()을 의미.
done();
//소켓 룸 생성 및 접속
socket.join(roomName);
//to메서드
socket.to(roomName).emit("welcome");
});
});
const handleListen = () => console.log("Listening on http://localhost:3000");
httpServer.listen(3000,handleListen);
여기서 핵심은
socket.to(roomName).emit("welcome");
to 메서드에 해당하는 다음의 코드이다.
설명 : 해당 채팅룸(roomName으로 구별)에 참가한 소켓들에 대해서만 이벤트가 발생.
이벤트명은 welcome으로 지정
5-2) app.js 수정하기
프론트에서 welcome 이벤트에 반응하도록 만들어야함.
const socket = io();
const welcome = document.getElementById("welcome");
const form = welcome.querySelector("form");
const room = document.getElementById("room");
room.hidden = true;
let roomName;
function addMessage(message){
//메시지 받아서 저장
const ul = room.querySelector("ul");
//li 생성
const li = document.createElement("li");
//li에 저장해둔 메시지 담음
li.innerText = message;
//ul에 가져다 붙힘
ul.appendChild(li);
}
function showRoom(){
welcome.hidden = true;
room.hidden = false;
const h3 = room.querySelector("h3");
h3.innerText =`Room ${roomName}`;
}
function handleRoomSubmit(event){
event.preventDefault();
const input = form.querySelector("input");
socket.emit("enter_room", input.value, showRoom);
roomName = input.value;
input.value = "";
}
form.addEventListener("submit", handleRoomSubmit);
//서버로부터 welcome받으면 메시지 날리도록 체킹
socket.on("welcome", () => {
addMessage("someone joined!");
})
<최종 테스트>
브라우저 1 / 브라우저 2 를 킨다. 1,2로 부른다고 가정
1에서 채팅룸 이름 bobo 입력
2에서 바로 채팅룸 이름 bobo입력 시
1에서는 누군가가 접속했다는 메시지를 받을 것임.
→ bobo라는 채팅방은 이미 만들어져 있으니 2는 새로운 채팅방 개설 없이 bobo라는 소켓룸으로 join될 것이고,
기존접속자들은 'someone joined!'라는 메시지를 받을 것임
6. disconnecting 이벤트 추가하기
채팅 나갈때도 알람이 갈 수 있도록 disconnecting 이벤트를 사용하여 코드를 수정해 보자.
6-1) server.js 코드 수정
socket.on("disconnecting", () =>{
socket.rooms.forEach(room => socket.to(room).emit("bye"));
});
disconnect이벤트가 발생하면 소켓이 bye 이벤트르 발생시킴
※ 접속중이던 채팅룸에만 이벤트를 발생시키기 위해서 rooms 속성을 사용한 것을 이해해야함.
rooms 속성은 접속 중인 채팅룸 목록을 뜻하고 forEach로 개별 room에 접속해 콜백함수를 호출할 수 있다.
6-2) app.js 수정하기
bye이벤트가 발생하면 bye이벤트를 발생시킨 소켓은 채팅룸을 떠나버리니까, 이 이벤트를 처리하는 것은 남아있는 다른 사용자들의 소켓의 몫이 될 것임.
socket.on("bye", () => {
addMessage("someone left!");
})
<결과 확인>
7. 메시지 보내기
7-1) app.js 에서 이벤트 핸들러 함수를 추가한다.
function handleMessageSubmit(event){
event.preventDefault();
const input = room.querySelector("input");
const value = input.value;
socket.emit("new_message", value, roomName, () => {
addMessage(`You: ${value}`);
});
input.value = "";
}
function showRoom(){
welcome.hidden = true;
room.hidden = false;
const h3 = room.querySelector("h3");
h3.innerText =`Room ${roomName}`;
const form = room.querySelector("form");
form.addEventListener("submit", handleMessageSubmit);
}
설명은 뒤의 코드와 함께 설명
7-2) server.js 코드 추가하기
socket.on("new_message", (msg, room, done) =>{
socket.to(room).emit("new_message", msg);
done();
})
설명 : app.js에서 showRoom() 메서드가 발생하고 메시지를 입력해 submit이벤트가 발생하면
handleMessageSubmit 호출을 함. 그러면 소켓에서 new_message 이벤트를 발생시키고 server.js가 받아서 콜백함수 실행
server.js 추가된 socket코드를 보면 각각 msg,room,done 이라는 이름으로 받았는데, msg와 room은 보낸 인자(파라미터)를 의미하는 것이고 done은 콜백함수를 의미하는 것임을 알고 있어야 한다. ( emit 메서드의 이해 필요 )
7-3) 메시지 받기
현재 채팅룸에 접속한 사용자가 메시지를 보내면, 서버가 그걸 받아서 채팅룸에 있는 모든 사용자에게 보내도록 해뒀지만, 정작 화면에는 출력이 안되는 상황임.
app.js의 코드에 다음 코드를 추가하자
socket.on("new_message", (msg) => {
addMessage(msg);
})
미리 만들어둔 addMessage함수를 사용하여 생산성을 높힘
<결과 화면>