https://zoom-clone--susze.run.goorm.io/
- JS
document.querySelector/getElementByIDcallback functiondocument.createElement.innerText.classList.add()
- NodeJS
package.jsonbabelnodemonExpressJSapp.get()Pug(req,res)=>
- Console
npm init -ypackage.jsonscripts에dev:nodemon
touch README.mdnpm i nodemon -Dtouch babel.config.jsonpresets: [@babel/preset-env]
touch nodemon.jsonexec:[메인js파일]
mkdir srctouch src/server.jsgit init .npm i @babel/core @babel/cli @babel/node @babel/preset-env -Dtouch .gitignore/node_modules
npm i expressnpm i pug
- public 폴더 만들기
mkdir public/jstouch public/js/app.jsapp.use("/public", express.static(__dirname + "/public"));
- pug 사용환경 만들기
app.set("view engine", "pug");app.set("views", __dirname + "/views");
- Root 웹페이지 Render하기
app.get("/", (req, res) => res.render("home");touch src/views/home.pug
home.pug만들기link(rel="stylesheet', href="https://unpkg.com/mvp.css")script(src="/public/js/app.js")
- 프로토콜(protocol)
- 두 컴퓨터간 데이터를 소통하는 방식
- 사용자-서버, 서버-서버 모두 가능하다
- HTTP
- 사용자가 request하면 서버가 response하는 방식
- stateless: backend가 사용자를 기억하지 못함
- 사용자의 request 없이는 서버가 임의로 데이터를 보낼 수 없음.
- WebSocket
- 사용자의 request를 서버가 accept하면 connection이 성립됨
- connection이 성립하면 실시간(real-time) 양방향(bi-directional) 소통이 가능해짐.
- 또한 이를 기반으로 이벤트 발생 시 이를 이용한 기능을 구현가능함.(event-based communication)
- ws 설치하기
npm i ws
- 서버 구축을 위해 http와 ws import하기
import httpimport WebSocket from "ws"
- Express App을 임의로 http서버 구축하기
http.createServer(app);
- HTTP서버 위에 ws서버 구축하기
=
new WebSocket.Server({ server }); - app.listen하기
- 브라우저에서 ws서버 연결하기(
app.js)- new WebSocket(
ws://${window.location.host}
- new WebSocket(
- 브라우저 Event 다루기
- ws서버와의 연결이 시작되었을 때,
socket.addEventListener("open", ~)
- ws서버에서 메세지를 받았을 때,
socket.addEventListener("message", (message) ...)- 메시지 내용만 필요할 때는,
message.data
- ws서버와 연결이 끊어졌을 때,
socket.addEventListener("close", ~)
- ws서버와의 연결이 시작되었을 때,
- 브라우저가 ws서버에 연결되어 있을 때,
[ws서버].on("connection", ~)- 브라우저가 연결되었을 때, 연결된 브라우저에 대한 정보가
socket에 저장된다 - 서버 전체에 대한 event는 서버에, 서버에 연결된 각 브라우저에 대한 event는 socket에 한다.
- 브라우저와의 서버연결이 끊겼을 때
socket.on("close", ~)
- 브라우저에 메세지 보내기
socket.send("[메세지]")
- 브라우저에서 보낸 메세지에 대해 처리하기
socket.on("message", (message) ~)
- sockets 배열에 socket 추가하기
const sockets = [];sockets.push(socket)
- 각 socket마다 message 보내기
sockets.forEach(aSocket => aSocket.send(message.toString()));
- 채팅방 기능 간략하게 구현하기
- HTML
ul로 채팅내용을li로 받기form/input/button
- JavaScript
document.querySelector로 form 가져오기.addEventListenersubmit이벤트시function(event)실행하기- submit default 행동 초기화하기(
event.preventDefault();) input값을 받아socket.send하기
- HTML
- socket이 message의 종류를 구분할 수 있도록 json형식으로 message를 보낸다
- 다만 ws는 websockets의 javascript implementation이므로 json object로 보내기보다 string으로 바꾸어 보내는 것이 바람직하다
- json message 만들기(
app.js)function makeMessage(type, payload) {~}const msg = { type, payload };return JSON.stringify(msg);socket.send(makeMessage("[message_종류]", input.value));
- stringify된 message를 받았을 때,(
server.js)socket.on("message",(aMessage) => ~)message = JSON.parse(aMessage)switch문으로 message의 type별로 처리하기switch(message.type) {~}case [TYPE명]:- case 마지막에
break하기
new_message의 경우, nickname과 함께 메시지 보내기닉네임:socket.nickname.toString()메시지내용:message.payload.toString()
nickname은 socket에 넣고 사용하기- 미리
socket["nickname"] = "Anonymous";로 정하기 socket["nickname"] = message.payload;
- 미리
- SocketIO: websocket을 비롯해 양방향 통신을 지원하는 프레임워크
- SocketIO의 장점
- webSockets가 가능하지 않을 때 자동적으로 대안을 실행함.
http long-polling- 재연결 자동 시도
npm i socket.io- server.js에
socket.ioimport하기 - http서버에
socket.io덮씌우기 ioServer.on("connection", (socket) => ~)- template에
script(src="/socket.io/socket.io.js")해서 브라우저에socket.io가져오기 const socket = io();
ioServer가connection이벤트를 받았을 때,socketargument를 받을 수 있다.server.jssocket.on은 특정 이벤트가 발생할 때 함수를 실행할 수 있다.socket.on("[이벤트명]", ([socket.emit으로_받은_데이터]) => ~;)
webSockets과 다른 점은message이벤트가 아닌 임의로 이벤트 이름을 줄 수 있다는 것이다.
app.jssocket.emit는 이벤트를 발생시키는 것으로 다음과 같은 매개변수를 가진다.socket.emit("[이벤트명]", [백엔드에_보낼_데이터]들, ...)
- 문자열(String)에 한정되지 않고 다양한 데이터 타입을 보낼 수 있다.
- 다만, 마지막 매개변수는 프론트엔드에 발생하는 것으로 통상적으로
이벤트처리 마지막으로 실행하는 함수가 들어간다.- 이때 인수를 설정하면 백엔드에서 프론트엔드로 데이터를 보내는것도 가능하다
socket.id: socket의 고유idsocket.rooms: socket이 접속하고 있는 rooms의 목록- socket마다 혼자있는 room이 있으며 그 room 이름은 id와 같다.
socket.rooms.forEach를 사용하면 socket이 접속한 모든 rooms에 이벤트를 발생시킬 수 있다.
socket.onAny((event) => ~ )- socket에서 발생하는 모든 event에 대해 다룰 수 있음.
- room: socket들이 공유하는 공간
- socket이 특정 room에 join하게 하기
socket.join([room명])
socket.to("[Rooms]").emit([이벤트명]);- 해당 Rooms에 접속한 Socket들에게 일괄 이벤트를 발생시킨다.
- [Rooms]는 한 room, rooms, room Array 모두 가능하다.
socket.on("disconnecting", () => ~)- socket의 접속이 끊어질 때 이벤트
io.sockets.emit(~)- 모두에게 event나 message 보내기(=broadcast)
io.socketsJoin("~");- 모든 socket들을 특정 room으로 이동시키기
io.in("[기존_ROOM명]").socketsJoin([이동_ROOM명])- 특정 room에 접속한 socket들을 다른 room으로 이동시키기
- socket들을 직접 관리하는 개체. 서비스 규모가 커짐에 따라 adapter의 수도 늘어난다
- 기본적으로 socketIO는 메모리를 adapter로 사용한다
- 접속하는 socket이 늘어날수록 유지해야하는 connection의 수도 증가하므로 DB를 활용한 adapter로 대체해줘야 한다.
- adapter의 수는 여러개일 수 있으며, 이 경우 DB를 이용해 서버간 통신을 한다
- Adapter을 이용해 할 수 있는 것들
- 어떤 socket이 연결되었는지, 지금 몇 개의 room이 있는지 정보를 재공함
- Template
- 방 접속하기 div에 ul 만들기
server.js- 방에 들어가는 이벤트 발생 시, 모든 socket들에게 room_change 이벤트를 emit한다
- 방을 나가는 이벤트에도 마찬가지로 room_change 이벤트를 발생시킨다
socket.on("disconnect", ~)
- room_change 이벤트를 emit할 때, 채팅방 개수를 return하는 함수를 보낸다
- 채팅방개수 세는 함수 만들기
ioServer.sockets.adapter에서sids와rooms가져오기sids와rooms는 map Object다- socketID에서는 기본적으로 나만의 채팅방을 제공한다. 따라서, sids(socketids)와 rooms를 비교해 공개 채팅방들만 추려낼 수 있다
- 각 rooms key를(forEach) sids의 key와 비교해 다른 것(undefined)들을 array에 넣어 return한다.
app.js- room_change 이벤트 발생할 시
- ul 초기화해주기
- 공개 채팅방을 각각(forEach) ul에 li로 추가해주기
- Map Object:
key-value 쌍데이터를 담은 그룹- Map 개체 선언하기:
new Map(); - Map item 추가하기:
[MAP].set(); - item의 key로 value 불러오기:
[MAP].get([KEY])
- Map 개체 선언하기:
server.js- "room_enter"와 "disconnecting" 이벤트가 발생하면,
- 해당 room에 채팅방 참여자 수를 emit한다
- 다만, "disconnecting"의 경우 1을 뺀다
- 채팅방 참여자 수 세는 함수 구하기
- rooms는 Set을 return한다
ioServer.sockets.adapter.rooms.get(roomName)?.size;
app.jswelcome과bye이벤트 발생하면 값을 return한다
- Set Object: 중복 값을 제거한 집합(Array)
- Set 개체 선언하기:
new Set(); - Set item 추가하기:
[SET].add([ITEM]); - Set이 특정 item을 가지는지 확인하기:
[SET].has([ITEM]); - Set item 지우기:
[SET].delete([ITEM]); - Set의 item 개수 구하기:
[SET].size
- Set 개체 선언하기:
- Optional Chaining(ES2020)
- 특정 개체의 존재여부를 간단히 표현하는 법
- 문서_살펴보기
npm i @socket.io/admin-ui- Configuration
https://admin.socket.io/https://zoom-clone--susze.run.goorm.io/admin