웹소켓을 사용해서 실시간 채팅 만들기(Node&React)

김지원·2021년 9월 28일
0

React

목록 보기
30/31

WebSocket을 파헤치기 전에 먼저 HTTP 프로토콜에 대해 알아보자!

HTTP 프로토콜

HTTP 프로토콜은 단방향 통신 프로토콜입니다.
클라이언트의 요청이 있을 때 서버가 응답하는 방식을 의미합니다.

  • 클라이언트가 요청을 보내는 경우에만 서버가 응답하는 단방향 프로그래밍 방식입니다.
  • 서버로부터 응답이 수신되는 즉시 연결이 바로 종료됩니다.
  • 실시간 연결이 아니고, 응답이 필요한 경우에만 서버와 연결을 맺어 요청을 보내는 상황에 유용합니다.

HTTP polling
HTTP streaming
server-sent events

WebSocket

소켓 통신이란 서버와 클라이언트 양방향 연결이 이루어지는 통신으로, 클라이언트와 서버 양쪽에서 서로에게 데이터 전달을 하는 방식의 양방향 통신이 가능합니다.

  • 서버와 클라이언트가 계속 연결을 유지하는 양방향 프로그래밍 방식입니다.
  • 웹소켓은 온라인 게임이나 주식 트레이딩 시스템같이 데이터 교환이 지속적으로 이뤄져야 하는 서비스에 아주 적합합니다.

socket.io

양방햔 통신을 하기위해 웹소켓 기술을 활용하는 라이브러리입니다.
Guillermo Rauch가 만든 Socket.io는 WebSocket, FlashSocket, AJAX Long Polling, AJAX Multi part Streaming, IFrame, JSONP Polling을 하나의 API로 추상화한 것이다.

WebSocket vs Socket.io

WebSocket

  • HTML5 웹 표준 기술
  • 매우 빠르게 작동하며 통신할 때 아주 적은 데이터를 이용함
  • 이벤트를 단순히 듣고, 보내는 것만 가능함

Socket.io

  • 표준 기술이 아니며, 라이브러리임
  • 소켓 연결 실패 시 fallback을 통해 다른 방식으로 알아서 해당 클라이언트와 연결을 시도함
  • 방 개념을 이용해 일부 클라이언트에게만 데이터를 전송하는 브로드캐스팅이 가능함

WebSocket

server 코드 작성

설치
npm init
npm install --save websocket http

// webSocket 서버 포트를 8000번으로 정의하고 webSocket과 HTTP를 가져온다.
const webSocketsServerPort = 8000;
const webSocketServer = require('websocket').server;
const http = require('http');

// HTTP모듈을 사용하여 HTTP 서버를 만든다.
// 이 작업이 완료되면 포트 8000번을 수신하도록 한다.
const server = http.createServer();
server.listen(webSocketsServerPort);
console.log('listening on port 8000');

// 방금 생성한 HTTP 서버의 인스턴스를 사용하여 WebSocket 서버를 생성한다.
const wsServer = new webSocketServer({
    httpServer: server
});

// 클라이언트 객체를 생성한다
const clients = {};

// This code generates unique userid for everyuser.
const getUniqueID = () => {
    const s4 = () => Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
    return s4() + s4() + '-' + s4();
};

//여기에 연결된 모든 클라리언트를 우리가 정의해야하는 요청 메서드에 저장할 것이다.
//서버가 여기에서 요청을 받으면 발생합니다.
wsServer.on('request', function (request) {
    var userID = getUniqueID();
    console.log((new Date()) + ' Recieved a new connection from origin ' + request.origin + '.');

    // You can rewrite this part of the code to accept only the requests from allowed origin
    const connection = request.accept(null, request.origin);
    clients[userID] = connection;
    console.log('connected: ' + userID + ' in ' + Object.getOwnPropertyNames(clients));

    connection.on('message', function(message) {
        if (message.type === 'utf8') {
            console.log('Received Message: ', message.utf8Data);

            // broadcasting message to all connected clients
            for(let key in clients) {
                clients[key].sendUTF(message.utf8Data);
                console.log('sent Message to: ', clients[key]);
            }
        }
    })
});

client 코드 작성

설치
npm init
npm install --save websocket react react-dom react-script antd

public index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8" />
    <meta name="description" content="chat app created using websockets" />
    <title>Socket chat</title>
</head>

<body>
<div id="root"></div>
</body>

src index.js

import ReactDOM from 'react-dom';
import React, { Component } from 'react';
import { w3cwebsocket as W3CWebSocket } from "websocket";
import { Card, Avatar, Input, Typography } from 'antd';
import 'antd/dist/antd.css';
import './index.css'

const { Search } = Input;
const { Text } = Typography;
const { Meta } = Card;

const client = new W3CWebSocket('ws://127.0.0.1:8000');

export default class App extends Component {

    state ={
        userName: '',
        isLoggedIn: false,
        messages: []
    }

    onButtonClicked = (value) => {
        client.send(JSON.stringify({
            type: "message",
            msg: value,
            user: this.state.userName
        }));
        this.setState({ searchVal: '' })
    }
    componentDidMount() {
        client.onopen = () => {
            console.log('WebSocket Client Connected');
        };
        client.onmessage = (message) => {
            const dataFromServer = JSON.parse(message.data);
            console.log('got reply! ', dataFromServer);
            if (dataFromServer.type === "message") {
                this.setState((state) =>
                    ({
                        messages: [...state.messages,
                            {
                                msg: dataFromServer.msg,
                                user: dataFromServer.user
                            }]
                    })
                );
            }
        };
    }
    render() {
        return (
            <div className="main" id='wrapper'>
                {this.state.isLoggedIn ?
                    <div>
                        <div className="title">
                            <Text id="main-heading" type="secondary" style={{ fontSize: '36px' }}>Websocket Chat: {this.state.userName}</Text>
                        </div>
                        <div style={{ display: 'flex', flexDirection: 'column', paddingBottom: 50 }} id="messages">
                            {this.state.messages.map(message =>
                                <Card key={message.msg} style={{ width: 300, margin: '16px 4px 0 4px', alignSelf: this.state.userName === message.user ? 'flex-end' : 'flex-start' }} loading={false}>
                                    <Meta
                                        avatar={
                                            <Avatar style={{ color: '#f56a00', backgroundColor: '#fde3cf' }}>{message.user[0].toUpperCase()}</Avatar>
                                        }
                                        title={message.user+":"}
                                        description={message.msg}
                                    />
                                </Card>
                            )}
                        </div>
                        <div className="bottom">
                            <Search
                                placeholder="input message and send"
                                enterButton="Send"
                                value={this.state.searchVal}
                                size="large"
                                onChange={(e) => this.setState({ searchVal: e.target.value })}
                                onSearch={value => this.onButtonClicked(value)}
                            />
                        </div>
                    </div>
                    :
                    <div style={{ padding: '200px 40px' }}>
                        <Search
                            placeholder="Enter Username"
                            enterButton="Login"
                            size="large"
                            onSearch={value => this.setState({ isLoggedIn: true, userName: value })}
                        />
                    </div>
                }
            </div>
        )
    }
}

ReactDOM.render(<App />, document.getElementById('root'));

src index.css

body {
    background-color: #bae7ff;
}

.main {
    height: 100vh;
    width: 100vw;
    background-color: #bae7ff;
}

.title {
    text-align: center;
    width: 100%;
    padding: 2%;
    background-color: #91d5ff;
    color: #efefef;
}

.bottom {
    position: fixed;
    width: 100%;
    left: 0;
    bottom: 0;
}

soket.io

https://socket.io/get-started/chat

참고
https://www.peterkimzz.com/websocket-vs-socket-io/

0개의 댓글

관련 채용 정보