웹 소켓으로 실시간 데이터 전송하기

웹 소켓으로 실시간 데이터 전송하기

웹 소켓 이해하기

웹 소켓 : 실시간 양방향 데이터 전송을 위한 기술

ws 프로토콜 사용 -> 브라우저가 지원해야 함

최신 브라우저는 대부분 웹 소켓을 지원함

노드는 ws나 Socket.IO같은 패키지를 통해 웹 소켓 사용 가능

웹 소켓 이전에는 폴링이라는 방식을 사용했음

HTTP가 클라이언트에서 서버로만 요청이 가기 때문에 주기적으로 서버에 요청을 보내 업데이트가 있는지 확인함

웹 소켓은 연결도 한 번만 맺으면 되고, HTTP와 포트 공유도 가능하며, 성능도 매우 좋음

웹 소켓 이미지

서버센트 이벤트

SSE(Server Sent Events)

EventSource라는 객체를 사용

처음에 한 번만 연결하면 서버가 클라이언트에 지속적으로 데이터를 보내줌

클라이언트에서 서버로는 데이터를 보낼 수 없음

폴링 vs SSE vs 웹 소켓

ws 모듈로 웹 소켓 사용해보기

gif-chat 프로젝트 생성

gif-chat 폴더 생성 후 package.json 작성

package.json

기본 파일 작성

패키지를 설치하고 .env 와 app.js, routes/index.js 파일 작성

.env

// app.js const express = require('express'); const path = require('path'); const morgan = require('morgan'); const cookieParser = require('cookie-parser'); const session = require('express-session'); const nunjucks = require('nunjucks'); const dotenv = require('dotenv'); dotenv.config(); const webSocket = require('./socket'); const indexRouter = require('./routes'); const app = express(); app.set('port', process.env.PORT || 8005); app.set('view engine', 'html'); nunjucks.configure('views', { express: app, watch: true, }); app.use(morgan('dev')); app.use(express.static(path.join(__dirname, 'public'))); app.use(express.json()); app.use(express.urlencoded({ extended: false })); app.use(cookieParser(process.env.COOKIE_SECRET)); app.use(session({ resave: false, saveUninitialized: false, secret: process.env.COOKIE_SECRET, cookie: { httpOnly: true, secure: false, }, })); app.use('/', indexRouter); app.use((req, res, next) => { const error = new Error(`${req.method} ${req.url} 라우터가 없습니다.`); error.status = 404; next(error); }); app.use((err, req, res, next) => { res.locals.message = err.message; res.locals.error = process.env.NODE_ENV !== 'production' ? err : {}; res.status(err.status || 500); res.render('error'); }); const server = app.listen(app.get('port'), () => { console.log(app.get('port'), '번 포트에서 대기중'); }); webSocket(server);

// routes/index.js const express = require('express'); const router = express.Router(); router.get('/',(req,res) => { res.render('index'); }); module.exports = router;

ws 모듈 설치하기

npm i ws 로 설치

웹 소켓을 익스프레스에 연결하기

socket.js는 나중에 작성

// 위 app.js참고 const webSocket = require('./socket'); . . . webSocket(server);

socket.js 파일

// socket.js const WebSocket = require('ws'); module.exports = (server) => { const wss = new WebSocket.Server({ server }); wss.on('connection', (ws, req) => { // 웹소켓 연결 시 const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress; console.log('새로운 클라이언트 접속', ip); ws.on('message', (message) => { // 클라이언트로부터 메시지 console.log(message.toString()); }); ws.on('error', (error) => { // 에러 시 console.error(error); }); ws.on('close', () => { // 연결 종료 시 console.log('클라이언트 접속 해제', ip); clearInterval(ws.interval); }); ws.interval = setInterval(() => { // 3초마다 클라이언트로 메시지 전송 if (ws.readyState === ws.OPEN) { ws.send('서버에서 클라이언트로 메시지를 보냅니다.'); } }, 3000); }); };

ws 모듈을 불러옴

new Websocket.Server({ server }) 로 익스프레스 서버와 연결

connection 이벤트는 서버와 연결될 때 실행되는 이벤트

req.headers[‘x-forwarded-for’] || req.connection.remoteAddress는 클라이언트의 IP를 알아내는 유명한 방법

message, error, close 이벤트는 각각 메시지가 올 때, 에러 발생할 때, 서버 연결 종료할 때 호출

ws.OPEN은 연결 상태가 열려있다는 뜻(연결되었다는 뜻)

ws.send로 메시지 전송(3초마다 보내고 있음)

프런트엔드에서 메시지 답장하기

index. html 를 작성하고 스크립트 작성

// views/index.html GIF 채팅방 F12를 눌러 console 탭과 network 탭을 확인하세요. const webSocket = new WebSocket("ws://localhost:8005"); webSocket.onopen = function () { console.log('서버와 웹소켓 연결 성공!'); }; webSocket.onmessage = function (event) { console.log(event.data); webSocket.send('클라이언트에서 서버로 답장을 보냅니다'); };

new WebSocket은 최신 브라우저에서 지원

인수로 서버의 주소를 입력

onopen 이벤트리스너는 서버와 연결되었을 때 호출

onmessage 이벤트리스너는 서버에서 메시지가 올 때 호출

event.data에 서버 메시지 내용이 들어 있음

webSocket.send로 서버로 메시지 전달 가능

웹 소켓 구조도

서버 실행하기

F12 console 탭 실행

접속하는 순간부터 노드의 콘솔과 브라우저의 콘솔에 3초마다 메시지 찍힘

노드 콘솔 개발자 도구의 Network 탭의 Messages 탭

다른 브라우저로도 연결하기

다른 브라우저에서 http://localhost:8005 에 접속

접속한 브라우저(클라이언트)가 두 개라, 서버가 받는 메시지의 양도 두 배가 됨

클라이언트 하나 종료하기

브라우저를 하나 종료하기

콘솔에 접속 해제 메시지가 뜨고, 메시지의 양이 하나가 됨

편의성을 위해 ws 모듈 대신 Socket.IO 모듈 사용

from http://jhg3410.tistory.com/33 by ccl(A) rewrite - 2021-11-24 01:27:06