자습서: 하위 프로토콜을 사용하여 WebSocket 클라이언트 간에 메시지 게시 및 구독
이 문서의 내용
채팅 앱 빌드 자습서 에서는 WebSocket API를 사용하여 Azure Web PubSub와 데이터를 보내고 받는 방법을 알아보았습니다. 클라이언트가 서비스와 통신할 때는 프로토콜이 필요하지 않습니다. 예를 들어 WebSocket.send()
를 사용하여 모든 형식의 데이터를 보낼 수 있으며 서버는 데이터를 있는 그대로 수신합니다. WebSocket API 프로세스는 사용하기 쉽지만 기능이 제한적입니다. 예를 들어 서버에 이벤트를 보낼 때 이벤트 이름을 지정하거나, 서버에 메시지를 전송하는 대신 다른 클라이언트에 메시지를 게시할 수 없습니다. 이 자습서에서는 하위 프로토콜을 사용하여 클라이언트의 기능을 확장하는 방법을 배웁니다.
이 자습서에서는 다음을 하는 방법을 알아볼 수 있습니다.
Web PubSub 서비스 인스턴스 만들기
WebSocket 연결을 설정하기 위한 전체 URL 생성
하위 프로토콜을 사용하여 WebSocket 클라이언트 간에 메시지 게시
Azure를 구독 하고 있지 않다면 시작하기 전에 Azure 체험 계정 을 만듭니다.
사전 요구 사항
이렇게 설정하려면 버전 2.22.0 이상의 Azure CLI가 필요합니다. Azure Cloud Shell을 사용하는 경우 최신 버전이 이미 설치되어 있습니다.
Azure Web PubSub 인스턴스 만들기
리소스 그룹 만들기
리소스 그룹은 Azure 리소스가 배포 및 관리되는 논리적 컨테이너입니다. az group create 명령을 사용하여 eastus
위치에서 myResourceGroup
이라는 리소스 그룹을 만듭니다.
az group create --name myResourceGroup --location EastUS
Web PubSub 인스턴스 만들기
az extension add 를 실행하여 webpubsub 확장을 현재 버전으로 설치하거나 업그레이드합니다.
az extension add --upgrade --name webpubsub
Azure CLI az webpubsub create 명령을 사용하여 만든 리소스 그룹에 Web PubSub를 만듭니다. 다음 명령은 EastUS 의 리소스 그룹 myResourceGroup 아래에 무료 Web PubSub 리소스를 만듭니다.
Important
각 Web PubSub 리소스에는 고유한 이름이 있어야 합니다. 다음 예제에서 <your-unique-resource-name>을 Web PubSub의 이름으로 바꿉니다.
az webpubsub create --name "<your-unique-resource-name>" --resource-group "myResourceGroup" --location "EastUS" --sku Free_F1
이 명령의 출력에는 새로 만든 리소스의 속성이 표시됩니다. 아래에 나열된 두 개의 속성을 기록합니다.
리소스 이름 : 위의 --name
매개 변수에 제공한 이름입니다.
hostName : 이 예제에서 호스트 이름은 <your-unique-resource-name>.webpubsub.azure.com/
입니다.
이때 Azure 계정은 이 새 리소스에서 모든 작업을 수행할 권한이 있는 유일한 계정입니다.
나중에 사용할 수 있도록 ConnectionString 가져오기
Important
연결 문자열에는 애플리케이션이 Azure Web PubSub 서비스에 액세스하는 데 필요한 권한 부여 정보가 포함됩니다. 연결 문자열 내의 액세스 키는 서비스의 루트 암호와 비슷합니다. 프로덕션 환경에서는 항상 액세스 키를 보호해야 합니다. Azure Key Vault를 사용하여 키를 안전하게 관리하고 회전합니다. 액세스 키를 다른 사용자에게 배포하거나 하드 코딩하거나 다른 사용자가 액세스할 수 있는 일반 텍스트로 저장하지 않도록 합니다. 키가 손상되었다고 생각되면 키를 교체하세요.
Azure CLI az webpubsub key 명령을 사용하여 서비스의 ConnectionString 을 가져옵니다. <your-unique-resource-name>
자리 표시자를 Azure Web PubSub 인스턴스 이름으로 바꿉니다.
az webpubsub key show --resource-group myResourceGroup --name <your-unique-resource-name> --query primaryConnectionString --output tsv
나중에 사용할 수 있게 연결 문자열을 복사합니다.
가져온 ConnectionString 을 복사합니다. 이 자습서의 뒷부분에서 <connection_string>
값으로 사용됩니다.
프로젝트 설정
필수 조건
하위 프로토콜 사용
클라이언트는 특정 하위 프로토콜 을 사용하여 WebSocket 연결을 시작할 수 있습니다. Azure Web PubSub 서비스는 클라이언트가 업스트리밍 서버에 왕복하는 대신 Web PubSub 서비스를 통해 직접 게시/구독할 수 있도록 json.webpubsub.azure.v1
이라는 하위 프로토콜을 지원합니다. 하위 프로토콜에 대한 자세한 내용은 Azure Web PubSub 지원 JSON WebSocket 하위 프로토콜 을 참조하세요.
다른 프로토콜 이름을 사용하는 경우 해당 이름은 서비스에서 무시되고 연결 이벤트 처리기의 서버에 전달되므로 사용자 고유의 프로토콜을 만들 수 있습니다.
이제 json.webpubsub.azure.v1
하위 프로토콜을 사용하여 웹 애플리케이션을 만들어 보겠습니다.
종속성 설치
mkdir logstream
cd logstream
dotnet new web
dotnet add package Microsoft.Extensions.Azure
dotnet add package Azure.Messaging.WebPubSub
mkdir logstream
cd logstream
npm init -y
npm install --save express
npm install --save ws
npm install --save node-fetch
npm install --save @azure/web-pubsub
mkdir logstream
cd logstream
# Create venv
python -m venv env
# Active venv
source ./env/bin/activate
pip install azure-messaging-webpubsubservice
Javalin 웹 프레임워크를 사용하여 웹 페이지를 호스트합니다.
먼저 Maven을 사용하여 새 logstream-webserver
앱을 만들고 logstream-webserver 폴더로 전환해 보겠습니다.
mvn archetype:generate --define interactiveMode=n --define groupId=com.webpubsub.tutorial --define artifactId=logstream-webserver --define archetypeArtifactId=maven-archetype-quickstart --define archetypeVersion=1.4
cd logstream-webserver
Azure Web PubSub SDK 및 javalin
웹 프레임워크 종속성을 pom.xml
의 dependencies
노드에 추가해 보겠습니다.
javalin
: 간단한 Java용 간단한 웹 프레임워크
slf4j-simple
: Java용 로거
azure-messaging-webpubsub
: Azure Web PubSub를 사용하는 서비스 클라이언트 SDK
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-messaging-webpubsub</artifactId>
<version>1.0.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.javalin/javalin -->
<dependency>
<groupId>io.javalin</groupId>
<artifactId>javalin</artifactId>
<version>3.13.6</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.30</version>
</dependency>
/negotiate
API 및 웹 페이지를 호스트하는 서버 쪽을 만듭니다.
Program.cs
를 다음 코드로 업데이트합니다.
AddAzureClients
를 업데이트하여 서비스 클라이언트를 추가하고 구성에서 연결 문자열을 읽습니다.
app.Run();
앞에 app.UseStaticFiles();
를 추가하여 정적 파일을 지원합니다.
/negotiate
요청을 통해 클라이언트 액세스 토큰을 생성하도록 app.MapGet
를 업데이트합니다.
using Azure.Messaging.WebPubSub;
using Microsoft.Extensions.Azure;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAzureClients(s =>
{
s.AddWebPubSubServiceClient(builder.Configuration["Azure:WebPubSub:ConnectionString"], "stream");
});
var app = builder.Build();
app.UseStaticFiles();
app.MapGet("/negotiate", async context =>
{
var service = context.RequestServices.GetRequiredService<WebPubSubServiceClient>();
var response = new
{
url = service.GetClientAccessUri(roles: new string[] { "webpubsub.sendToGroup.stream", "webpubsub.joinLeaveGroup.stream" }).AbsoluteUri
};
await context.Response.WriteAsJsonAsync(response);
});
app.Run();
server.js
를 만들고 아래 코드를 추가합니다.
const express = require('express');
const { WebPubSubServiceClient } = require('@azure/web-pubsub');
let service = new WebPubSubServiceClient(process.env.WebPubSubConnectionString, 'stream');
const app = express();
app.get('/negotiate', async (req, res) => {
let token = await service.getClientAccessToken({
roles: ['webpubsub.sendToGroup.stream', 'webpubsub.joinLeaveGroup.stream']
});
res.send({
url: token.url
});
});
app.use(express.static('public'));
app.listen(8080, () => console.log('server started'));
/negotiate
API 및 웹 페이지를 호스트하는 server.py
를 만듭니다.
import json
import sys
from http.server import HTTPServer, SimpleHTTPRequestHandler
from azure.messaging.webpubsubservice import WebPubSubServiceClient
service = WebPubSubServiceClient.from_connection_string(sys.argv[1], hub='stream')
class Resquest(SimpleHTTPRequestHandler):
def do_GET(self):
if self.path == '/':
self.path = 'public/index.html'
return SimpleHTTPRequestHandler.do_GET(self)
elif self.path == '/negotiate':
roles = ['webpubsub.sendToGroup.stream',
'webpubsub.joinLeaveGroup.stream']
token = service.get_client_access_token(roles=roles)
self.send_response(200)
self.send_header('Content-Type', 'application/json')
self.end_headers()
self.wfile.write(json.dumps({
'url': token['url']
}).encode())
if __name__ == '__main__':
if len(sys.argv) != 2:
print('Usage: python server.py <connection-string>')
exit(1)
server = HTTPServer(('localhost', 8080), Resquest)
print('server started')
server.serve_forever()
다음과 같이 편집기에서 /src/main/java/com/webpubsub/tutorial 디렉터리를 찾아서 App.java 파일을 열고, Javalin.create
를 사용하여 고정 파일을 제공합니다.
package com.webpubsub.tutorial;
import com.azure.messaging.webpubsub.WebPubSubServiceClient;
import com.azure.messaging.webpubsub.WebPubSubServiceClientBuilder;
import com.azure.messaging.webpubsub.models.GetClientAccessTokenOptions;
import com.azure.messaging.webpubsub.models.WebPubSubClientAccessToken;
import io.javalin.Javalin;
public class App {
public static void main(String[] args) {
if (args.length != 1) {
System.out.println("Expecting 1 arguments: <connection-string>");
return;
}
// create the service client
WebPubSubServiceClient service = new WebPubSubServiceClientBuilder()
.connectionString(args[0])
.hub("chat")
.buildClient();
// start a server
Javalin app = Javalin.create(config -> {
config.addStaticFiles("public");
}).start(8080);
// Handle the negotiate request and return the token to the client
app.get("/negotiate", ctx -> {
GetClientAccessTokenOptions option = new GetClientAccessTokenOptions();
option.addRole("webpubsub.sendToGroup.stream");
option.addRole("webpubsub.joinLeaveGroup.stream");
WebPubSubClientAccessToken token = service.getClientAccessToken(option);
// return JSON string
ctx.result("{\"url\":\"" + token.getUrl() + "\"}");
return;
});
}
}
설정에 따라 pom.xml에서 언어 수준을 Java 8로 명시적으로 설정해야 할 수도 있습니다. 다음 코드 조각을 추가합니다.
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
웹 페이지 만들기
아래 내용으로 HTML 페이지를 만들고 wwwroot/index.html
로 저장합니다.
아래 내용으로 HTML 페이지를 만들고 public/index.html
로 저장합니다.
아래 내용으로 HTML 페이지를 만들고 public/index.html
로 저장합니다.
아래 콘텐츠를 사용하여 HTML 페이지를 만들고 /src/main/resources/public/index.html 에 저장합니다.
<html>
<body>
<div id="output"></div>
<script>
(async function () {
let res = await fetch('/negotiate')
let data = await res.json();
let ws = new WebSocket(data.url, 'json.webpubsub.azure.v1');
ws.onopen = () => {
console.log('connected');
};
let output = document.querySelector('#output');
ws.onmessage = event => {
let d = document.createElement('p');
d.innerText = event.data;
output.appendChild(d);
};
})();
</script>
</body>
</html>
위 코드는 서비스에 연결하고 페이지에 수신된 메시지를 인쇄하기만 합니다. 주요 변경 내용은 WebSocket 연결을 만들 때 하위 프로토콜을 지정하는 것입니다.
서버 실행
.NET Core용 Secret Manager 도구를 사용하여 연결 문자열을 설정합니다. 아래 명령을 실행하고, <connection_string>
을 이전 단계 에서 가져온 연결 문자열로 바꾸고, 브라우저에서 http://localhost:5000/index.html을 엽니다.
dotnet user-secrets init
dotnet user-secrets set Azure:WebPubSub:ConnectionString "<connection-string>"
dotnet run
이제 아래 명령을 실행하고, <connection-string>
을 이전 단계 에서 가져온 ConnectionString 으로 바꾸고, 브라우저에서 http://localhost:8080을 엽니다.
export WebPubSubConnectionString="<connection-string>"
node server
이제 아래 명령을 실행하고, <connection-string>
을 이전 단계 에서 가져온 ConnectionString 으로 바꾸고, 브라우저에서 http://localhost:8080을 엽니다.
python server.py "<connection-string>"
이제 아래 명령을 실행하고, <connection-string>
을 이전 단계 에서 가져온 ConnectionString 으로 바꾸고, 브라우저에서 http://localhost:8080을 엽니다.
mvn compile & mvn package & mvn exec:java -Dexec.mainClass="com.webpubsub.tutorial.App" -Dexec.cleanupDaemonThreads=false -Dexec.args="'<connection_string>'"
Chrome을 사용하는 경우 F12 키를 누르거나 마우스 오른쪽 단추 ->검사 ->개발자 도구 를 클릭하고 네트워크 탭을 선택하면 됩니다. 웹 페이지를 로드하면 WebSocket 연결이 설정된 것을 볼 수 있습니다. WebSocket 연결을 검사하기 위해 선택하면 아래 connected
이벤트 메시지가 클라이언트에 수신되는 것을 확인할 수 있습니다. 이 클라이언트에 대해 생성된 connectionId
를 가져올 수 있습니다.
{"type":"system","event":"connected","userId":null,"connectionId":"<the_connection_id>"}
하위 프로토콜의 도움을 받으면 연결이 connected
일 때 연결의 일부 메타데이터를 가져올 수 있습니다.
이제 클라이언트는 일반 텍스트 대신 JSON 메시지를 받습니다. JSON 메시지에는 메시지의 유형 및 원본과 같은 추가 정보가 포함되어 있습니다. 따라서 이 정보를 사용하여 메시지에 대한 추가 처리를 수행할 수 있습니다. 예를 들어 다른 원본에서 온 메시지인 경우 다른 스타일로 표시할 수 있습니다. 이에 대한 내용은 이후 섹션에서 찾을 수 있습니다.
클라이언트에서 메시지 게시
채팅 앱 빌드 자습서에서 클라이언트가 WebSocket 연결을 통해 Web PubSub 서비스에 메시지를 보내면 서비스는 서버 쪽에서 사용자 이벤트를 트리거합니다. 하위 프로토콜을 사용하면 클라이언트가 JSON 메시지를 전송하여 더 많은 기능을 갖게 됩니다. 예를 들어 Web PubSub 서비스를 통해 클라이언트에서 다른 클라이언트로 직접 메시지를 게시할 수 있습니다.
이 기능은 대량의 데이터를 다른 클라이언트에 실시간으로 스트리밍하려는 경우에 유용합니다. 이 기능을 사용하여 콘솔 로그를 실시간으로 브라우저에 스트리밍할 수 있는 로그 스트리밍 애플리케이션을 빌드해 보겠습니다.
스트리밍 프로그램 만들기
다음과 같이 stream
프로그램을 만듭니다.
mkdir stream
cd stream
dotnet new console
Program.cs
를 다음 내용으로 업데이트합니다.
using System;
using System.Net.Http;
using System.Net.WebSockets;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
namespace stream
{
class Program
{
private static readonly HttpClient http = new HttpClient();
static async Task Main(string[] args)
{
// Get client url from remote
var stream = await http.GetStreamAsync("http://localhost:5000/negotiate");
var url = (await JsonSerializer.DeserializeAsync<ClientToken>(stream)).url;
var client = new ClientWebSocket();
client.Options.AddSubProtocol("json.webpubsub.azure.v1");
await client.ConnectAsync(new Uri(url), default);
Console.WriteLine("Connected.");
var streaming = Console.ReadLine();
while (streaming != null)
{
if (!string.IsNullOrEmpty(streaming))
{
var message = JsonSerializer.Serialize(new
{
type = "sendToGroup",
group = "stream",
data = streaming + Environment.NewLine,
});
Console.WriteLine("Sending " + message);
await client.SendAsync(Encoding.UTF8.GetBytes(message), WebSocketMessageType.Text, true, default);
}
streaming = Console.ReadLine();
}
await client.CloseAsync(WebSocketCloseStatus.NormalClosure, null, default);
}
private sealed class ClientToken
{
public string url { get; set; }
}
}
}
다음 내용으로 stream.js
를 만듭니다.
const WebSocket = require('ws');
const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args));
async function main() {
let res = await fetch(`http://localhost:8080/negotiate`);
let data = await res.json();
let ws = new WebSocket(data.url, 'json.webpubsub.azure.v1');
let ackId = 0;
ws.on('open', () => {
process.stdin.on('data', data => {
ws.send(JSON.stringify({
type: 'sendToGroup',
group: 'stream',
ackId: ++ackId,
dataType: 'text',
data: data.toString()
}));
});
});
ws.on('message', data => console.log("Received: %s", data));
process.stdin.on('close', () => ws.close());
}
main();
위의 코드는 서비스에 대한 WebSocket 연결을 만든 다음, 데이터를 수신할 때마다 ws.send()
를 사용하여 데이터를 게시합니다. 다른 클라이언트에 게시하려면 type
을 sendToGroup
으로 설정하고 메시지의 그룹 이름을 지정하기만 하면 됩니다.
stream
프로그램에 대한 또 다른 bash 창을 열고 websockets
종속성을 설치합니다.
mkdir stream
cd stream
# Create venv
python -m venv env
# Active venv
source ./env/bin/activate
pip install websockets
다음 내용으로 stream.py
를 만듭니다.
import asyncio
import sys
import threading
import time
import websockets
import requests
import json
async def connect(url):
async with websockets.connect(url, subprotocols=['json.webpubsub.azure.v1']) as ws:
print('connected')
id = 1
while True:
data = input()
payload = {
'type': 'sendToGroup',
'group': 'stream',
'dataType': 'text',
'data': str(data + '\n'),
'ackId': id
}
id = id + 1
await ws.send(json.dumps(payload))
await ws.recv()
if __name__ == '__main__':
res = requests.get('http://localhost:8080/negotiate').json()
try:
asyncio.get_event_loop().run_until_complete(connect(res['url']))
except KeyboardInterrupt:
pass
위의 코드는 서비스에 대한 WebSocket 연결을 만든 다음, 데이터를 수신할 때마다 ws.send()
를 사용하여 데이터를 게시합니다. 다른 클라이언트에 게시하려면 type
을 sendToGroup
으로 설정하고 메시지의 그룹 이름을 지정하기만 하면 됩니다.
다른 터미널을 사용하여 루트 폴더로 돌아가서 스트리밍 콘솔 앱 logstream-streaming
을 만들고 logstream-streaming 폴더로 전환해 보겠습니다.
mvn archetype:generate --define interactiveMode=n --define groupId=com.webpubsub.quickstart --define artifactId=logstream-streaming --define archetypeArtifactId=maven-archetype-quickstart --define archetypeVersion=1.4
cd logstream-streaming
HttpClient 종속성을 pom.xml
의 dependencies
노드에 추가해 보겠습니다.
<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.9</version>
</dependency>
이제 WebSocket을 사용하여 서비스에 연결해 보겠습니다. /src/main/java/com/webpubsub/quickstart 디렉터리로 이동하고, 편집기에서 App.java 파일을 열고, 코드를 아래 코드로 바꿉니다.
package com.webpubsub.quickstart;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.WebSocket;
import java.util.concurrent.CompletionStage;
import com.google.gson.Gson;
public class App
{
public static void main( String[] args ) throws IOException, InterruptedException
{
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://localhost:8080/negotiate"))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
Gson gson = new Gson();
String url = gson.fromJson(response.body(), Entity.class).url;
WebSocket ws = HttpClient.newHttpClient().newWebSocketBuilder().subprotocols("json.webpubsub.azure.v1")
.buildAsync(URI.create(url), new WebSocketClient()).join();
int id = 0;
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String streaming = reader.readLine();
App app = new App();
while (streaming != null && !streaming.isEmpty()){
String frame = gson.toJson(app.new GroupMessage(streaming + "\n", ++id));
System.out.println("Sending: " + frame);
ws.sendText(frame, true);
streaming = reader.readLine();
}
}
private class GroupMessage{
public String data;
public int ackId;
public final String type = "sendToGroup";
public final String group = "stream";
GroupMessage(String data, int ackId){
this.data = data;
this.ackId = ackId;
}
}
private static final class WebSocketClient implements WebSocket.Listener {
private WebSocketClient() {
}
@Override
public void onOpen(WebSocket webSocket) {
System.out.println("onOpen using subprotocol " + webSocket.getSubprotocol());
WebSocket.Listener.super.onOpen(webSocket);
}
@Override
public CompletionStage<?> onText(WebSocket webSocket, CharSequence data, boolean last) {
System.out.println("onText received " + data);
return WebSocket.Listener.super.onText(webSocket, data, last);
}
@Override
public void onError(WebSocket webSocket, Throwable error) {
System.out.println("Bad day! " + webSocket.toString());
WebSocket.Listener.super.onError(webSocket, error);
}
}
private static final class Entity {
public String url;
}
}
pom.xml 파일이 포함된 디렉터리로 이동해 아래 명령을 사용하여 프로젝트를 실행합니다.
mvn compile & mvn package & mvn exec:java -Dexec.mainClass="com.webpubsub.quickstart.App" -Dexec.cleanupDaemonThreads=false
여기서는 "그룹"이라는 새로운 개념을 볼 수 있습니다. 그룹은 연결 그룹에 메시지를 게시할 수 있는 허브의 논리적 개념입니다. 허브에 여러 그룹을 포함할 수 있으며 한 클라이언트가 동시에 여러 그룹을 구독할 수 있습니다. 하위 프로토콜을 사용하는 경우 전체 허브에 브로드캐스트하는 대신 한 그룹에만 게시할 수 있습니다. 용어에 대한 자세한 내용은 기본 개념 을 참조하세요.
여기서는 그룹을 사용하기 때문에 index.html
콜백 내에서 WebSocket 연결이 설정되면 그룹에 조인하도록 ws.onopen
웹 페이지를 업데이트해야 합니다.
let ackId = 0;
ws.onopen = () => {
console.log('connected');
ws.send(JSON.stringify({
type: 'joinGroup',
group: 'stream',
ackId: ++ackId
}));
};
클라이언트가 메시지를 joinGroup
형식으로 전송하여 그룹에 조인하는 것을 볼 수 있습니다.
또한 JSON 응답을 구문 분석하고 ws.onmessage
그룹의 메시지만 인쇄하도록 stream
콜백 논리를 살짝 업데이트합니다. 그러면 라이브 스트림 프린터로 작동합니다.
ws.onmessage = event => {
let message = JSON.parse(event.data);
if (message.type === 'message' && message.group === 'stream') {
let d = document.createElement('span');
d.innerText = message.data;
output.appendChild(d);
window.scrollTo(0, document.body.scrollHeight);
}
};
보안상의 이유로 클라이언트는 기본적으로 그룹 자체를 게시하거나 구독할 수 없습니다. 이러한 이유로 토큰을 생성할 때 roles
를 클라이언트로 설정했습니다.
Startup.cs
의 GenerateClientAccessUri
가 아래와 같으면 roles
를 설정합니다.
service.GenerateClientAccessUri(roles: new string[] { "webpubsub.sendToGroup.stream", "webpubsub.joinLeaveGroup.stream" })
server.js
의 getClientAccessToken
이 아래와 같으면 roles
를 추가합니다.
app.get('/negotiate', async (req, res) => {
let token = await service.getClientAccessToken({
roles: ['webpubsub.sendToGroup.stream', 'webpubsub.joinLeaveGroup.stream']
});
...
});
액세스 토큰 생성 프로세스 동안 server.py
에서 올바른 역할을 클라이언트에 설정합니다.
roles = ['webpubsub.sendToGroup.stream',
'webpubsub.joinLeaveGroup.stream']
token = service.get_client_access_token(roles=roles)
액세스 토큰 생성 프로세스 동안 App.java
에서 올바른 역할을 클라이언트에 설정합니다.
GetClientAccessTokenOptions option = new GetClientAccessTokenOptions();
option.addRole("webpubsub.sendToGroup.stream");
option.addRole("webpubsub.joinLeaveGroup.stream");
WebPubSubClientAccessToken token = service.getClientAccessToken(option);
마지막으로, 보기 좋게 표시되도록 index.html
에 스타일을 적용합니다.
<html>
<head>
<style>
#output {
white-space: pre;
font-family: monospace;
}
</style>
</head>
이제 아래 코드를 실행하고 텍스트를 입력하면 실시간으로 브라우저에 표시됩니다.
ls -R | dotnet run
# Or call `dir /s /b | dotnet run` when you are using CMD under Windows
또는 데이터가 실시간으로 브라우저에 스트리밍되는 것을 볼 수 있도록 속도를 낮출 수도 있습니다.
for i in $(ls -R); do echo $i; sleep 0.1; done | dotnet run
이 자습서의 전체 코드 샘플은 여기 서 찾을 수 있습니다.
node stream
또는 이 앱을 사용하여 다른 콘솔 앱의 출력을 파이핑하고 브라우저로 스트리밍할 수도 있습니다. 예시:
ls -R | node stream
# Or call `dir /s /b | node stream` when you are using CMD under Windows
또는 데이터가 실시간으로 브라우저에 스트리밍되는 것을 볼 수 있도록 속도를 낮출 수도 있습니다.
for i in $(ls -R); do echo $i; sleep 0.1; done | node stream
이 자습서의 전체 코드 샘플은 여기 서 찾을 수 있습니다.
이제 python stream.py
를 실행하고 텍스트를 입력하면 실시간으로 브라우저에 표시됩니다.
또는 이 앱을 사용하여 다른 콘솔 앱의 출력을 파이핑하고 브라우저로 스트리밍할 수도 있습니다. 예시:
ls -R | python stream.py
# Or call `dir /s /b | python stream.py` when you are using CMD under Windows
또는 데이터가 실시간으로 브라우저에 스트리밍되는 것을 볼 수 있도록 속도를 낮출 수도 있습니다.
for i in $(ls -R); do echo $i; sleep 0.1; done | python stream.py
이 자습서의 전체 코드 샘플은 여기 서 찾을 수 있습니다.
이제 아래 코드를 실행하고 텍스트를 입력하면 실시간으로 브라우저에 표시됩니다.
mvn compile & mvn package & mvn exec:java -Dexec.mainClass="com.webpubsub.quickstart.App" -Dexec.cleanupDaemonThreads=false
이 자습서의 전체 코드 샘플은 여기 서 찾을 수 있습니다.
다음 단계
이 자습서에서는 하위 프로토콜을 허용하여 Web PubSub 서비스에 연결하는 방법과 연결된 클라이언트에 메시지를 게시하는 방법에 대한 기본 개념을 알아보았습니다.
다른 자습서를 확인하여 서비스 사용 방법을 자세히 알아보세요.