-
devfest 2023 - signalr_netcore발표/후속 자료 2023. 12. 30. 00:04
https://github.com/atawLee/devfest2023.git
관련소스는 여기서 확인 가능합니다.
SignalR
SignalR은 실시간 웹 통신기능을 사용하기 편하게 만든 라이브러리 입니다.
기본은 WebSocket방식이지만 Long Polling등의 방식도 지원합니다.
SignalR은 닷넷기반 라이브러리이므로 사용하려면 닷넷 서버여야 합니다.서버측은 닷넷에 종속되어있지만 Client로는 닷넷뿐만 아니라 js,python,dart등을 지원하는 라이브러리들이 많습니다.
이번 devfest에서 소스에서 signalR에 대한 내용을 약간 넣었는데 해당 부분에 대한 질문을 하신분이 계셔 포스트를 작성하게 되었습니다.이포스트에서는 SignalR 서버를 구축하는 방법을 살짝 살펴보고
플러터 signalr_netcore에 대한 내용을 보도록 하겠습니다.SignalR 서버를 구축하는 내용은 아주 간단합니다.
서버는 닷넷에 종속되어있으므로 닷넷관련 개발도구를 설치해야합니다.SignalR .NET Server
그리고 webapi 프로젝트를 생성합니다.
dotnet new webapi -n server
기본적으로 weather관련된 샘플소스가 program.cs에 담겨있습니다.
다음은 signalR 패키지를 설치합니다.
dotnet add package Microsoft.AspNetCore.SignalR.Common --version 8.0.0
뒤에 나온 버전은 사용하시는 닷넷버전에 맞춰서 사용하시면 됩니다.
그리고 메세지를 처리할 클래스를 작성합니다.using Microsoft.AspNetCore.SignalR; namespace ChatServer; public class ChatHub : Hub { public async Task SendMessage(string user, string message) { await Clients.All.SendAsync("ReceiveMessage", user, message); } }
ChatHub에 SendMessage를 호출받으면 받은내용을 "RceiveMessage"를 구독하고 있는 사람들에게 보냅니다.
원활한 확인을 위해 Url을 http://localhost:5000으로 고정 하고,(vscode에서 f5로 실행시 launchsettings.json 기준 포트로 실행될 수 있습니다. 브라우저에서 직접 고정된 5000 포트로 변경해주세요. )
그리고 Program.cs에서 SignalR을 사용할수 있도록 AddSwaggerGen 밑에 AddSignalR()을 추가해서 주입합니다.using ChatServer; var builder = WebApplication.CreateBuilder(args); builder.WebHost.UseUrls("http://localhost:5000"); //고정 포트 추가 코드 // Add services to the container. // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.AddSignalR(); //SignalR 추가 코드 var app = builder.Build(); // 하단생략
그리고 엔드포인트를 설정해줍니다.
//상단 생략 app.MapHub<ChatHub>("/Chat"); app.Run(); //하단 생략
간단한 채팅 서버 셋팅이 완료 되었습니다.
SignalR Flutter Client
플러터 프로젝트를 만들고
signalr_netcore를 설치해주세요flutter pub add signalr_netcore
이전 발표에서는 signalr_core를 사용했지만, 현재는 pubdev를 확인해보니 netcore가 더 최신버전이 올라와 해당버전을 사용했습니다.
signalr_core는 다음과 같은 프로토콜을 지원합니다.
signalr_netcore 전송 프로토콜
- WebSocket
- Service Side Events
- Long Polling
데이터 프로토콜
- json
- MessagePack
모두 signalR Server와 호환되는 프로토콜입니다.
별도의 설정을 하지않았을때 사용되는 방식은 WebSocket과 송수신하고, 데이터는 json으로 포맷팅 됩니다.아래 git repository에 코드가 모두 공개되어있고 어떻게 사용하는지 testapp 디렉토리에 자세하게 나와있습니다.
sefidgaran/signalr_client: A Flutter SignalR Client for ASP.NET Core (github.com)
[GitHub - sefidgaran/signalr_client: A Flutter SignalR Client for ASP.NET Core
A Flutter SignalR Client for ASP.NET Core. Contribute to sefidgaran/signalr_client development by creating an account on GitHub.
github.com](https://github.com/sefidgaran/signalr_client)
HubConnectionBuilder
허브커넥션 빌더는 허브 커넥션에대한 설정을 빌더패턴으로 생성할수있게 해줍니다.
hubConnection = HubConnectionBuilder() .withUrl("$url/Chat") .withAutomaticReconnect() .build();
가장 기본적인 설정은 withUrl을 설정하고 그냥 build하는 것 입니다.
위의 설정에서는 withAutomaticReconnect() 설정이 있는데 연결이 끊길시 자동으로 재연결 시도를 설정할 수 있습니다.그외에 기능으로는 logging 설정을 지정할수있는 configureLogging(Logger logger)
데이터 프로토콜 방식을 지정할 수 있는 withHubProtocol(IHubProtocol protocol) 옵션들이 있으니 필요한 옵션들을 설정하고 build 하면
HubConnection 클래스의 인스턴스가 반환됩니다.HubConnection
HubConnection Method
invoke(String methodName, {List? args}) → Future<Object?>
send(String methodName, {List? args}) → Futureinvoke와 send 모두 signalr서버에 명령을 보내는 메서드 입니다.
두 메서드의 다른점은 invoke는 반환타입을 가진다는 점 입니다.
invoke에서는 메서드에 대한 응답을 대기할 수 있습니다.
send method는 반면에 응답을 받지 않습니다.
on(String methodName, MethodInvocationFunc newMethod) → void
off(String methodName, {MethodInvocationFunc? method}) → voidon Method는 SignalR서버의 특정 메서드에 대해서 구독을 시작한다. 라는 개념으로 생각하면 편합니다.
서버측에서 methodName으로 지정된 항목에 대해서 발행되게 되면 flutter client에서는 on으로 등록해놓은 mehtod를 실행하게 됩니다.위 서버에서는 SendMessage를 받을때마다 ReceiveMessage를 구독하고 있는 대상에게 string user, string message를 전달해줍니다.
off Method는 구독을 해제하는 기능입니다.
stream(String methodName, List args) → Stream<Object?>
stream 메서드는 연속적인 수신을 받을때 사용합니다.
public async IAsyncEnumerable<string> StreamMethod(CancellationToken cancellationToken) { for (var i = 0; i < 10; i++) { await Task.Delay(1000, cancellationToken); // 1초 간격 yield return $"Message {i}"; } }
위와같이 서버에서 한번에 전체리스트가 오는것이 아니라 yield return으로 순차적으로 실행되어 메세지가 오는 방식이라면 stream으로 처리해서 yield return이 발생될때마다 특정메서드를 실행시킬 수 있습니다.
onclose(ClosedCallback callback) → void
onreconnected(ReconnectedCallback callback) → dynamic
onreconnecting(ReconnectingCallback callback) → dynamic
위 메서드들은 각이벤트들이 발생될때 콜백을 발생시키는 메서드입니다.import 'package:signalr_netcore/hub_connection.dart'; import 'package:signalr_netcore/hub_connection_builder.dart'; abstract class ChatClient { Future<void> connect(); Future<void> sendMessage(String user, String content); void setReceiveMessage( Function(String user, String message) onReceiveMessage); void close(); } class SignalRChatClient extends ChatClient { late HubConnection hubConnection; String url; SignalRChatClient({required this.url}); @override Future<void> connect() async { hubConnection = HubConnectionBuilder() .withUrl("$url/Chat") .withAutomaticReconnect() .build(); await hubConnection.start(); } @override Future<void> sendMessage(String user, String content) async { await hubConnection.send('SendMessage', args: [user, content]); } @override void setReceiveMessage( Function(String user, String message) onReceiveMessage) { hubConnection.on('ReceiveMessage', (arguments) { String user = arguments![0] as String; String message = arguments[1] as String; onReceiveMessage(user, message); }); } @override void close() { hubConnection.stop(); } }
위 hubConnection을 바탕으로 ChatClient 클래스로 추상화하고 SignalrChatClient 클래스로 구현클래스를 구성했습니다.
연결시에는 HubConnectionBuilder의 옵션을 추가해서 적절히 필요한 옵션을 넣고 build한뒤
HubConnection 개체의 각 메서드들을 필요에 따라서 사용하시면 됩니다.SignalR은 위와 같이 복잡한 단순하게 receive에 대해서는 subscribe 형식으로 사용해서 필요한 메세지를 받아볼 수 있습니다.
위에 설명한 내용 외에도 signalr_netcore 샘플코드를 보면 Header를 추가하는 내용 또한 있습니다.요청을 받아 추가적인 내용을 작성하였으나, 어느 부분에 대해서 더 알고싶은지 물어보았으면 더 좋았을듯 합니다.
그래도 오늘 작성해둔 내용이 도움이 되셨기를 바랍니다.