서버-보내기 이벤트 (SSE) 소개
서버-보내기 이벤트 (SSE)는 클라이언트가 HTTP 연결을 통해 서버로부터 자동으로 업데이트를 받을 수 있는 서버 푸시 기술입니다. 초기 클라이언트 연결을 설정한 후에 서버가 클라이언트로 데이터 전송을 시작하는 방법을 설명합니다. SSE는 일반적으로 메시지 업데이트를 보내거나 지속적인 데이터 스트림을 브라우저 클라이언트로 보내는 데 사용되며, 이를 위해 JavaScript API인 EventSource를 통한 로컬 크로스 브라우저 스트림을 향상시키기 위해 사용됩니다. 클라이언트는 이 API를 사용하여 특정 URL을 요청하여 이벤트 스트림을 받을 수 있습니다. SSE는 HTML5의 일부로, EventSource API는 WHATWG에 의해 표준화되었습니다. SSE의 미디어 유형은 text/event-stream입니다.
참고: SSE와 WebSocket의 가장 큰 차이점은 SSE가 일방적인 서버-클라이언트 메시지 푸시인 반면, WebSocket은 양방향 메시지 푸시입니다. 때때로 비즈니스 요구사항이 그리 복잡하지 않을 때, SSE의 일방적인 메시지 푸시가 충분합니다. 이는 ChatGPT AI 대화에서 사용된 기술과 유사합니다.
Fiber SSE 예제
package main
import (
"bufio"
"fmt"
"log"
"time"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/valyala/fasthttp"
)
// HTML 클라이언트를 모의하는 부분입니다. 실제 비즈니스 시나리오에서는 프론트엔드에서 이를 구현합니다.
var index = []byte(`<!DOCTYPE html>
<html>
<body>
<h1>SSE Messages</h1>
<div id="result"></div>
<script>
if(typeof(EventSource) !== "undefined") {
var source = new EventSource("http://127.0.0.1:3000/sse");
source.onmessage = function(event) {
document.getElementById("result").innerHTML += event.data + "<br>";
};
} else {
document.getElementById("result").innerHTML = "죄송합니다, 사용 중인 브라우저가 서버-보내기 이벤트를 지원하지 않습니다...";
}
</script>
</body>
</html>
`)
func main() {
// Fiber 인스턴스
app := fiber.New()
// 모든 도메인에서의 접근을 허용하기 위한 CORS 제한 설정
app.Use(cors.New(cors.Config{
AllowOrigins: "*",
AllowHeaders: "Cache-Control",
AllowCredentials: true,
}))
// / 경로에 대한 액세스, 먼저 프론트엔드 페이지를 반환하여 프론트엔드가 백엔드에 SSE 메시지 푸시를 시작하도록 허용
app.Get("/", func(c *fiber.Ctx) error {
c.Response().Header.SetContentType(fiber.MIMETextHTMLCharsetUTF8)
return c.Status(fiber.StatusOK).Send(index)
})
// sse 메시지 푸시 주소
app.Get("/sse", func(c *fiber.Ctx) error {
// SSE http 헤더 설정, Content-Type에 주의
c.Set("Content-Type", "text/event-stream")
c.Set("Cache-Control", "no-cache")
c.Set("Connection", "keep-alive")
c.Set("Transfer-Encoding", "chunked")
// 메시지 푸시 시작
c.Context().SetBodyStreamWriter(fasthttp.StreamWriter(func(w *bufio.Writer) {
fmt.Println("WRITER")
var i int
// 클라이언트로 지속적으로 메시지 푸시 모의
for {
i++
msg := fmt.Sprintf("%d - 현재 시간: %v", i, time.Now())
// writer 및 Fprintf 함수를 사용하여 메시지를 클라이언트로 푸시하는데, 실제 비즈니스 시나리오에서는 프론트엔드 처리를 위해 JSON 텍스트를 보낼 수 있음
fmt.Fprintf(w, "data: 메시지: %s\n\n", msg)
fmt.Println(msg)
// 출력 데이터를 클라이언트로 플러시
err := w.Flush()
if err != nil {
// 웹 브라우저에서 페이지를 새로 고치면 새로운 SSE 연결을 설정하나, 오직 마지막 연결만 유효하므로 여기서 종료되지 않은 연결을 닫아야 합니다.
fmt.Printf("플러싱 중 오류: %v. http 연결을 닫습니다.\n", err)
break
}
time.Sleep(2 * time.Second)
}
}))
return nil
})
// 서버 시작
log.Fatal(app.Listen(":3000"))
}