.NET 9 Blazor: Blazor United를 활용한 풀스택 개발 가이드 2026

.NET 9 Blazor United는 정적 SSR, Server, WebAssembly 렌더링 모드를 하나의 풀스택 프레임워크로 통합합니다. 렌더링 모드 활용법, 스트리밍 렌더링, 생성자 주입, 프로덕션 패턴을 실습 중심으로 다루는 튜토리얼입니다.

.NET 9 Blazor United를 활용한 다중 렌더링 모드 풀스택 개발

.NET 9의 Blazor United는 정적 서버 사이드 렌더링(SSR), 인터랙티브 Server, WebAssembly라는 세 가지 렌더링 전략을 하나의 풀스택 프레임워크로 통합한 기술입니다. .NET 8에서 처음 도입되고 .NET 9에서 더욱 정교해진 이 통합 모델 덕분에, 코드를 작성하기 전에 Blazor 호스팅 모델을 선택해야 하는 부담이 사라졌습니다.

Blazor United 핵심 요약

Blazor United를 사용하면 동일한 애플리케이션 내에서 정적 SSR, Server 인터랙티비티, WebAssembly 인터랙티비티를 혼합하여 사용할 수 있습니다. 각 컴포넌트가 독립적으로 렌더링 모드를 선언하므로, 페이지별로 성능과 인터랙티비티를 세밀하게 제어할 수 있습니다.

.NET 9 Blazor 웹 앱 설정하기

.NET 9의 blazorweb 템플릿은 세 가지 렌더링 모드를 모두 지원하는 프로젝트를 자동으로 생성합니다. 설정에는 .NET 9 SDK(9.0.100 이상)가 필요하며, 하나의 명령으로 서버 프로젝트와 클라이언트(WASM) 프로젝트가 함께 생성됩니다.

bash
# Create a new Blazor Web App with all render modes enabled
dotnet new blazorweb -n FullStackApp --interactivity Auto --all-interactive false
cd FullStackApp
dotnet run

--interactivity Auto 플래그를 사용하면 Server와 WebAssembly 렌더링 모드가 모두 활성화됩니다. --all-interactive false를 설정하면 기본 렌더링 모드가 정적 SSR로 유지되어, 전역이 아닌 컴포넌트 단위로 인터랙티비티를 선택하는 구조가 됩니다.

.NET 9의 Blazor 렌더링 모드 이해하기

Blazor 웹 앱의 모든 컴포넌트는 실행 위치, 인터랙티비티 지원 여부, 서버와의 통신 방식을 결정하는 렌더링 모드를 갖습니다. .NET 9에서는 네 가지 렌더링 모드를 제공합니다.

| 렌더링 모드 | 호스팅 | 인터랙티비티 | 적합한 용도 | |---|---|---|---| | 정적 SSR | 서버 | 없음 | 마케팅 페이지, 문서, SEO 콘텐츠 | | Interactive Server | SignalR 기반 서버 | 완전 지원 | 대시보드, CRUD, 폼 | | Interactive WebAssembly | WASM 기반 브라우저 | 완전 지원 | 오프라인, 클라이언트 측 고부하 로직 | | Interactive Auto | 서버 → WASM | 완전 지원 | 빠른 로딩과 독립성의 조화 |

렌더링 모드는 @rendermode 지시문 속성을 사용하여 컴포넌트 수준에서 설정됩니다. 이러한 컴포넌트 단위의 세분화 덕분에 하나의 페이지 내에서 정적 섹션과 인터랙티브 섹션을 혼합할 수 있습니다.

Components/Pages/Dashboard.razorcsharp
@page "/dashboard"
@rendermode InteractiveServer

<PageTitle>Dashboard</PageTitle>

<h1>Real-Time Metrics</h1>

<!-- This component renders interactively via SignalR -->
<MetricsChart />

<!-- Static content below renders as plain HTML -->
<footer>Updated every 5 seconds</footer>

.NET 9에서 추가된 ComponentBase.RendererInfo API를 사용하면 컴포넌트가 런타임에 현재 렌더링 모드를 감지할 수 있으며, 실행 컨텍스트에 따른 조건부 로직을 구현할 수 있습니다.

Components/Shared/AdaptiveComponent.razorcsharp
@if (RendererInfo.IsInteractive)
{
    <button @onclick="HandleClick">Interactive Action</button>
}
else
{
    <a href="/fallback">Static Fallback Link</a>
}

@code {
    private void HandleClick()
    {
        // Runs only in interactive modes (Server or WASM)
    }
}

비동기 작업이 많은 페이지를 위한 스트리밍 렌더링

비동기적으로 데이터를 가져오는 정적 SSR 페이지는 모든 작업이 완료될 때까지 응답을 차단할 수 있습니다. 스트리밍 렌더링은 플레이스홀더 HTML을 즉시 전송한 후, 데이터가 도착하면 최종 콘텐츠를 푸시하여 이 문제를 해결합니다. .NET 9에서는 [StreamRendering] 속성에 true 매개변수가 더 이상 필요하지 않으며, 기본적으로 활성화됩니다.

Components/Pages/Products.razorcsharp
@page "/products"
@attribute [StreamRendering]

<PageTitle>Product Catalog</PageTitle>

@if (products is null)
{
    <p>Loading products...</p>
}
else
{
    <div class="product-grid">
        @foreach (var product in products)
        {
            <ProductCard Item="@product" />
        }
    </div>
}

@code {
    private List<Product>? products;

    // Data loads asynchronously; streaming pushes updates to the browser
    protected override async Task OnInitializedAsync()
    {
        products = await ProductService.GetAllAsync();
    }
}

스트리밍 렌더링은 HTTP/1.1 청크 전송 인코딩에 의존하며, 모든 최신 브라우저에서 지원됩니다. 플레이스홀더 콘텐츠가 즉시 렌더링되고, 전체 페이지 새로고침 없이 최종 마크업으로 교체됩니다.

Blazor 컴포넌트의 생성자 주입

.NET 9에서는 기존의 @inject 지시문과 [Inject] 속성을 보완하는 Blazor 컴포넌트용 생성자 주입이 도입되었습니다. C# 12의 기본 생성자와 결합하면 서비스 의존성이 컴포넌트의 타입 시그니처에 포함됩니다.

Components/Pages/OrderHistory.razorcsharp
@page "/orders"
@rendermode InteractiveServer

<h2>Order History</h2>

@if (orders is not null)
{
    <table>
        <thead>
            <tr><th>Order ID</th><th>Date</th><th>Total</th></tr>
        </thead>
        <tbody>
            @foreach (var order in orders)
            {
                <tr>
                    <td>@order.Id</td>
                    <td>@order.PlacedAt.ToShortDateString()</td>
                    <td>@order.Total.ToString("C")</td>
                </tr>
            }
        </tbody>
    </table>
}

@code {
    // Constructor injection via primary constructor
    [Inject] public required IOrderService OrderService { get; set; }
    [Inject] public required NavigationManager Nav { get; set; }

    private List<Order>? orders;

    protected override async Task OnInitializedAsync()
    {
        orders = await OrderService.GetRecentOrdersAsync();
    }
}

생성자 주입을 통해 의존성이 명시적으로 드러나며, 단위 테스트가 간소화됩니다. Program.cs에 등록된 서비스는 자동으로 해석되며, builder.Services.AddKeyedScoped<T>()로 추가된 키 기반 서비스도 포함됩니다.

.NET 면접 준비가 되셨나요?

인터랙티브 시뮬레이터, flashcards, 기술 테스트로 연습하세요.

Interactive Auto 모드 — 양쪽의 장점을 모두 취하다

Auto 렌더링 모드는 가장 빠른 체감 로딩 속도를 제공합니다. 첫 방문 시 컴포넌트는 SignalR을 통해 서버에서 실행되면서, 백그라운드에서 WebAssembly 런타임이 다운로드됩니다. 이후 방문부터는 실행이 완전히 브라우저로 이동합니다.

Components/Pages/Chat.razorcsharp
@page "/chat"
@rendermode InteractiveAuto

<h2>Live Chat</h2>

<div class="chat-messages">
    @foreach (var msg in messages)
    {
        <div class="message">@msg.Author: @msg.Text</div>
    }
</div>

<input @bind="newMessage" @onkeydown="HandleKey" placeholder="Type a message..." />

@code {
    [Inject] public required IChatService ChatService { get; set; }

    private List<ChatMessage> messages = new();
    private string newMessage = string.Empty;

    protected override async Task OnInitializedAsync()
    {
        messages = await ChatService.GetRecentMessagesAsync();
    }

    private async Task HandleKey(KeyboardEventArgs e)
    {
        if (e.Key == "Enter" && !string.IsNullOrWhiteSpace(newMessage))
        {
            await ChatService.SendAsync(newMessage);
            newMessage = string.Empty;
        }
    }
}

Auto 모드에서는 컴포넌트의 의존성과 로직이 서버와 WASM 두 컨텍스트 모두에서 동작해야 합니다. 서비스는 인터페이스로 추상화하고, 서버와 클라이언트의 DI 컨테이너에 모두 등록해야 합니다.

.NET 9의 재연결 및 복원력 개선

.NET 9에서는 Blazor Server의 재연결 로직이 지수 백오프 전략으로 전면 재작성되었습니다. 고정 간격으로 재시도하는 대신, 프레임워크가 빠른 재시도로 시작하여 점차 지연 시간을 늘립니다. 이를 통해 장애 시 서버 부하를 줄이면서도 일시적인 연결 끊김에서 빠르게 복구할 수 있습니다.

주요 개선 사항:

  • 탭으로 다시 돌아오면 즉시 재연결을 시도합니다
  • 서버가 이미 서킷을 해제한 경우, 페이지가 자동으로 새로고침됩니다
  • 기본 UI에 "서버에 재연결 중..."과 진행 표시기가 표시됩니다
  • 재시도 간격은 Blazor.start()를 통해 구성할 수 있습니다
wwwroot/app.js — Custom reconnection configurationjavascript
Blazor.start({
    circuit: {
        reconnectionOptions: {
            retryIntervalMilliseconds: (retryNumber) => {
                // Exponential backoff: 200ms, 400ms, 800ms, max 30s
                return Math.min(200 * Math.pow(2, retryNumber), 30000);
            },
            maxRetries: 15
        }
    }
});

정적 SSR과 선택적 인터랙티비티

2026년 대부분의 Blazor 웹 앱에 권장되는 아키텍처는 정적 SSR을 기본값으로 사용하고, 필요한 곳에만 인터랙티비티를 추가하는 방식입니다. [ExcludeFromInteractiveRouting] 속성(.NET 9 신규 기능)은 정적으로 유지해야 하는 페이지를 표시하는 데 사용되며, HTTP 쿠키나 요청/응답 주기에 의존하는 페이지에 유용합니다.

Components/Pages/Privacy.razorcsharp
@page "/privacy"
@attribute [ExcludeFromInteractiveRouting]

<PageTitle>Privacy Policy</PageTitle>

<!-- This page always renders as static HTML -->
<!-- Even if global interactivity is enabled -->
<article>
    <h1>Privacy Policy</h1>
    <p>Last updated: April 2026</p>
    <!-- Content -->
</article>

이 패턴은 MapStaticAssets와 잘 어울립니다. MapStaticAssets는 .NET 9의 새로운 미들웨어로, 빌드 시점에 정적 파일을 압축하고 핑거프린팅 처리하여 기존의 UseStaticFiles 방식을 최적화된 전달 방식으로 대체합니다.

.NET 9 AOT 컴파일을 통한 성능 향상

.NET 9 AOT(Ahead-of-Time) 컴파일은 .NET 8 대비 WebAssembly 페이로드 크기를 최대 40% 줄입니다. Google Lighthouse 벤치마크 기준으로 Blazor WASM 시작 속도가 25% 빨라졌습니다. 이러한 성능 개선은 AOT를 활성화하여 퍼블리시하면 자동으로 적용됩니다.

bash
# Publish with AOT for production
dotnet publish -c Release -p:RunAOTCompilation=true

인터랙티브 Server 컴포넌트에서 WebSocket 압축도 기본적으로 활성화됩니다. 압축 관련 공격 벡터를 완화하기 위해 콘텐츠 보안 정책(frame-ancestors: 'self')이 자동으로 적용됩니다.

연습을 시작하세요!

면접 시뮬레이터와 기술 테스트로 지식을 테스트하세요.

결론

  • .NET 9의 Blazor United는 정적 SSR, Server, WebAssembly를 단일 프로젝트에 통합하여 호스팅 모델 선택의 부담을 없앱니다
  • @rendermode로 컴포넌트별 렌더링 모드를 설정하여 각 페이지를 독립적으로 최적화할 수 있습니다 — SEO에는 정적, 대시보드에는 Server, 하이브리드 시나리오에는 Auto를 적용합니다
  • [StreamRendering]을 활용한 스트리밍 렌더링으로 비동기 작업이 많은 페이지의 로딩 지연을 해소할 수 있습니다
  • .NET 9의 신규 기능인 생성자 주입은 컴포넌트의 의존성을 명시적이고 테스트 가능하게 만듭니다
  • Auto 렌더링 모드는 Server를 통한 빠른 첫 렌더링을 제공한 후, 재방문 시 WASM으로 전환하여 완전한 클라이언트 독립성을 실현합니다
  • 지수 백오프 재연결로 연결 끊김이 줄어들고 수동 페이지 새로고침이 불필요해집니다
  • AOT 컴파일로 WASM 페이로드 크기가 40% 감소하고 시작 시간이 25% 단축됩니다
  • 모든 Blazor 웹 앱은 정적 SSR을 기본값으로 시작한 후, UX가 요구하는 곳에 인터랙티비티를 추가하는 것이 권장됩니다

연습을 시작하세요!

면접 시뮬레이터와 기술 테스트로 지식을 테스트하세요.

태그

#dotnet
#blazor
#aspnet-core
#webassembly
#full-stack

공유

관련 기사