본문 바로가기
개발(Development)/JS(자바스크립트)

Node.js: Web Socket 통신 - ws모듈 사용 방법

by 카레유 2021. 7. 2.

# 웹소켓(Web Socket)이란?

: ws프로토콜을 이용하는 양방향 통신방식이다.

: ws프로토콜은 한번 연결되면 "연결을 끊지 않고 계속 유지"한 상태로 "클라이언트와 서버가 서로 데이터를 주고 받는다"

: 따라서 서버 하나에 여러 클라이언트가 붙어서 지속적으로 데이터를 주고 받아야하는 서비스에 유용하다.

 

: HTTP 통신의 경우, 클라이언트가 요청을 하고 서버가 응답을 하면 바로 연결이 끊어진다.

 

예를 들어 채팅서비스의 경우,

계속해서 새로운 메시지가 있는지 확인해야 하는데

웹소켓을 이용하면 매번 서버에 요청할 필요 없이 계속해서 새로운 데이터를 받을 수 있다.

기존에 연결된 통로를 통해 추가 요쳥이 없어도 서버에서 데이터를 보내줄 수 있기 때문이다.

 

* 참고: 웹소켓 지원 전에는 클라이언트가 서버에게 주기적으로 반복해서 요청을 보내고 응답을 받는 폴링(Polling)이라는 방식을 이용했었다.

* 참고: SSE(Server Sent Events)라는 방식으로 연결을 끊지 않고 유지하는 방식도 있으나, 서버만 데이터를 전송할 수 있다는 단점이 있다.


# ws 모듈

1. ws 모듈이란?

: 간단한 웹소켓 통신을 구현하기 위한 모듈이다.

* 참고: socket.io라는 모듈을 사용하면, WebSocket이 지원되지 않는 브라우저에서도 여러가지 방식을 동원해 비슷하게 구현해준다.(+편의기능 제공)

* socket.io에 대한 글 참고: [node.js] socket.io 웹 소켓 모듈 기본 사용 방법

 

2. ws사용 방법

- 기본 구조는 서버측 WebSocket과 클라이언트(브라우저)측 WebSocket이 통신을 하는 구조다.

 

- 서버측 WebSocket은 node.js환경에서 ws모듈을 통해 생성해 준다.

- 클라이언트측 WebSocket은 브라우저 환경에서 지원되는 WebSocket객체를 생성해 준다.

 

- 서버측 WebSocket 생성시, HTTP서버를 연결해주면 PORT를 공유하며 작동을 시작한다.

- 클라이언트측 WebSocket 생성시, 접속할 WebSocket서버 주소를 연결해주면 통신을 시작한다.

 

- 기본적인 연결 상태 체크와 데이터 송수신은 이벤트 기반으로 동작된다.

- 서버/클라이언트 연결시 connection이벤트가 발생하며, 데이터를 주고 받을 때마다 message이벤트가 발생한다.

 

- 작업 순서는 아래와 같다.

1) ws 모듈 설치 ( 콘솔 명령어:  npm install ws  )

2) HTTP서버 생성(Express 모듈 등 활용)

3) WebSocket 서버 생성(HTTP서버 연결)

4) 클라이언트(브라우저)에서 WebSocket 객체 생성(연결)

5) 서버-클라이언트간 데이터 통신 수행(이벤트 기반)

 

* 테스트는 브라우저 개발자도구의 network 탭에서 주고 받는 메시지를 확인한다.


# ws모듈: 웹소켓 구현 샘플  소스코드

1. app.js 파일

: app.js 파일에 HTTP서버와 WebSockt 서버를 구현해준다.

 

1) express 모듈로 HTTP 서버를 생성 및 구동

- HTTP 서버를 생성/구동해준다.

// ********* app.js 파일


// HTTP 서버(express) 생성 및 구동

// 1. express 객체 생성
const express = require('express');
const app = express();

// 2. "/" 경로 라우팅 처리
app.use("/", (req, res)=>{
    res.sendFile('./index.html')); // index.html 파일 응답
})

// 3. 30001 port에서 서버 구동
const HTTPServer = app.listen(30001, ()=>{
    console.log("Server is open at port:30001");
});

 

2) ws모듈로 WebSocket 서버를 생성

- WebSocket 서버 생성 시, HTTP서버를 연결해주어야 한다.

- port는 같이 사용해도 되고 별도로 지정해줘도 된다.

// ********* app.js 파일

// **** HTTP 서버 부분 생략


// WebSocekt 서버(ws) 생성 및 구동

// 1. ws 모듈 취득
const wsModule = require('ws');

// 2. WebSocket 서버 생성/구동
const webSocketServer = new wsModule.Server( 
    {
        server: HTTPServer, // WebSocket서버에 연결할 HTTP서버를 지정한다.
        // port: 30002 // WebSocket연결에 사용할 port를 지정한다(생략시, http서버와 동일한 port 공유 사용)
    }
);

 

3) WebSocket의 이벤트 처리

- 클라이언트가 웹소켓 서버에 연결되면 connection 이벤트가 발생한다.

- 이 때부터 message 수신 및 전송이 가능해진다(아래 소스코드의 주석 참고)

// ********* app.js 파일
// **** HTTP서버 부분 생략
// **** WebSocekt서버 부분 생략

// connection(클라이언트 연결) 이벤트 처리
webSocketServer.on('connection', (ws, request)=>{

    // 1) 연결 클라이언트 IP 취득
    const ip = request.headers['x-forwarded-for'] || request.connection.remoteAddress;
    
    console.log(`새로운 클라이언트[${ip}] 접속`);
    
    // 2) 클라이언트에게 메시지 전송
    if(ws.readyState === ws.OPEN){ // 연결 여부 체크
        ws.send(`클라이언트[${ip}] 접속을 환영합니다 from 서버`); // 데이터 전송
    }
    
    // 3) 클라이언트로부터 메시지 수신 이벤트 처리
    ws.on('message', (msg)=>{
        console.log(`클라이언트[${ip}]에게 수신한 메시지 : ${msg}`);
        ws.send('메시지 잘 받았습니다! from 서버')
    })
    
    // 4) 에러 처러
    ws.on('error', (error)=>{
        console.log(`클라이언트[${ip}] 연결 에러발생 : ${error}`);
    })
    
    // 5) 연결 종료 이벤트 처리
    ws.on('close', ()=>{
        console.log(`클라이언트[${ip}] 웹소켓 연결 종료`);
    })
});

* 서버 ws.readyState는 상태에 따라 아래의 값을 갖는다.

- ws.OPEN : 연결됨

- ws.CLOSED : 연결 끊김

- ws.CLOSING : 연결 끊는 중

- ws.CONNECTING: 연결 중 

 

 

2. index.html

: 브라우저에서 WebSocket클라이언트 객체를 만들고, WebSocket 서버(app.js)에 연결한다.

 

1) HTML 작업

- 버튼을 2개 만들어 주었다.

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>웹소켓</title>
</head>
<body>
    <h1>웹소켓 테스트</h1>

    <!-- 버튼 2개 생성 -->
    <button id="btn_send">메시지 전송</button>
    <button id="btn_close">연결 끊기</button>
</body>

</html>

 

2) 클라이언트 WebSocket 객체 생성

- 브라우저에서 기본적으로 지원해주는 WebSocket 클라이언트 객체를 생성한다.

- 생성시 지정한 주소로 ws프로토콜 통신이 수행된다. port 번호에 주의하자.

<script>
    
    // 1. 웹소켓 클라이언트 객체 생성
    const webSocket = new WebSocket("ws://127.0.0.1:30001");
    
</script>

 

3) 클라이언트 WebSocket 객체 이벤트 처리

- 서버 WebSocket과 연결되면 open 이벤트가 발생한다.

- 이 때부터 서버WebSocket과 데이터를 주고 받을 수 있으며, message이벤트가 발생한다.

- 버튼을 클릭 시, 서버 WebSocket에 데이터를 보내거나, 연결을 종료시키는 코드도 작성했다.

<script>
    // 1. 웹소켓 클라이언트 객체 생성
    const webSocket = new WebSocket("ws://localhost:30001");


    // 2. 웹소켓 이벤트 처리
    // 2-1) 연결 이벤트 처리
    webSocket.onopen = ()=>{
        console.log("웹소켓서버와 연결 성공");
    };

    // 2-2) 메세지 수신 이벤트 처리
    webSocket.onmessage = function (event) {
        console.log(`서버 웹소켓에게 받은 데이터: ${event.data}`);
    }

    // 2-3) 연결 종료 이벤트 처리
    webSocket.onclose = function(){
        console.log("서버 웹소켓 연결 종료");
    }

    // 2-4) 에러 발생 이벤트 처리
    webSocket.onerror = function(event){
        console.log(event)
    }


    // 3. 버튼 클릭 이벤트 처리
    // 3-1) 웹소켓 서버에게 메세지 보내기
    let count = 1;
    document.getElementById("btn_send").onclick = function(){

        if(webSocket.readyState === webSocket.OPEN){ // 연결 상태 확인
            webSocket.send(`증가하는 숫자를 보냅니다 => ${count}`); // 웹소켓 서버에게 메시지 전송
            count++; // 보낼때마다 숫자를 1씩 증가

        }else{
            alert("연결된 웹소켓 서버가 없습니다.");
        }
    }

    // 3-2) 웹소켓 서버와 연결 끊기
    document.getElementById("btn_close").onclick = function(){

        if(webSocket.readyState === webSocket.OPEN){ // 연결 상태 확인
            webSocket.close(); // 연결 종료

        }else{
            alert("연결된 웹소켓 서버가 없습니다.");
        }
    }


</script>

* 클라이언트 WebSocket.readyState는 상태에 따라 아래의 값을 갖는다.

- WebSocket.OPEN : 연결됨

- WebSocket.CLOSED: 연결 끊김

- WebSocket.CLOSING: 연결 끊는 중 

- WebSocket.CONNECTING: 연결 중


# 테스트 결과

- 브라우저에서 http://127.0.0.1:30001/ 이나 http://localhost:30001/ 등으로 접근 후,

- 메시지 전송 버튼과, 연결 끊기 버튼을 누르면 서버측과 브라우저에 아래와 같은 로그가 찍힌다.

 

* 서버측 로그

 

* 클라이언트측(브라우저) 개발자도구 Network탭 및 콘솔 로그


* 샘플 코드 전문

- app.js

// ********* app.js 파일

// 디렉터리 관리를 위해 path 모듈 사용
const path = require("path");


// HTTP 서버(express) 생성 및 구동

// 1. express 객체 생성
const express = require('express');
const app = express();

// 2. "/" 경로 라우팅 처리
app.use("/", (req, res)=>{
    res.sendFile(path.join(__dirname, './index.html')); // index.html 파일 응답
})

// 3. 30001 port에서 서버 구동
const HTTPServer = app.listen(30001, ()=>{
    console.log("Server is open at port:30001");
});


// WebSocekt 서버(ws) 생성 및 구동

// 1. ws 모듈 취득
const wsModule = require('ws');

// 2. WebSocket 서버 생성/구동
const webSocketServer = new wsModule.Server( 
    {
        server: HTTPServer, // WebSocket서버에 연결할 HTTP서버를 지정한다.
        //port: 30002 // WebSocket연결에 사용할 port를 지정한다(생략시, http서버와 동일한 port 공유 사용)
    }
);


// connection(클라이언트 연결) 이벤트 처리
webSocketServer.on('connection', (ws, request)=>{

    // 1) 연결 클라이언트 IP 취득
    const ip = request.headers['x-forwarded-for'] || request.connection.remoteAddress;
    
    console.log(`새로운 클라이언트[${ip}] 접속`);
    
    // 2) 클라이언트에게 메시지 전송
    if(ws.readyState === ws.OPEN){ // 연결 여부 체크
        ws.send(`클라이언트[${ip}] 접속을 환영합니다 from 서버`); // 데이터 전송
    }
    
    // 3) 클라이언트로부터 메시지 수신 이벤트 처리
    ws.on('message', (msg)=>{
        console.log(`클라이언트[${ip}]에게 수신한 메시지 : ${msg}`);
        ws.send('메시지 잘 받았습니다! from 서버')
    })
    
    // 4) 에러 처러
    ws.on('error', (error)=>{
        console.log(`클라이언트[${ip}] 연결 에러발생 : ${error}`);
    })
    
    // 5) 연결 종료 이벤트 처리
    ws.on('close', ()=>{
        console.log(`클라이언트[${ip}] 웹소켓 연결 종료`);
    })
});

 

- index.html

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>웹소켓</title>
</head>
<body>
    <h1>웹소켓 테스트</h1>

    <!-- 버튼 2개 생성 -->
    <button id="btn_send">메시지 전송</button>
    <button id="btn_close">연결 끊기</button>
</body>
<script>
    // 1. 웹소켓 클라이언트 객체 생성
    const webSocket = new WebSocket("ws://localhost:30001");


    // 2. 웹소켓 이벤트 처리
    // 2-1) 연결 이벤트 처리
    webSocket.onopen = ()=>{
        console.log("웹소켓서버와 연결 성공");
    };

    // 2-2) 메세지 수신 이벤트 처리
    webSocket.onmessage = function (event) {
        console.log(`서버 웹소켓에게 받은 데이터: ${event.data}`);
    }

    // 2-3) 연결 종료 이벤트 처리
    webSocket.onclose = function(){
        console.log("서버 웹소켓 연결 종료");
    }

    // 2-4) 에러 발생 이벤트 처리
    webSocket.onerror = function(event){
        console.log(event)
    }


    // 3. 버튼 클릭 이벤트 처리
    // 3-1) 웹소켓 서버에게 메세지 보내기
    let count = 1;
    document.getElementById("btn_send").onclick = function(){

        if(webSocket.readyState === webSocket.OPEN){ // 연결 상태 확인
            webSocket.send(`증가하는 숫자를 보냅니다 => ${count}`); // 웹소켓 서버에게 메시지 전송
            count++; // 보낼때마다 숫자를 1씩 증가

        }else{
            alert("연결된 웹소켓 서버가 없습니다.");
        }
    }

    // 3-2) 웹소켓 서버와 연결 끊기
    document.getElementById("btn_close").onclick = function(){

        if(webSocket.readyState === webSocket.OPEN){ // 연결 상태 확인
            webSocket.close(); // 연결 종료

        }else{
            alert("연결된 웹소켓 서버가 없습니다.");
        }
    }


</script>
</html>

 

 

댓글