.NET에서 SignalR 활용하기

서론

이전에는 ASP.NET의 긴 연결과 WebSocket에 대해 학습했습니다.

ASP.NET으로 긴 연결 구현 - chenxizhaolu - 블로그원 .NET에서 WebSocket 사용 사례 - chenxizhaolu - 블로그원

오늘은 SignalR을 학습하여 실시간 웹 기능三部曲을 마무리하겠습니다.

ASP.NET Core SignalR은 실시간 웹 기능을 애플리케이션에 추가하는 것을 단순화하는 오픈 소스 라이브러리입니다.실시간 웹 기능은 클라이언트가 주기적으로 서버를 폴링하여 새 데이터를 요청하는 대신, 서버 측 코드가 데이터를 사용 가능하게 되면 즉시 연결된 클라이언트에 콘텐츠를 푸시할 수 있게 합니다. SignalR은 채팅 애플리케이션, 실시간 대시보드, 게임 및 협업 도구 등과 같이 고빈도 데이터 업데이트가 필요한 시나리오에 특히 적합합니다.

간단한 채팅 대화상자

클라이언트가 메시지를 서버로 보내면 서버가 응답합니다.

서버 코드:

public class ChatHub : Hub
{
    public async Task TransmitMessage(string username, string content)
    {
        await Clients.All.SendAsync("ReceiveMessage", username, "서버가 클라이언트 메시지를 수신했습니다: " + content);
    }
    public override async Task OnConnectedAsync()
    {
        await Clients.All.SendAsync("ReceiveMessage", "시스템", $"{Context.ConnectionId}이(가) 채팅에 참여했습니다");
        await base.OnConnectedAsync();
    }
    public override async Task OnDisconnectedAsync(Exception exception)
    {
        await Clients.All.SendAsync("ReceiveMessage", "시스템", $"{Context.ConnectionId}이(가) 채팅을 나갔습니다");
        await base.OnDisconnectedAsync(exception);
    }
}
var appBuilder = WebApplication.CreateBuilder(args);
appBuilder.Services.AddCors(options =>
{
    options.AddPolicy("AllowAll", policy =>
    {
        policy.WithOrigins("http://localhost:5065") // 모든 출처 허용
              .AllowAnyMethod() // 모든 HTTP 메서드 허용
              .AllowAnyHeader() // 모든 요청 허용
              .AllowCredentials();

    });
});
appBuilder.Services.AddSignalR();
appBuilder.WebHost.UseUrls("http://localhost:5000");
var app = appBuilder.Build();
// CORS 미들웨어 활성화
app.UseCors("AllowAll");
app.UseRouting();
app.MapHub<ChatHub>("/chatHub");
app.Run();

클라이언트 코드:

var webBuilder = WebApplication.CreateBuilder(args);
var app = webBuilder.Build();
app.UseStaticFiles();
app.Run();
<!-- wwwroot/index.html -->

<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>SignalR 채팅</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <style>
        body {
            padding-top: 50px;
        }

        #messageContainer {
            height: 400px;
            overflow-y: scroll;
            border: 1px solid #ccc;
            padding: 10px;
            margin-bottom: 10px;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1 class="mt-5">SignalR 채팅</h1>
        <div id="messageContainer"></div>
        <input type="text" id="userNameInput" class="form-control mt-2" placeholder="이름을 입력하세요">
        <input type="text" id="messageInput" class="form-control mt-2" placeholder="메시지를 입력하세요...">
        <button id="transmitButton" class="btn btn-primary mt-2">전송</button>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/6.0.1/signalr.min.js"></script>
    <script>
        const hubConnection = new signalR.HubConnectionBuilder()
            .withUrl("http://localhost:5000/chatHub", { withCredentials: true }) // 자격 증명 전송 허용
            .configureLogging(signalR.LogLevel.Information)
            .build();

        // 연결 시작
        hubConnection.start().then(() => {
            console.log("SignalR 연결이 설정되었습니다");
        }).catch(err => console.error(err.toString()));

        // 메시지 수신
        hubConnection.on("ReceiveMessage", (user, message) => {
            const messageElement = document.createElement("div");
            messageElement.textContent = `${user}: ${message}`;
            document.getElementById("messageContainer").appendChild(messageElement);
            window.scrollTo(0, document.body.scrollHeight);
        });

        // 메시지 전송
        document.getElementById("transmitButton").addEventListener("click", async () => {
            const user = document.getElementById("userNameInput").value;
            const message = document.getElementById("messageInput").value;
            await hubConnection.invoke("TransmitMessage", user, message);
            document.getElementById("messageInput").value = "";
        });
    </script>
</body>
</html>

실시간 푸시 기능

public class BackgroundTask : BackgroundService
{
    private readonly ILogger<BackgroundTask> _logger;
    private readonly IHubContext<ChatHub> _hubContext;
    public BackgroundTask(ILogger<BackgroundTask> logger, IHubContext<ChatHub> hubContext)
    {
        _logger = logger;
        _hubContext = hubContext;
    }
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            _logger.LogInformation("백그라운드 작업 실행 시간: {time}", DateTimeOffset.Now);

            await _hubContext.Clients.All.SendAsync("OnlineUsers", "시스템", $"현재 연결된 사용자: {string.Join(',', ChatHub.ConnectedConnections.Keys)}");
            await Task.Delay(10000, stoppingToken);
        }
    }
}
builder.Services.AddHostedService<BackgroundTask>();

간단한 기능 확인

클라이언트가 서버에 메시지를 보내면 서버가 즉시 응답합니다. 특정 클라이언트에게 응답하려면 SendAsync의 첫 번째 매개변수인 methodName을 수정하기만 하면 됩니다.

서버가 주기적으로 모든 클라이언트에게 현재 온라인 상태를 푸시합니다

SignalR 콘솔 클라이언트 예제

소스 코드 주소: https://gitee.com/xiaoqingyao/keep-alive-http-demo.git

태그: SignalR asp.net-core 실시간-통신 websocket C#

6월 27일 21:29에 게시됨