Websocket Interaction

최신 업데이트:2024-09-03 17:00:40

이 예제에서는 단일 Edge Cloud Apps 함수를 사용하여 완전한 WebSocket 서비스를 구축하는 방법을 보여줍니다.

기존 CDN은 WebSocket 트래픽에 대한 가속 기능이 제한적입니다. WebSocket 로직을 에지로 마이그레이션함으로써 지연 시간을 크게 줄이고, 더 빠른 응답 경험을 제공하고, 오리진 서버의 압력을 완화할 수 있습니다.

예제 설명

이 예제에서는 단일 Edge Cloud Apps 함수가 다음을 포함한 WebSocket 서비스를 생성하는 방법을 보여줍니다.

  • WebSocket 서버: 클라이언트의 WebSocket 연결 요청을 수신하고 처리합니다.
  • HTML 페이지 생성: WebSocket 연결을 설정하고 상호 작용하기 위한 클라이언트 측 JavaScript 코드가 포함된 HTML 페이지를 반환합니다.

사용자가 HTML 페이지를 방문하면 페이지의 JavaScript 코드가 함수와 WebSocket 연결을 설정합니다. 사용자가 페이지의 버튼을 클릭하면 브라우저는 WebSocket을 통해 함수에 메시지를 보냅니다. 함수는 메시지를 처리하고 결과를 반환하며, 브라우저는 실시간으로 결과로 페이지를 업데이트합니다.

전체 상호 작용은 에지 노드에서 이루어지므로 오리진 서버와 통신할 필요가 없어 지연 시간이 줄어들고 응답 시간이 빨라집니다.

코드 예제

async function handleRequest(request) {
    let { pathname } = new URL(request.url);
    pathname = pathname.toLowerCase();
    if (pathname.endsWith('/')) {
        pathname = pathname.substring(0, pathname.length - 1);
    }
    
    // 요청 경로에 따라 다른 처리 로직을 선택합니다.
    if (pathname === '/wsclick.html') {
        // HTML 페이지 반환
        return new Response(HOMEPAGE, {headers: {"Content-Type": "text/html; charset=utf-8"}});
    } else if (pathname === '/wsclick') {
        // WebSocket 연결 요청 처리
        return websocketHandler(request); 
    }
}

// WebSocket 연결 요청 처리
async function websocketHandler(request) {
    const upgradeHeader = request.headers.get("Upgrade");
    // WebSocket 업그레이드 요청인지 확인합니다.
    if (upgradeHeader !== "websocket") {
        return new Response("웹소켓이 예상됨", { status: 400 });
    }

    // WebSocket 연결 쌍 생성
    const [client, server] = Object.values(new WebSocketPair());
    let serverName = request.headers.get('x-ws-request-id');
    if (serverName) {
      serverName = serverName.match(/_(.*?)_/)[1];
    }
    await handleSession(server, serverName); // WebSocket 세션 처리

    // WebSocket 응답 반환
    return new Response(null, {
        status: 101,
        webSocket: client
    });
}

let count = 0;

// WebSocket 세션 처리
async function handleSession(websocket, serverName) {
    websocket.accept(); // WebSocket 연결 수락
    websocket.addEventListener("message", async ({ data }) => {
        console.log('메시지', data);
        const now = (new Date()).toString().replace(' ()', '');
        if (data === "CLICK") {
            // "CLICK" 메시지 처리
            count += 1;
            websocket.send(JSON.stringify({ count, tz: now, server: serverName })); // 카운트 및 서버 정보 반환
        } else {
            // 알 수 없는 메시지 처리
            websocket.send(JSON.stringify({ error: "알 수 없는 메시지가 수신되었습니다.", tz: now, server: serverName })); 
        }
    });
  
    // 연결 닫기 이벤트 처리
    websocket.addEventListener("close", async evt => {
        console.log('xxx:닫기', evt);
    });
}

// HTML 페이지 콘텐츠
const HOMEPAGE = `
<html>
<head>
<title>WebSocket 클릭 카운터 예제</title>
<style>
  body {
    margin: 1rem;
    font-family: monospace;
    font-size: 16px;
  }
</style>
</head>
<body>
<p>클릭 수: <span id="num"></span></p>
<button id="click">클릭하여 메시지 보내기</button>

<p>WebSocket 서버가 인식하지 못하는 메시지를 보낼 수도 있습니다. 이렇게 하면 WebSocket 서버가 클라이언트에 오류 메시지를 반환합니다.</p>
<button id="unknown">인식할 수 없는 메시지 보내기</button>

<p>테스트가 끝나면 아래 버튼을 클릭하여 연결을 닫을 수 있습니다. 페이지를 새로 고칠 때까지 추가 클릭은 효과가 없습니다.</p>
<button id="close">연결 닫기</button>

<p id="error" style="color: red;"></p>

<h4>수신된 서버 WebSocket 메시지</h4>
<ul id="events"></ul>

<script>
  let ws

  async function websocket(url) {
    ws = new WebSocket(url)

    if (!ws) {
      throw new Error("서버에서 ws를 수락하지 않았습니다.")
    }

    ws.addEventListener("open", () => {
      console.log('WebSocket을 열었습니다.')
      updateCount(0)
    })

    ws.addEventListener("message", ({ data }) => {
      const { count, tz, error } = JSON.parse(data)
      addNewEvent(data)
      if (error) {
        setErrorMessage(error)
      } else {
        setErrorMessage()
        updateCount(count)
      }
    })

    ws.addEventListener("close", () => {
      console.log('WebSocket을 닫았습니다.')

      const list = document.querySelector("#events")
      list.innerText = ""
      updateCount(0)
      setErrorMessage()
    })
  }

  const url = new URL(window.location)
  url.protocol = "ws"
  url.pathname = "/wsclick"
  websocket(url)

  document.querySelector("#click").addEventListener("click", () => {
    ws.send("CLICK")
  })

  const updateCount = (count) => {
    document.querySelector("#num").innerText = count
  }

  const addNewEvent = (data) => {
    const list = document.querySelector("#events")
    const item = document.createElement("li")
    item.innerText = data
    list.prepend(item)
  }

  const closeConnection = () => ws.close()

  document.querySelector("#close").addEventListener("click", closeConnection)
  document.querySelector("#unknown").addEventListener("click", () => ws.send("HUH"))

  const setErrorMessage = message => {
    document.querySelector("#error").innerHTML = message ? message : ""
  }
</script>
</body>
</html>
`;
                    
addEventListener("fetch", event => {
    return event.respondWith(handleRequest(event.request));
});
이 문서의 내용이 도움이 되었습니까?
아니오
정상적으로 제출되었습니다.피드백을 주셔서 감사합니다.앞으로도 개선을 위해 노력하겠습니다.