Blazor와 함께 ASP.NET Core SignalR 사용

참고 항목

이 문서의 최신 버전은 아닙니다. 현재 릴리스는 이 문서의 ASP.NET Core 8.0 버전을 참조 하세요.

이 자습서에서는 .를 사용하여 SignalRBlazor실시간 앱을 빌드하기 위한 기본 작업 환경을 제공합니다. 이 문서는 이미 잘 알고 SignalR 있고 앱에서 Blazor 사용하는 SignalR 방법을 이해하려는 개발자에게 유용합니다. 및 Blazor 프레임워크에 대한 SignalR 자세한 지침은 다음 참조 설명서 집합 및 API 설명서를 참조하세요.

다음의 방법을 알아보세요.

  • Blazor 앱 만들기
  • SignalR 클라이언트 라이브러리 추가
  • SignalR 허브 추가
  • SignalR 서비스 및 SignalR 허브에 대한 엔드포인트 추가
  • 채팅을 Razor 위한 구성 요소 코드 추가

이 모든 과정을 마치면 채팅 앱을 실행할 수 있습니다.

필수 조건

Visual Studio 2022 이상ASP.NET 및 웹 개발 워크로드

샘플 앱

이 자습서에서는 자습서의 샘플 채팅 앱을 다운로드할 필요가 없습니다. 샘플 앱은 이 자습서의 단계를 따르면 최종적으로 생성되는 유효한 앱입니다.

샘플 코드 보기 및 다운로드(다운로드 방법)

Blazor 웹앱 만들기

선택한 도구의 지침을 따르세요.

참고 항목

Visual Studio 2022 이상 및 .NET Core SDK 8.0.0 이상이 필요합니다.

새 프로젝트를 만듭니다.

웹앱 템플릿을 Blazor 선택합니다. 다음을 선택합니다.

프로젝트 이름 필드에 BlazorSignalRApp을 입력합니다. 위치 항목이 올바른지 확인하거나 프로젝트의 위치를 제공합니다. 다음을 선택합니다.

프레임워크.NET 8 이상인지 확인합니다. 만들기를 실행합니다.

SignalR 클라이언트 라이브러리 추가

솔루션 탐색기에서 BlazorSignalRApp 프로젝트를 마우스 오른쪽 단추로 클릭하고 NuGet 패키지 관리를 선택합니다.

NuGet 패키지 관리 대화 상자에서 패키지 원본nuget.org로 설정되어 있는지 확인합니다.

찾아보기를 선택하고 검색 상자에 Microsoft.AspNetCore.SignalR.Client를 입력합니다.

검색 결과에서 패키지의 최신 릴리스를 Microsoft.AspNetCore.SignalR.Client 선택합니다. 설치를 선택합니다.

변경 내용 미리 보기 대화 상자가 표시되면 확인을 선택합니다.

라이선스 승인 대화 상자가 나타나면 사용 조건에 동의하는 경우 동의함을 선택합니다.

SignalR 허브 추가

(복수) 폴더를 Hubs 만들고 앱의 루트에 다음 ChatHub 클래스(Hubs/ChatHub.cs)를 추가합니다.

using Microsoft.AspNetCore.SignalR;

namespace BlazorSignalRApp.Hubs;

public class ChatHub : Hub
{
    public async Task SendMessage(string user, string message)
    {
        await Clients.All.SendAsync("ReceiveMessage", user, message);
    }
}

서비스 및 SignalR 허브에 대한 엔드포인트 추가

Program 파일을 엽니다.

Microsoft.AspNetCore.ResponseCompressionChatHub 클래스에 대한 네임스페이스를 파일 맨 위에 추가합니다.

using Microsoft.AspNetCore.ResponseCompression;
using BlazorSignalRApp.Hubs;

응답 압축 미들웨어 서비스 추가:

builder.Services.AddResponseCompression(opts =>
{
   opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
         new[] { "application/octet-stream" });
});

처리 파이프라인 구성의 맨 위에 있는 응답 압축 미들웨어를 사용합니다.

app.UseResponseCompression();

구성 요소를 매핑 Razor 하는 줄 바로 뒤 허브에 대한 엔드포인트를 추가합니다(app.MapRazorComponents<T>()).

app.MapHub<ChatHub>("/chathub");

채팅을 위한 Razor 구성 요소 코드 추가

Components/Pages/Home.razor 파일을 엽니다.

태그를 다음 코드로 바꿉니다.

@page "/"
@rendermode InteractiveServer
@using Microsoft.AspNetCore.SignalR.Client
@inject NavigationManager Navigation
@implements IAsyncDisposable

<PageTitle>Home</PageTitle>

<div class="form-group">
    <label>
        User:
        <input @bind="userInput" />
    </label>
</div>
<div class="form-group">
    <label>
        Message:
        <input @bind="messageInput" size="50" />
    </label>
</div>
<button @onclick="Send" disabled="@(!IsConnected)">Send</button>

<hr>

<ul id="messagesList">
    @foreach (var message in messages)
    {
        <li>@message</li>
    }
</ul>

@code {
    private HubConnection? hubConnection;
    private List<string> messages = new List<string>();
    private string? userInput;
    private string? messageInput;

    protected override async Task OnInitializedAsync()
    {
        hubConnection = new HubConnectionBuilder()
            .WithUrl(Navigation.ToAbsoluteUri("/chathub"))
            .Build();

        hubConnection.On<string, string>("ReceiveMessage", (user, message) =>
        {
            var encodedMsg = $"{user}: {message}";
            messages.Add(encodedMsg);
            InvokeAsync(StateHasChanged);
        });

        await hubConnection.StartAsync();
    }

    private async Task Send()
    {
        if (hubConnection is not null)
        {
            await hubConnection.SendAsync("SendMessage", userInput, messageInput);
        }
    }

    public bool IsConnected =>
        hubConnection?.State == HubConnectionState.Connected;

    public async ValueTask DisposeAsync()
    {
        if (hubConnection is not null)
        {
            await hubConnection.DisposeAsync();
        }
    }
}

참고 항목

핫 다시 로드를 사용하는 경우 Development 환경에서 응답 압축 미들웨어를 사용하지 않도록 설정합니다. 자세한 내용은 ASP.NET Core BlazorSignalR 지침을 참조하세요.

앱 실행

선택한 도구의 지침을 따르세요.

F5 키를 눌러 디버깅이 설정된 상태로 앱을 실행하거나 Ctrl+F5(Windows)/+F5(macOS)를 눌러 디버깅 없이 앱을 실행합니다.

주소 표시줄에서 URL을 복사하고, 다른 브라우저 인스턴스 또는 탭을 열고, 주소 표시줄에 URL을 붙여넣습니다.

브라우저 중 하나를 선택하고, 이름 및 메시지를 입력하고, 메시지를 보내는 단추를 선택합니다. 이름과 메시지는 두 페이지 모두에 즉시 표시됩니다.

SignalRBlazor sample app open in two browser windows showing exchanged messages.

인용: Star Trek VI: The Undiscovered Country ©1991 Paramount

호스트된 Blazor WebAssembly 환경

앱 만들기

선택한 도구에 대한 지침에 따라 호스트 Blazor WebAssembly 된 앱을 만듭니다.

참고 항목

Visual Studio 2022 이상 및 .NET Core SDK 6.0.0 이상이 필요합니다.

새 프로젝트를 만듭니다.

Blazor WebAssembly 앱 템플릿을 선택합니다. 다음을 선택합니다.

프로젝트 이름 필드에 BlazorWebAssemblySignalRApp을 입력합니다. 위치 항목이 올바른지 확인하거나 프로젝트의 위치를 제공합니다. 다음을 선택합니다.

추가 정보 대화 상자에서 ASP.NET Core 호스트됨 확인란을 선택합니다.

만들기를 실행합니다.

다음과 같은 방법으로 호스트된 Blazor WebAssembly 앱이 생성되었는지 확인합니다. 솔루션 탐색기에서 Client 프로젝트와 Server 프로젝트가 있는지 확인합니다. 이 두 프로젝트가 없으면 처음부터 다시 시작하고 만들기를 선택하기 전에 ASP.NET Core 호스트됨 확인란이 선택되었는지 확인합니다.

SignalR 클라이언트 라이브러리 추가

솔루션 탐색기에서 BlazorWebAssemblySignalRApp.Client 프로젝트를 마우스 오른쪽 단추로 클릭하고 NuGet 패키지 관리를 선택합니다.

NuGet 패키지 관리 대화 상자에서 패키지 원본nuget.org로 설정되어 있는지 확인합니다.

찾아보기를 선택하고 검색 상자에 Microsoft.AspNetCore.SignalR.Client를 입력합니다.

검색 결과에서 Microsoft.AspNetCore.SignalR.Client 패키지를 선택합니다. 앱의 공유 프레임워크와 일치하도록 버전을 설정합니다. 설치를 선택합니다.

변경 내용 미리 보기 대화 상자가 표시되면 확인을 선택합니다.

라이선스 승인 대화 상자가 나타나면 사용 조건에 동의하는 경우 동의함을 선택합니다.

SignalR 허브 추가

BlazorWebAssemblySignalRApp.Server 프로젝트에서 Hubs(복수형) 폴더를 만들고 다음 ChatHub 클래스(Hubs/ChatHub.cs)를 추가합니다.

using Microsoft.AspNetCore.SignalR;

namespace BlazorWebAssemblySignalRApp.Server.Hubs;

public class ChatHub : Hub
{
    public async Task SendMessage(string user, string message)
    {
        await Clients.All.SendAsync("ReceiveMessage", user, message);
    }
}
using Microsoft.AspNetCore.SignalR;

namespace BlazorWebAssemblySignalRApp.Server.Hubs;

public class ChatHub : Hub
{
    public async Task SendMessage(string user, string message)
    {
        await Clients.All.SendAsync("ReceiveMessage", user, message);
    }
}
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;

namespace BlazorWebAssemblySignalRApp.Server.Hubs
{
    public class ChatHub : Hub
    {
        public async Task SendMessage(string user, string message)
        {
            await Clients.All.SendAsync("ReceiveMessage", user, message);
        }
    }
}
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;

namespace BlazorWebAssemblySignalRApp.Server.Hubs
{
    public class ChatHub : Hub
    {
        public async Task SendMessage(string user, string message)
        {
            await Clients.All.SendAsync("ReceiveMessage", user, message);
        }
    }
}

서비스 및 SignalR 허브에 대한 엔드포인트 추가

BlazorWebAssemblySignalRApp.Server 프로젝트에서 파일을 엽니다Program.cs.

ChatHub 클래스에 대한 네임스페이스를 파일의 맨 위에 추가합니다.

using BlazorWebAssemblySignalRApp.Server.Hubs;

추가 SignalR 및 응답 압축 미들웨어 서비스:

builder.Services.AddSignalR();
builder.Services.AddResponseCompression(opts =>
{
      opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
         new[] { "application/octet-stream" });
});

앱을 빌드하는 줄 바로 뒤 처리 파이프라인 구성의 맨 위에 있는 응답 압축 미들웨어를 사용합니다.

app.UseResponseCompression();

컨트롤러와 클라이언트 쪽 대체에 대한 엔드포인트 사이에 허브에 대한 엔드포인트를 추가합니다. 줄 app.MapControllers();바로 다음에 다음 줄을 추가합니다.

app.MapHub<ChatHub>("/chathub");

BlazorWebAssemblySignalRApp.Server 프로젝트에서 파일을 엽니다Startup.cs.

ChatHub 클래스에 대한 네임스페이스를 파일의 맨 위에 추가합니다.

using BlazorWebAssemblySignalRApp.Server.Hubs;

추가 SignalR 및 응답 압축 미들웨어 서비스:

services.AddSignalR();
services.AddResponseCompression(opts =>
{
      opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
         new[] { "application/octet-stream" });
});

처리 파이프라인 구성의 맨 위에 있는 응답 압축 미들웨어를 사용합니다.

app.UseResponseCompression();

컨트롤러의 엔드포인트와 클라이언트 쪽 대체(fallback) 사이에 다음 줄 endpoints.MapControllers();바로 뒤 허브에 대한 엔드포인트를 추가합니다.

endpoints.MapHub<ChatHub>("/chathub");

채팅을 위한 Razor 구성 요소 코드 추가

BlazorWebAssemblySignalRApp.Client 프로젝트에서 파일을 엽니다Pages/Index.razor.

태그를 다음 코드로 바꿉니다.

@page "/"
@using Microsoft.AspNetCore.SignalR.Client
@inject NavigationManager Navigation
@implements IAsyncDisposable

<PageTitle>Index</PageTitle>

<div class="form-group">
    <label>
        User:
        <input @bind="userInput" />
    </label>
</div>
<div class="form-group">
    <label>
        Message:
        <input @bind="messageInput" size="50" />
    </label>
</div>
<button @onclick="Send" disabled="@(!IsConnected)">Send</button>

<hr>

<ul id="messagesList">
    @foreach (var message in messages)
    {
        <li>@message</li>
    }
</ul>

@code {
    private HubConnection? hubConnection;
    private List<string> messages = new List<string>();
    private string? userInput;
    private string? messageInput;

    protected override async Task OnInitializedAsync()
    {
        hubConnection = new HubConnectionBuilder()
            .WithUrl(Navigation.ToAbsoluteUri("/chathub"))
            .Build();

        hubConnection.On<string, string>("ReceiveMessage", (user, message) =>
        {
            var encodedMsg = $"{user}: {message}";
            messages.Add(encodedMsg);
            StateHasChanged();
        });

        await hubConnection.StartAsync();
    }

    private async Task Send()
    {
        if (hubConnection is not null)
            {
                await hubConnection.SendAsync("SendMessage", userInput, messageInput);
            }
    }

    public bool IsConnected =>
        hubConnection?.State == HubConnectionState.Connected;

    public async ValueTask DisposeAsync()
    {
        if (hubConnection is not null)
        {
            await hubConnection.DisposeAsync();
        }
    }
}
@page "/"
@using Microsoft.AspNetCore.SignalR.Client
@inject NavigationManager Navigation
@implements IAsyncDisposable

<PageTitle>Index</PageTitle>

<div class="form-group">
    <label>
        User:
        <input @bind="userInput" />
    </label>
</div>
<div class="form-group">
    <label>
        Message:
        <input @bind="messageInput" size="50" />
    </label>
</div>
<button @onclick="Send" disabled="@(!IsConnected)">Send</button>

<hr>

<ul id="messagesList">
    @foreach (var message in messages)
    {
        <li>@message</li>
    }
</ul>

@code {
    private HubConnection? hubConnection;
    private List<string> messages = new List<string>();
    private string? userInput;
    private string? messageInput;

    protected override async Task OnInitializedAsync()
    {
        hubConnection = new HubConnectionBuilder()
            .WithUrl(Navigation.ToAbsoluteUri("/chathub"))
            .Build();

        hubConnection.On<string, string>("ReceiveMessage", (user, message) =>
        {
            var encodedMsg = $"{user}: {message}";
            messages.Add(encodedMsg);
            StateHasChanged();
        });

        await hubConnection.StartAsync();
    }

    private async Task Send()
    {
        if (hubConnection is not null)
            {
                await hubConnection.SendAsync("SendMessage", userInput, messageInput);
            }
    }

    public bool IsConnected =>
        hubConnection?.State == HubConnectionState.Connected;

    public async ValueTask DisposeAsync()
    {
        if (hubConnection is not null)
        {
            await hubConnection.DisposeAsync();
        }
    }
}
@page "/"
@using Microsoft.AspNetCore.SignalR.Client
@inject NavigationManager NavigationManager
@implements IAsyncDisposable

<div class="form-group">
    <label>
        User:
        <input @bind="userInput" />
    </label>
</div>
<div class="form-group">
    <label>
        Message:
        <input @bind="messageInput" size="50" />
    </label>
</div>
<button @onclick="Send" disabled="@(!IsConnected)">Send</button>

<hr>

<ul id="messagesList">
    @foreach (var message in messages)
    {
        <li>@message</li>
    }
</ul>

@code {
    private HubConnection hubConnection;
    private List<string> messages = new List<string>();
    private string userInput;
    private string messageInput;

    protected override async Task OnInitializedAsync()
    {
        hubConnection = new HubConnectionBuilder()
            .WithUrl(NavigationManager.ToAbsoluteUri("/chathub"))
            .Build();

        hubConnection.On<string, string>("ReceiveMessage", (user, message) =>
        {
            var encodedMsg = $"{user}: {message}";
            messages.Add(encodedMsg);
            StateHasChanged();
        });

        await hubConnection.StartAsync();
    }

    async Task Send() =>
        await hubConnection.SendAsync("SendMessage", userInput, messageInput);

    public bool IsConnected =>
        hubConnection.State == HubConnectionState.Connected;

    public async ValueTask DisposeAsync()
    {
        if (hubConnection is not null)
        {
            await hubConnection.DisposeAsync();
        }
    }
}
@page "/"
@using Microsoft.AspNetCore.SignalR.Client
@inject NavigationManager NavigationManager
@implements IDisposable

<div class="form-group">
    <label>
        User:
        <input @bind="userInput" />
    </label>
</div>
<div class="form-group">
    <label>
        Message:
        <input @bind="messageInput" size="50" />
    </label>
</div>
<button @onclick="Send" disabled="@(!IsConnected)">Send</button>

<hr>

<ul id="messagesList">
    @foreach (var message in messages)
    {
        <li>@message</li>
    }
</ul>

@code {
    private HubConnection hubConnection;
    private List<string> messages = new List<string>();
    private string userInput;
    private string messageInput;

    protected override async Task OnInitializedAsync()
    {
        hubConnection = new HubConnectionBuilder()
            .WithUrl(NavigationManager.ToAbsoluteUri("/chathub"))
            .Build();

        hubConnection.On<string, string>("ReceiveMessage", (user, message) =>
        {
            var encodedMsg = $"{user}: {message}";
            messages.Add(encodedMsg);
            StateHasChanged();
        });

        await hubConnection.StartAsync();
    }

    async Task Send() =>
        await hubConnection.SendAsync("SendMessage", userInput, messageInput);

    public bool IsConnected =>
        hubConnection.State == HubConnectionState.Connected;

    public void Dispose()
    {
        _ = hubConnection?.DisposeAsync();
    }
}

참고 항목

핫 다시 로드를 사용하는 경우 Development 환경에서 응답 압축 미들웨어를 사용하지 않도록 설정합니다. 자세한 내용은 ASP.NET Core BlazorSignalR 지침을 참조하세요.

앱 실행

선택한 도구의 지침을 따르세요.

솔루션 탐색기에서 BlazorWebAssemblySignalRApp.Server 프로젝트를 선택합니다. F5 키를 눌러 디버깅이 설정된 상태로 앱을 실행하거나 Ctrl+F5(Windows)/+F5(macOS)를 눌러 디버깅 없이 앱을 실행합니다.

Important

호스트된 Blazor WebAssembly 앱을 실행하는 경우 솔루션Server 프로젝트에서 앱을 실행합니다.

디버깅 세션용으로 Google Chrome 또는 Microsoft Edge 브라우저를 선택해야 합니다.

브라우저에서 앱을 시작하지 못하는 경우:

  • .NET 콘솔에서 솔루션이 "Server" 프로젝트에서 실행 중인지 확인합니다.
  • 브라우저의 다시 로드 단추를 사용하여 브라우저를 새로 고칩니다.

주소 표시줄에서 URL을 복사하고, 다른 브라우저 인스턴스 또는 탭을 열고, 주소 표시줄에 URL을 붙여넣습니다.

브라우저 중 하나를 선택하고, 이름 및 메시지를 입력하고, 메시지를 보내는 단추를 선택합니다. 이름과 메시지는 두 페이지 모두에 즉시 표시됩니다.

SignalRBlazor sample app open in two browser windows showing exchanged messages.

인용: Star Trek VI: The Undiscovered Country ©1991 Paramount

Blazor Server 환경

앱 만들기

선택한 도구에 대한 지침에 따라 앱을 만듭니다.Blazor Server

참고 항목

Visual Studio 2022 이상 및 .NET Core SDK 6.0.0 이상이 필요합니다.

새 프로젝트를 만듭니다.

Blazor Server 앱 템플릿을 선택합니다. 다음을 선택합니다.

프로젝트 이름 필드에 BlazorServerSignalRApp을 입력합니다. 위치 항목이 올바른지 확인하거나 프로젝트의 위치를 제공합니다. 다음을 선택합니다.

만들기를 실행합니다.

SignalR 클라이언트 라이브러리 추가

솔루션 탐색기에서 BlazorServerSignalRApp 프로젝트를 마우스 오른쪽 단추로 클릭하고 NuGet 패키지 관리를 선택합니다.

NuGet 패키지 관리 대화 상자에서 패키지 원본nuget.org로 설정되어 있는지 확인합니다.

찾아보기를 선택하고 검색 상자에 Microsoft.AspNetCore.SignalR.Client를 입력합니다.

검색 결과에서 Microsoft.AspNetCore.SignalR.Client 패키지를 선택합니다. 앱의 공유 프레임워크와 일치하도록 버전을 설정합니다. 설치를 선택합니다.

변경 내용 미리 보기 대화 상자가 표시되면 확인을 선택합니다.

라이선스 승인 대화 상자가 나타나면 사용 조건에 동의하는 경우 동의함을 선택합니다.

System.Text.Encodings.Web 패키지 추가

‘이 섹션은 ASP.NET Core 버전 3.x용 앱에만 적용됩니다.’

ASP.NET Core 3.x 앱에서 System.Text.Json 5.x를 사용하는 경우 패키지 확인 문제로 인해 프로젝트에는 System.Text.Encodings.Web에 대한 패키지 참조가 필요합니다. 기본 문제는 패치 릴리스에서 해결되었으며 ASP.NET Core 5.0으로 백포트되었습니다. 자세한 내용은 System.Text.Json defines netcoreapp3.0 with no dependencies (dotnet/runtime #45560)를 참조하세요.

프로젝트에 System.Text.Encodings.Web을 추가하려면 선택한 도구에 대한 지침을 따르세요.

솔루션 탐색기에서 BlazorServerSignalRApp 프로젝트를 마우스 오른쪽 단추로 클릭하고 NuGet 패키지 관리를 선택합니다.

NuGet 패키지 관리 대화 상자에서 패키지 원본nuget.org로 설정되어 있는지 확인합니다.

찾아보기를 선택하고 검색 상자에 System.Text.Encodings.Web를 입력합니다.

검색 결과에서 System.Text.Encodings.Web 패키지를 선택합니다. 사용 중인 공유 프레임워크와 일치하는 패키지의 버전을 선택합니다. 설치를 선택합니다.

변경 내용 미리 보기 대화 상자가 표시되면 확인을 선택합니다.

라이선스 승인 대화 상자가 나타나면 사용 조건에 동의하는 경우 동의함을 선택합니다.

SignalR 허브 추가

Hubs(복수형) 폴더를 만들고 다음 ChatHub 클래스(Hubs/ChatHub.cs)를 추가합니다.

using Microsoft.AspNetCore.SignalR;

namespace BlazorServerSignalRApp.Server.Hubs;

public class ChatHub : Hub
{
    public async Task SendMessage(string user, string message)
    {
        await Clients.All.SendAsync("ReceiveMessage", user, message);
    }
}
using Microsoft.AspNetCore.SignalR;

namespace BlazorServerSignalRApp.Server.Hubs;

public class ChatHub : Hub
{
    public async Task SendMessage(string user, string message)
    {
        await Clients.All.SendAsync("ReceiveMessage", user, message);
    }
}
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;

namespace BlazorServerSignalRApp.Server.Hubs
{
    public class ChatHub : Hub
    {
        public async Task SendMessage(string user, string message)
        {
            await Clients.All.SendAsync("ReceiveMessage", user, message);
        }
    }
}
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;

namespace BlazorServerSignalRApp.Server.Hubs
{
    public class ChatHub : Hub
    {
        public async Task SendMessage(string user, string message)
        {
            await Clients.All.SendAsync("ReceiveMessage", user, message);
        }
    }
}

서비스 및 SignalR 허브에 대한 엔드포인트 추가

Program.cs 파일을 엽니다.

Microsoft.AspNetCore.ResponseCompressionChatHub 클래스에 대한 네임스페이스를 파일 맨 위에 추가합니다.

using Microsoft.AspNetCore.ResponseCompression;
using BlazorServerSignalRApp.Server.Hubs;

응답 압축 미들웨어 서비스 추가:

builder.Services.AddResponseCompression(opts =>
{
   opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
         new[] { "application/octet-stream" });
});

처리 파이프라인 구성의 맨 위에 있는 응답 압축 미들웨어를 사용합니다.

app.UseResponseCompression();

허브를 매핑 Blazor 하기 위한 엔드포인트와 클라이언트 쪽 대체(fallback) 사이에 다음 줄 app.MapBlazorHub();바로 뒤 허브에 대한 엔드포인트를 추가합니다.

app.MapHub<ChatHub>("/chathub");

Startup.cs 파일을 엽니다.

Microsoft.AspNetCore.ResponseCompressionChatHub 클래스에 대한 네임스페이스를 파일 맨 위에 추가합니다.

using Microsoft.AspNetCore.ResponseCompression;
using BlazorServerSignalRApp.Server.Hubs;

응답 압축 미들웨어 서비스 추가:

services.AddResponseCompression(opts =>
{
   opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
         new[] { "application/octet-stream" });
});

처리 파이프라인 구성의 맨 위에 있는 응답 압축 미들웨어를 사용합니다.

app.UseResponseCompression();

허브를 매핑 Blazor 하기 위한 엔드포인트와 클라이언트 쪽 대체(fallback) 사이에 다음 줄 endpoints.MapBlazorHub();바로 뒤 허브에 대한 엔드포인트를 추가합니다.

endpoints.MapHub<ChatHub>("/chathub");

채팅을 위한 Razor 구성 요소 코드 추가

Pages/Index.razor 파일을 엽니다.

태그를 다음 코드로 바꿉니다.

@page "/"
@using Microsoft.AspNetCore.SignalR.Client
@inject NavigationManager Navigation
@implements IAsyncDisposable

<PageTitle>Index</PageTitle>

<div class="form-group">
    <label>
        User:
        <input @bind="userInput" />
    </label>
</div>
<div class="form-group">
    <label>
        Message:
        <input @bind="messageInput" size="50" />
    </label>
</div>
<button @onclick="Send" disabled="@(!IsConnected)">Send</button>

<hr>

<ul id="messagesList">
    @foreach (var message in messages)
    {
        <li>@message</li>
    }
</ul>

@code {
    private HubConnection? hubConnection;
    private List<string> messages = new List<string>();
    private string? userInput;
    private string? messageInput;

    protected override async Task OnInitializedAsync()
    {
        hubConnection = new HubConnectionBuilder()
            .WithUrl(Navigation.ToAbsoluteUri("/chathub"))
            .Build();

        hubConnection.On<string, string>("ReceiveMessage", (user, message) =>
        {
            var encodedMsg = $"{user}: {message}";
            messages.Add(encodedMsg);
            InvokeAsync(StateHasChanged);
        });

        await hubConnection.StartAsync();
    }

    private async Task Send()
    {
        if (hubConnection is not null)
            {
                await hubConnection.SendAsync("SendMessage", userInput, messageInput);
            }
    }

    public bool IsConnected =>
        hubConnection?.State == HubConnectionState.Connected;

    public async ValueTask DisposeAsync()
    {
        if (hubConnection is not null)
        {
            await hubConnection.DisposeAsync();
        }
    }
}
@page "/"
@using Microsoft.AspNetCore.SignalR.Client
@inject NavigationManager Navigation
@implements IAsyncDisposable

<PageTitle>Index</PageTitle>

<div class="form-group">
    <label>
        User:
        <input @bind="userInput" />
    </label>
</div>
<div class="form-group">
    <label>
        Message:
        <input @bind="messageInput" size="50" />
    </label>
</div>
<button @onclick="Send" disabled="@(!IsConnected)">Send</button>

<hr>

<ul id="messagesList">
    @foreach (var message in messages)
    {
        <li>@message</li>
    }
</ul>

@code {
    private HubConnection? hubConnection;
    private List<string> messages = new List<string>();
    private string? userInput;
    private string? messageInput;

    protected override async Task OnInitializedAsync()
    {
        hubConnection = new HubConnectionBuilder()
            .WithUrl(Navigation.ToAbsoluteUri("/chathub"))
            .Build();

        hubConnection.On<string, string>("ReceiveMessage", (user, message) =>
        {
            var encodedMsg = $"{user}: {message}";
            messages.Add(encodedMsg);
            InvokeAsync(StateHasChanged);
        });

        await hubConnection.StartAsync();
    }

    private async Task Send()
    {
        if (hubConnection is not null)
            {
                await hubConnection.SendAsync("SendMessage", userInput, messageInput);
            }
    }

    public bool IsConnected =>
        hubConnection?.State == HubConnectionState.Connected;

    public async ValueTask DisposeAsync()
    {
        if (hubConnection is not null)
        {
            await hubConnection.DisposeAsync();
        }
    }
}
@page "/"
@using Microsoft.AspNetCore.SignalR.Client
@inject NavigationManager NavigationManager
@implements IAsyncDisposable

<div class="form-group">
    <label>
        User:
        <input @bind="userInput" />
    </label>
</div>
<div class="form-group">
    <label>
        Message:
        <input @bind="messageInput" size="50" />
    </label>
</div>
<button @onclick="Send" disabled="@(!IsConnected)">Send</button>

<hr>

<ul id="messagesList">
    @foreach (var message in messages)
    {
        <li>@message</li>
    }
</ul>

@code {
    private HubConnection hubConnection;
    private List<string> messages = new List<string>();
    private string userInput;
    private string messageInput;

    protected override async Task OnInitializedAsync()
    {
        hubConnection = new HubConnectionBuilder()
            .WithUrl(NavigationManager.ToAbsoluteUri("/chathub"))
            .Build();

        hubConnection.On<string, string>("ReceiveMessage", (user, message) =>
        {
            var encodedMsg = $"{user}: {message}";
            messages.Add(encodedMsg);
            InvokeAsync(StateHasChanged);
        });

        await hubConnection.StartAsync();
    }

    async Task Send() =>
        await hubConnection.SendAsync("SendMessage", userInput, messageInput);

    public bool IsConnected =>
        hubConnection.State == HubConnectionState.Connected;

    public async ValueTask DisposeAsync()
    {
        if (hubConnection is not null)
        {
            await hubConnection.DisposeAsync();
        }
    }
}
@page "/"
@using Microsoft.AspNetCore.SignalR.Client
@inject NavigationManager NavigationManager
@implements IAsyncDisposable

<div class="form-group">
    <label>
        User:
        <input @bind="userInput" />
    </label>
</div>
<div class="form-group">
    <label>
        Message:
        <input @bind="messageInput" size="50" />
    </label>
</div>
<button @onclick="Send" disabled="@(!IsConnected)">Send</button>

<hr>

<ul id="messagesList">
    @foreach (var message in messages)
    {
        <li>@message</li>
    }
</ul>

@code {
    private HubConnection hubConnection;
    private List<string> messages = new List<string>();
    private string userInput;
    private string messageInput;

    protected override async Task OnInitializedAsync()
    {
        hubConnection = new HubConnectionBuilder()
            .WithUrl(NavigationManager.ToAbsoluteUri("/chathub"))
            .Build();

        hubConnection.On<string, string>("ReceiveMessage", (user, message) =>
        {
            var encodedMsg = $"{user}: {message}";
            messages.Add(encodedMsg);
            InvokeAsync(StateHasChanged);
        });

        await hubConnection.StartAsync();
    }

    async Task Send() =>
        await hubConnection.SendAsync("SendMessage", userInput, messageInput);

    public bool IsConnected =>
        hubConnection.State == HubConnectionState.Connected;

    public async ValueTask DisposeAsync()
    {
        await hubConnection?.DisposeAsync();
    }
}

참고 항목

핫 다시 로드를 사용하는 경우 Development 환경에서 응답 압축 미들웨어를 사용하지 않도록 설정합니다. 자세한 내용은 ASP.NET Core BlazorSignalR 지침을 참조하세요.

앱 실행

선택한 도구의 지침을 따르세요.

F5 키를 눌러 디버깅이 설정된 상태로 앱을 실행하거나 Ctrl+F5(Windows)/+F5(macOS)를 눌러 디버깅 없이 앱을 실행합니다.

주소 표시줄에서 URL을 복사하고, 다른 브라우저 인스턴스 또는 탭을 열고, 주소 표시줄에 URL을 붙여넣습니다.

브라우저 중 하나를 선택하고, 이름 및 메시지를 입력하고, 메시지를 보내는 단추를 선택합니다. 이름과 메시지는 두 페이지 모두에 즉시 표시됩니다.

SignalRBlazor sample app open in two browser windows showing exchanged messages.

인용: Star Trek VI: The Undiscovered Country ©1991 Paramount

다음 단계

이 자습서에서는 다음 작업 방법을 알아보았습니다.

  • Blazor 앱 만들기
  • SignalR 클라이언트 라이브러리 추가
  • SignalR 허브 추가
  • SignalR 서비스 및 SignalR 허브에 대한 엔드포인트 추가
  • 채팅을 Razor 위한 구성 요소 코드 추가

및 Blazor 프레임워크에 대한 SignalR 자세한 지침은 다음 참조 설명서 집합을 참조하세요.

추가 리소스