강력한 형식의 허브
형식이 지정된 허브 인터페이스를 정의해 클라이언트 메서드 호출에 대한 컴파일 시점 안전성과 IntelliSense를 확보합니다.
강력한 형식의 허브은(는) CoddyKit의 무료 C# Academy 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 C# Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. C# Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
문자열 메서드 이름 사용의 문제
표준 SignalR 호출은 SendAsync("MethodName", ...)을 사용해 메서드 이름을 문자열로 전달합니다. 메서드 이름에 오타가 있으면 오류가 표시되지 않은 채 실패하므로 클라이언트는 메시지를 전혀 받지 못합니다. 강력한 형식의 허브를 사용하면 이 문제를 해결할 수 있습니다.
클라이언트 인터페이스 정의
서버가 클라이언트에서 호출할 수 있는 모든 메서드를 선언하는 인터페이스를 만드십시오. 메서드 이름과 매개 변수 형식이 계약이 됩니다.
// INotificationClient.cs
public interface INotificationClient
{
Task ReceiveMessage(string user, string message);
Task OrderShipped(int orderId, string trackingNumber);
Task UserJoined(string username);
Task UserLeft(string username);
Task SystemAlert(string message);
}강력한 형식의 허브 만들기
클라이언트 인터페이스인 T를 사용해 Hub<T>에서 상속하십시오. 이제 Clients 속성이 형식화되어 IntelliSense에 사용 가능한 메서드가 표시되고 오타는 컴파일 오류가 됩니다.
public class ChatHub : Hub<INotificationClient>
{
public async Task SendMessage(string message)
{
var user = Context.User!.Identity!.Name ?? "Anonymous";
// Compile-time safety — no magic strings!
await Clients.All.ReceiveMessage(user, message);
await Clients.Others.UserJoined(user); // IntelliSense works
}
}강력한 형식의 허브로 특정 클라이언트 대상 지정
Clients.User, Clients.Group, Clients.Caller, Clients.Client 등 모든 표준 대상 지정 메서드를 강력한 형식의 허브에서 사용할 수 있으며, 모두 강력한 형식의 인터페이스를 반환합니다.
public async Task NotifyOrderShipped(int orderId, string customerId)
{
await Clients.User(customerId)
.OrderShipped(orderId, "TRK-12345");
await Clients.Group("admins")
.SystemAlert($"Order {orderId} shipped to {customerId}");
await Clients.Caller.ReceiveMessage("System", "Notification sent");
}강력한 형식의 IHubContext
허브 외부에서 메시지를 보낼 때도 컴파일 시 동일한 안정성을 얻으려면 서비스에 IHubContext<THub, TClient>를 주입하십시오.
public class ShipmentService
{
private readonly IHubContext<ChatHub, INotificationClient> _hub;
public ShipmentService(
IHubContext<ChatHub, INotificationClient> hub) => _hub = hub;
public async Task ProcessShipmentAsync(int orderId, string customerId)
{
// Fully typed — no magic strings
await _hub.Clients.User(customerId)
.OrderShipped(orderId, "TRK-99999");
}
}클라이언트 인터페이스 규칙
클라이언트 인터페이스의 메서드는 Task를 반환해야 하며 Task<T>를 반환해서는 안 됩니다. SignalR은 클라이언트 호출을 실행하고 결과를 기다리지 않으므로 클라이언트에서 서버로 반환되는 값이 없습니다.
// CORRECT
public interface IMyClient
{
Task ReceiveMessage(string msg); // OK
Task UpdateProgress(int percent); // OK
Task<string> GetUserInput(); // NOT SUPPORTED
}
// Return types other than Task are not supported in hub client interfaces
// For request-response patterns, use client-callable hub methods instead강력한 형식의 허브와 그룹 결합
강력한 형식의 그룹도 형식이 지정되지 않은 그룹과 동일하게 작동합니다. API는 같고 메서드 이름이 컴파일 시 검사된다는 점만 다릅니다.
public class RoomHub : Hub<IRoomClient>
{
public async Task JoinRoom(string room)
{
await Groups.AddToGroupAsync(Context.ConnectionId, room);
await Clients.Group(room).UserJoined(Context.User!.Identity!.Name!);
}
public async Task BroadcastToRoom(string room, string message)
{
var user = Context.User!.Identity!.Name!;
await Clients.Group(room).ReceiveMessage(user, message);
}
}강력한 형식의 허브 테스트
클라이언트 메서드 호출이 인터페이스를 통해 이루어지므로 단위 테스트에서 클라이언트를 모의 객체로 대체하여 올바른 메서드가 올바른 인수와 함께 호출되었는지 확인할 수 있습니다.
// NSubstitute test
var mockClients = Substitute.For<IHubCallerClients<INotificationClient>>();
var mockAllClients = Substitute.For<INotificationClient>();
mockClients.All.Returns(mockAllClients);
var hub = new ChatHub { Clients = mockClients };
await hub.SendMessage("Hello!");
await mockAllClients.Received(1).ReceiveMessage("System", "Hello!");애플리케이션 하나에서 여러 허브 사용
하나의 애플리케이션에 서로 다른 실시간 기능을 위한 여러 허브 엔드포인트를 둘 수 있으며, 각 허브는 자체적인 강력한 형식의 클라이언트 인터페이스를 사용할 수 있습니다.
// Three separate hubs
app.MapHub<ChatHub>("/hubs/chat");
app.MapHub<NotificationHub>("/hubs/notifications");
app.MapHub<DashboardHub>("/hubs/dashboard");
// Each with its own client interface:
// Hub<IChatClient>, Hub<INotificationClient>, Hub<IDashboardClient>구조화된 데이터 전송
클라이언트 인터페이스 메서드는 복잡한 데이터 전송 객체(DTO)를 받을 수 있습니다. SignalR이 이를 자동으로 JSON으로 직렬화하므로 형식이 직렬화 가능한지 확인하십시오.
public interface IOrderClient
{
Task OrderUpdated(OrderStatusDto status);
}
public record OrderStatusDto(
int OrderId,
string Status,
decimal Total,
DateTime UpdatedAt);
// Usage in hub:
await Clients.User(customerId).OrderUpdated(
new OrderStatusDto(order.Id, order.Status.ToString(),
order.Total, DateTime.UtcNow));빠른 확인
SignalR 허브 클라이언트 인터페이스의 메서드 반환 형식에는 어떤 제한이 있습니까?
요약: 강력한 형식의 허브
핵심 내용:
- 서버가 클라이언트에서 호출할 수 있는 모든 메서드를 나열하는 인터페이스(IMyClient)를 정의합니다
Hub<IMyClient>에서 상속하면 Clients 속성이 완전히 형식화됩니다- 허브 외부에서 강력한 형식으로 메시지를 보내려면
IHubContext<THub, TClient>를 주입합니다 - 클라이언트 인터페이스 메서드는
Task를 반환해야 하며Task<T>를 반환해서는 안 됩니다 - 인터페이스 기반 클라이언트는 모의 객체로 만들 수 있으므로 강력한 형식의 허브는 테스트하기 쉽습니다
자주 묻는 질문
“강력한 형식의 허브” 강의는 무료인가요?
네 — “강력한 형식의 허브” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 C# Academy 강의 전체를 잠금 해제할 수 있습니다. C# Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
“강력한 형식의 허브”에서 뭘 배우나요?
형식이 지정된 허브 인터페이스를 정의해 클라이언트 메서드 호출에 대한 컴파일 시점 안전성과 IntelliSense를 확보합니다. 브라우저에서 직접 실행하는 실습 코드로 C# Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
C# Academy을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 C# Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.
“강력한 형식의 허브” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 C# Academy 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 C# Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.