Server-Sent Events를 사용하는 Websocket 없이 실시간

다음 자습서에서는 POST 요청을 시뮬레이션하기 위해 cURL을 사용하는 한 Express 및 Node.js를 사용합니다. 이는 HTTP 프로토콜 구현이 있는 모든 프로그래밍 언어를 사용하여 수행할 수도 있습니다.

노드 구성 만들기




touch package.json



{
  "type": "module",
  "scripts": {
    "start": "node sources/server/index.js
  }
}


익스프레스 설치




npm install --save --save-exact express


서버 파일 생성




mkdir -p sources/server
touch sources/server/index.js



import express from "express"

// Express server initialization
const server = express()

// Port exposing the HTTP server
const port = 8000

// Host to listen for HTTP requests
const host = "0.0.0.0"

// Used for hosting our client files (HTML, CSS, JavaScript, ...)
server.use(express.static("sources/client"))

// Server-Sent Event HTTP route
server.get("/api/users/event", (request, response) => {
    // Used to prevent the browser from closing the connection after receiving the first body
    response.setHeader("Connection", "keep-alive")

    // Used to tell the browser that we are sending Server-Sent Events
    response.setHeader("Content-Type", "text/event-stream")

    // Used as a security measure to prevent caching the data we send
    response.setHeader("Cache-Control", "no-cache")

    // Loop to simulate realtime data updates at regular intervals
    setInterval(() => {
        // Expected format that the browser will decode for use in the client
        // write is used to prevent closing the connection from the server with a send, json, end, ...
        response.write("event: update\ndata: Hello, world!\n\n")
    }, 1000)
})

// Start listening for HTTP requests
server.listen(port, host, () => {
    // Displaying the host & port listened in the console for debugging purposes
    console.log(`Listening on http://${host}:${port}`)
})


HTML 파일 만들기




mkdir -p sources/client
touch sources/client/index.html



<!DOCTYPE html>
<html lang="en-US">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta name="description" content="Test for demonstrating how to use the Server-Sent Events">
        <title>Server-Sent Events</title>
    </head>
    <body>
        <script src="./index.js" type="module"></script>
    </body>
</html>


자바스크립트 파일 생성




touch sources/client/index.js



const usersEventSource = new EventSource("/api/users/event")

usersEventSource.addEventListener("open", () => {
    console.log("Connected to the server")
})

usersEventSource.addEventListener("error", error => {
    console.error("Oops! Something went wrong, here is why")
    console.error(error)
})

usersEventSource.addEventListener("update", event => {
    console.log("Successfully received a notification from the server")
    console.log(event.data)
})


서버 시작




npm start


브라우저 열기



http://localhost:8000 에서 브라우저를 열거나 변경하기로 결정한 경우 선택한 포트와 호스트를 사용합니다.

메시지가 1초마다 업데이트되는 것을 콘솔에 표시해야 합니다.

브라우저의 네트워크 탭을 열면 주기적인 HTTP 요청을 보내지 않는 것을 볼 수 있습니다. 이는 데이터를 수신할 때 표준 동작인 서버에서 본문을 수신한 후에도 연결이 유지되기 때문입니다. 서버에서 클라이언트로.

보다 실제적인 예를 만들어 이 예를 개선해 보겠습니다.

BodyParser 설치




npm install --save --save-exact body-parser


BodyParser 사용을 위한 미들웨어 추가




import express from "express"

// Let us import the BodyParser library
import bodyParser from "body-parser"

const server = express()

const port = 8000

const host = "0.0.0.0"

server.use(express.static("sources/client"))

// Let us parse the request body and access the
// JSON content easily
server.use(bodyParser.json())

server.get("/api/users/event", (request, response) => {
    response.setHeader("Connection", "keep-alive")
    response.setHeader("Content-Type", "text/event-stream")
    response.setHeader("Cache-Control", "no-cache")

    setInterval(() => {
        response.write("event: update\ndata: Hello, world!\n\n")
    }, 1000)
})

server.listen(port, host, () => {
    console.log(`Listening on http://${host}:${port}`)
})


사용자 생성을 위한 경로 추가




import express from "express"
import bodyParser from "body-parser"

// Import the EventEmitter class
import { EventEmitter } from "events"

const server = express()
const port = 8000
const host = "0.0.0.0"

// Create the event emitter for sending and receiving updates
const usersEventEmitter = new EventEmitter()

server.use(express.static("sources/client"))
server.use(bodyParser.json())

server.get("/api/users/event", (request, response) => {
    response.setHeader("Connection", "keep-alive")
    response.setHeader("Content-Type", "text/event-stream")
    response.setHeader("Cache-Control", "no-cache")

    // Wait and listen for user updates
    usersEventEmitter.on("user", user => {
        // Sends back the user that has been created with the /api/users route
        response.write(`event: update\ndata: ${JSON.stringify(user)}\n\n`)
    })
})

// Adds a new route handler for user creation requests
server.post("/api/users", (request, response) => {
    // Get the firstname from the parsed JSON body
    const firstname = request.body.firstname

    // Get the lastname from the parsed JSON body
    const lastname = request.body.lastname

    // Create a user object
    const user = {firstname, lastname}

    // Sends the event to the /api/users/event route
    usersEventEmitter.emit("user", user)

    // Tells the client that the user has been created
    response.status(201)

    // Closes the HTTP connection without any body
    response.end()
})

server.listen(port, host, () => {
    console.log(`Listening on http://${host}:${port}`)
})


서버를 다시 시작하십시오.




npm start


브라우저 새로고침



아직 사용자를 생성하지 않았기 때문에 콘솔에 아무것도 없어야 합니다. 지금 해보자.

cURL을 사용하여 새 사용자 만들기




curl -X POST -H 'Content-Type: application/json' -d '{"firstname":"John","lastname":"DOE"}' localhost:8000/api/users


브라우저로 돌아갑니다. 이제 새 알림이 표시됩니다.

새 사용자를 만든 후에만 알림을 받게 됩니다.

목적



Server-Sent Event는 주문 상태 또는 알림 시스템과 같은 업데이트된 데이터를 서버에서 실시간으로 받아야 하는 경우 정말 좋습니다.

또한 설정이 정말 쉽고 RFC를 따르는 복잡성이 추가된 완전히 새로운 프로토콜을 구현할 필요가 없습니다. 모든 최신 브라우저에서 웹 API로 구현되며 서버 구현은 매우 간단합니다.

폴링 또는 긴 폴링을 사용하여 서버에서 데이터를 가져와야 하는 대부분의 시나리오에서 Server-Sent Events는 하나의 HTTP 연결만 생성하고 필요하지 않을 때 데이터를 가져오지 않기 때문에 보다 효율적인 대안이 될 수 있습니다.

제한 사항



클라이언트에서 서버로 실시간 데이터를 다시 보낼 수 없으며 이 경우 Websocket 프로토콜 구현을 사용해야 합니다.

무엇 향후 계획?


  • 데이터베이스 선택
  • POST/api/users 경로에서 생성된 사용자를 데이터베이스에 저장합니다
  • .
  • EventSource에 연결하기 전에 데이터베이스에 이미 저장된 사용자 목록을 가져옵니다
  • .
  • 연결이 끊어진 경우 서버에 다시 연결하는 방법 찾기(오류, 네트워크 문제 등...)
  • 실시간으로 사용자 가져오기를 버리고 HTTP 및 SSE 요청을 모두 사용하여 인스턴트 채팅 메시징 시스템을 만들어 처음부터 시작합니다
  • .

    좋은 웹페이지 즐겨찾기