Go web 8. EventSource를 이용한 채팅

EventSource란?

현재의 계속 바뀌는 웹 형태가 요구 됨에 따라 추가된 기술로 WebSocket과 EventSource가 있다.

WebSoket은 양쪽에 소켓을 연결하여 send/receive가 가능한 형태이다. 페이지가 열린 상태에서 서버와 연결을 끊지 않고 데이터를 계속 주고 받을 수 있는 기술

EventSource는 서버가 일방적으로 데이터를 보내기만 하는 일방통행 형태의 기술이다.

EventSource를 이용한 채팅

ex) index.html

<html>
    <head>
        <title>EventSource Chatting program</title>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
        <script src="chat.js"></script>
    </head>
    <body>
        <div id="chat-log"></div>
        <dev id="user-name"></dev>
        <from id="input-form">
            <input type="text" id="cat-msg" size="64" autofocus />
            <input type="submit" value= "Send"/>
        </from>
    </body>
</html>

ex) chat.js

$(function(){
    if (!window.EventSource) {
        alert("No EventSource!")
        return
    } //현재 위도우가 이벤트 소스를 지원하는지
    var $chatlog = $('#chat-log')
    // 채팅로그를 출력할 부분 해당 엘리멘트를 찾아옴
    var $chatmsg = $("#chat-msg")
    // 채팅 메세지 부분

    // 입력 받은 유저네임 확인
    var isBlank = function(string){
        return string == null || string.trim() === "";
        // 스트링이 널 이거나 스트링의 여백을 지운결과가 빈문자열 이면 
        // === 세개일때 값과 타입까지 비교
    }; 
    // 유저 내임 입력 받기
    var username;
    while (isBlank(username)){
        username = prompt("What's your name?");
        if(!isBlank(username)) {
            $('#user-name').html('<b>' + username + '</b>') ;
        }
    }
    // form 에서 submit했을때 메세지 보내기
    $('#input-form').on('submit', function(e){
        $.post('/messages', { //jquery의 post라는 펑션
            msg: $chatmsg.val(), 
            name: username
        });// username과 msg내용 보냄
        $chatmsg.val(""); //빈문자열로 바꾸고
        $chatmsg.focus(); // 다시 문자열 입력할 수 있게
        return false;
    });
    // 서버가 이벤트소스르 통해서 알려줄 경우 호출
    var addMessage = function(data){
        var text = "";
        if(!isBlank(data.name)){
           // 만약 데이터에 name이 있을 경우
            text = '<strong>' + data.name + ':</strong>';
        }
        text += data.msg // 텍스트에 데이터 메세지 추가
        $chatlog.prepend('<div><span>' + text + '</span></dvi>');
    };

    addMessage({
        msg: 'hello',
        name: 'aaa'
    })

    addMessage({
        msg: 'hello2',
    })
    // 이벤트 소스 열기
    var es = new EventSource('/stream') // 이벤트 소스 요청 경로
    es.onopen = function(e){ 
        $.pos('users/', {
            name: username
        }); // 이벤트 소스가 열릴떄 유저스로 메세지 전송
    }
    // 이벤트 소스를 통해서 메세지가 올때 onmessage펑션이 호출
    es.onmessage = function(e) { //e 인자로 받아서
        var msg = JSON.parse(e.data); // json형태의 메세지
        addMessage(msg); // 메세지 추가
    };

    // 상대방이 나갔을때 표시
    window.onbeforeunload = function() {
        $.ajax({
            url: "/users?username=" + username,
            type: "DELETE"
        });  // 딜리트 메소드
        es.close() // 이벤트 소스 닫기
    };
})

ex) main

package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"time"

	"github.com/antage/eventsource"
	"github.com/gorilla/pat"
	"github.com/urfave/negroni"
)

func postMessageHandler(w http.ResponseWriter, r *http.Request) {
	msg := r.FormValue("msg")
	name := r.FormValue("name")
	sendMessage(name, msg) // 메세지 보내기
}

func addUserHandler(w http.ResponseWriter, r *http.Request) {
	username := r.FormValue("name")
	sendMessage("", fmt.Sprintf("add user: %s", username))
}

func leftUserHandler(w http.ResponseWriter, r *http.Request) {
	username := r.FormValue("username")
	sendMessage("", fmt.Sprintf("left user: %s", username))
}

type Message struct {
	Name string `json:"name"`
	Msg  string `json:"msg"`
}

var msgCh chan Message

// message를 집어넣는 채널
func sendMessage(name, msg string) {
	msgCh <- Message{name, msg}
	// sendMessage 가 호출되면 이름과 메세지가 msg채널에 들어감
}

//메세지 채널에서 하나씩 팝해옴
func processMsgCh(es eventsource.EventSource) {
	for msg := range msgCh {
		data, _ := json.Marshal(msg) //json형태로 마샬
		es.SendEventMessage(string(data), "", strconv.Itoa(time.Now().Nanosecond()))
		// 메세지와 아이디를
	}
}
func main() {
	es := eventsource.New(nil, nil) //이벤트 소스 패키지 사용
	defer es.Close()

	go processMsgCh(es) // go쓰래드로 메세지를 팝해줌

	mux := pat.New()
	mux.Post("/messages", postMessageHandler)
	mux.Handle("/stream", es)
	mux.Post("/users", addUserHandler)
	mux.Delete("/users", leftUserHandler)

	n := negroni.Classic()
	n.UseHandler(mux)
	//negroni 데코레이터로 먹스핸들러를 감쌈
	//기본적으로 퍼블릭 서버를 제공
	http.ListenAndServe(":3000", n)
}

좋은 웹페이지 즐겨찾기