헬창 개발자

실시간 소켓 통신 구현을 해보자 본문

공부방

실시간 소켓 통신 구현을 해보자

찬배 2022. 10. 5. 11:24

HTTP와 AJAX

HTTP는 URL과 Header같은 부가 정보를 포함하여 사용자가 원하는 데이터를 정확히 주고받을 수 있도록 해준다. HTTP 통신은 클라이언트 요청 한번에 응답 한 번을 보내고 통신을 끝내게 된다. 따라서 페이지의 일부분만 갱신하고 싶어도 응답을 다시 보내야 한다. 따라서 페이지의 일부분만 갱신하고 싶어도 응답을 다시 보내야 한다.

그래서 이러한 제약으로부터 조금 더 진화한 AJAX라는 것이 등장했다. AJAX는 ‘Asynchonous JavaScript XML’의 약자로 XMLHttpRequest라는 자바스크립트 객체를 이용해 서버와 비동기 방식으로 통신하여 DOM을 조작해 문서의 일부분만 갱신하는 것을 가능하게 된다. 따라서 HTTP 대신 AJAX를 사용하는 경우는 이메일 중복 체크나 비밀번호 확인 등 페이지 이동이 없는 경우와 빠른 렌더링을 보장하기 위한 경우 사용된다.

하지만 AJAX도 HTTP 통신이기 때문에 여전히 클라이언트가 요청을 보내지 않으면 통신을 시작할 수 없다는 한계가 있다. 그래서 이를 근본적으로 해결하기 위해 새로운 통신 규약이 생겨나게 된다.

 

웹 소켓은 클라이언트와 서버 간의 양방향 통신이 가능하도록 지원하는 프로토콜이다. 클라이언트가 요청을 먼저 보내지 않아도 서버 측에서 데이터를 보낼 수 있다. 특히 실시간 채팅이나 게임, 스트리밍 서비스등을 궁극적으로 발전시킨 원동력이 되었다. 클라이언트가 세 번의 요청을 보낸다고 하면 HTTP 방식은 세 번의 통신 연결이 필요하지만 웹 소켓을 사용하면 딱 한 번만 연결을 맺고 양방향으로 통신이 가능하다. 이렇게 양방향 통신에 있어 성능이 훨씬 뛰어난 웹소켓 프로트콜은 구현 방법도 생각보다 쉽다.

단순한 API로 구성되어 있어서 설계나 구현도 간결하고, 기존에 사용하던 http 통신으로 구현한 express 서버와 포트도 공유가 가능하다. 또 XMLHttpReauest 객체와 Server-Sent Event를 조합해서 사용하는 것도 가능하다.

WS 모듈로 웹 소켓 구현하기

npm install ws

위 명령어로 ws 모듈을 설치합니다.

  • ws 모듈을 이용한 WebSoket 구현
const WebSocket = require('ws'); // npm install -g ws@7.4.3

module.exports = (server) => {
    const wss = new WebSocket.Server({ server });

    wss.on('connection', (ws, req) => { // Connection Generate
        const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
        console.log('New Client : ', ip);
        ws.on('message', (message) => { // 클라이언트로부터 메세지
            console.log(message);
        });
        ws.on('error', (err) => { // 에러처리
            console.error(err);
        });
        ws.on('close', () => { // 종료
            console.log('클라이언트 접속 해제', ip);
            clearInterval(ws.interval);
        });

        ws.interval = setInterval(() => { // 서버에서 메세지
            if (ws.readyState === ws.OPEN) {
                ws.send('Message From Server.');
            }
        }, 3000);
    });
};
  • 클라이언트 코드 작성
<!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>WebSocket</title>
  </head>
  <body>
    <div>ws 모듈로 웹 소켓을 알아봅시다.</div>
    <script>
      const webSocket = new WebSocket("ws://localhost:8080"); // ws protocol
      webSocket.onopen = function () {
        console.log("Web Socket Connected");
      };
      webSocket.onmessage = function (event) {
        console.log(event.data);
        webSocket.send("This Message From Client");
      };
    </script>
  </body>
</html>
  • WebSocket 서버 코드
const morgan = require('morgan');
const cookieParser = require('cookie-parser');
const session = require('express-session');
const express = require('express');
const app = express();

const webSocket = require('./socket.js');

/* 포트 설정 */
app.set('port', process.env.PORT || 8080);

/* 공통 미들웨어 */
app.use(morgan('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cookieParser('wsExample'));
app.use(session({
    resave: false,
    saveUninitialized: false,
    secret: 'wsExample',
    cookie: {
        httpOnly: true,
        secure: false
    }
}));

/* 라우터 설정 */
app.get('/', (req, res) => {
    res.sendFile(__dirname + '/index.html');
});

/* 404 에러처리 */
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.send('error Occurred');
})

/* 서버와 포트 연결.. */
const server = app.listen(app.get('port'), () => {
    console.log(app.get('port'), '번 포트에서 서버 실행 중 ..')
});

webSocket(server); // ws와 http 포트 공유

socket.io로 실시간 채팅 구현하기

websoket을 사용할 수 있는 방법에는 ws 모듈 말고 soket.io라는 것이 있다. soket.io는 ws 모듈의 메시지를 주고받는 기능을 확장한 패키지이다. 사용자를 그룹화해서 메시지를 보낼수도 있고 특정 사용자에게만 메시지를 보낸는 기능을 쉽게 만들 수도 있어 주로 채팅 기능을 구현할때 많이 사용한다.

$ npm install soket.io

위 명령어로 soket.io 모듈을 설치한다.

const morgan = require('morgan');
const cookieParser = require('cookie-parser');
const session = require('express-session');
const express = require('express');
const app = express();

const webSocket = require('./socket.js');

/* 포트 설정 */
app.set('port', process.env.PORT || 8080);

/* 공통 미들웨어 */
app.use(morgan('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cookieParser('wsExample'));
app.use(session({
    resave: false,
    saveUninitialized: false,
    secret: 'wsExample',
    cookie: {
        httpOnly: true,
        secure: false
    }
}));

/* 라우터 설정 */
app.get('/', (req, res) => {
    res.sendFile(__dirname + '/index.html');
});

/* 404 에러처리 */
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.send('error Occurred');
})

/* 서버와 포트 연결.. */
const server = app.listen(app.get('port'), () => {
    console.log(app.get('port'), '번 포트에서 서버 실행 중 ..')
});

webSocket(server); // ws와 http 포트 공유
  • SoketIO 인스턴스 생성
const SocketIO = require("socket.io");

module.exports = (server) => {
    const io = SocketIO(server, { path: "/socket.io" }); // index.js의 path와 동일하게

    io.on("connection", (socket) => {
        const req = socket.request;
        const ip = req.headers["x-forwarded-for"] || req.connection.remoteAddress;
        console.log(
            `New Client : ${ip}, socket.id : ${socket.id}`
        );

        socket.on("disconnect", () => {
            console.log(`Client Out : ${ip}, socket.id : ${socket.id}`);
            clearInterval(socket.interval);
        });

        socket.on("error", (error) => { });

        socket.on("from client", (data) => { // 클라이언트가 넘긴 데이터
            console.log(data);
        });

        socket.interval = setInterval(() => { // send 대신 emit으로 메세지를 보냄
            socket.emit("from server", "Message From Server");
        }, 3000);
    });
};
  • SoketIO 클라이언트 코드
<!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>Socket.io</title>
  </head>
  <body>
    <div>socket.io 모듈로 웹 소켓을 알아봅시다.</div>
    <script src="/socket.io/socket.io.js"></script>
    <!-- io 제공 script -->
    <script>
      const socket = io.connect("<http://localhost:8080>", {
        path: "/socket.io",
        transports: ["websocket"], // polling 생략
      });
      socket.on("from server", function (data) {
        // 이벤트 리스너, 여러개 할당 가능
        console.log(data);
        socket.emit("from client", "Message From Client"); // 이벤트 이름, 데이터
      });
    </script>
  </body>
</html>

실시간 채팅 구현하기

  • 실시간 채팅창 구현
const http = require("http");
const express = require("express");
const app = express();

app.use(express.static(__dirname)); 

const server = http.Server(app);
const io = require("socket.io")(server);
let users = [];

server.listen(8080, () => {
    console.log("8080포트에서 서버 실행 중...");
});

app.get("/", (req, res) => {
    res.sendFile(__dirname + "/index.html");
});

io.on("connection", (socket) => {
    let name = "";
    socket.on("has connected", (username) => { // 이벤트 : has connected
        name = username;
        users.push(username);
        io.emit("has connected", { username: username, usersList: users });
    });

    socket.on("disconnect", () => { // 이벤트 : has disconnected
        users.splice(users.indexOf(name), 1);
        io.emit("has disconnected", { username: name, usersList: users });
    })

    socket.on("new message", (data) => { // 이벤트 : new message
        io.emit("new message", data); // 모든 소켓에 메세지를 보냄
    });
});

index.css

index.html

 

 

'공부방' 카테고리의 다른 글

도커 맛만 보기  (0) 2022.10.29
passport 구현  (0) 2022.10.25
Node.js 환경에서 NoSQL : MongoDB 사용하기  (0) 2022.09.22
Node.js와 데이터베이스  (0) 2022.09.04
Node.js를 이용한 웹파싱  (0) 2022.09.01
Comments