Illie
Nomad. Zoom Clone Coding (1) - 셋팅 / 웹소켓 본문
1. 서버 셋팅
1) npm init yarn으로 package.json 파일 생성 (버전 관리 편리)
- "script" : 패키지의 생명주기에서 다양한 타이밍에 자주 사용할 commend를 alias(별칭)을 통해 지정해 둘 수 있는 dictionary
- "dependencies" : 필드에 자동으로 install한 '패키지 이름과 '버전'
- "devDependencies" : 개발시에만 필요한 의존 패키지
2) npm i nodemon -D
- nodemon : 서버 코드를 변경할 때마다 자동으로 시작해 줌
3) npm i @babel/core @babel/cli @babel/node -D
@babel/core : 핵심적인 동작이 담겨있는 바벨 코어
@babel/cli : 커멘드라인 명령어를 지원하기 위한 바벨 CLI
@babel/node : 개발 및 테스트 단계에서만 사용하는 것을 권장
4) server.js
import express from "express"; //서버 만들어 줌
const app = express(); //app이라는 변수 가져와서 만들어줌
console.log("hello");
app.listen(3000); // 3000번 포트와 연결
5) nodemon.json
{
"exec": "babel-node src/server.js"
}
2. 프론트엔드 셋팅
server.js
app.set("view engine", "pug");
app.set("views", __dirname + "/src/views");
app.use("/public", express.static(__dirname + "/public"));
app.get("/", (req, res) => res.render("home"));
app.get("/*", (req, res) => res.redirect("/"));
- app.set("view engine", "pug"): 뷰 엔진을 pug로 하겠다
- app.set("views", __dirname + "/src/views"): 디렉토리 설정(dirname: 절대경로 <-> filename: 상대경로)
- app.use("/public", express.static(__dirname + "/public"): public 폴더를 유저에게 공개
- app.get("/", (req, res) => res.render("home")): 홈페이지로 이동할 때 사용될 템플릿을 렌더링
- app.get("/*", (req, res) => res.redirect("/)): 도메인 뒤에 뭐를 치던간에 다시 /로 돌아 오게 함
3. 웹소켓
1-1) 웹소켓 연결하기 - backend
// server.js
const app = express();
const server = http.createServer(app);
const wss = new WebsocketServer({ server });
// http, ws 모두 같은 포트 번호로 돌린다!
wss.on("connection", () => {console.log(socket)}); 연결되면 소켓으로 정보가 올 예정
1-2) 웹소켓 연결하기 - frontend
// app.js
const socket = new Websocket(`ws://${window.location.host}`)
2-1) 메시지 주고받기 ( send : 나(크롬), receive: 나(크롬))
// server.js
wss.on("connection", (socket) => {
console.log("Connected to Browswer ✔");
socket.on("close", () => console.log("Disconnected from the Browser ❌"));
socket.on("message", (message) => {
console.log(message.toString("utf8"));
});
socket.send("hello?");
});
// 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 to Server ❌");
});
setTimeout(() => {
socket.send("hello from the browser!");
}, 10000);
input에 넣은 value를 보냄, 그리고 input.value 초기화
// app.js
const messageList = document.querySelector("ul");
const messageForm = document.querySelector("form");
function handelSubmit(event) {
event.preventDefault();
const input = messageForm.querySelector("input");
socket.send(input.value);
input.value = "";
}
messageForm.addEventListener("submit", handelSubmit);
2-2) 메시지 주고받기 ( send : 나(크롬 브로우저), receive: 나(사파리 브라우저))
// app.js
const sockets = [];
wss.on("connection", (socket) => {
sockets.push(socket);
socket.on("message", (message, isBinary) => {
const messageString = isBinary ? message : message.toString("utf8");
sockets.forEach((aSocket) => aSocket.send(messageString));
});
});
2-3) 닉네임 추가(백엔드는 다양한 언어를 사용하기 때문에, 클라이언트에서 string으로 보내야 함)
// app.js
const nickForm = document.querySelector("#nick");
const messageForm = document.querySelector("#message");
function makeMessage(type, payload) {
const msg = { type, payload };
return JSON.stringify(msg);
}
function handelSubmit(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", handelSubmit);
nickForm.addEventListener("submit", handleNickSubmit);
// server.js
const sockets = [];
wss.on("connection", (socket) => {
sockets.push(socket);
socket["nickname"] = "Anon"; // 익명으로 우선 설정
socket.on("message", (message1, isBinary) => {
const messageString = isBinary ? message1 : message1.toString("utf8");
const message = JSON.parse(messageString);
// const message -> string으로 변경
switch (message.type) {
case "new_message":
sockets.forEach((aSocket) =>
aSocket.send(`${socket.nickname}: ${message.payload}`)
);
break;
case "nickname":
socket["nickname"] = message.payload;
break;
}
});
});