.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

๊ณต์œ 

๊ด€๋ จ ๊ธฐ์‚ฌ

Entity Framework Core ์„ฑ๋Šฅ ์ตœ์ ํ™” ๋‹ค์ด์–ด๊ทธ๋žจ - ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฟผ๋ฆฌ์™€ .NET 10

Entity Framework Core: 2026๋…„ ์„ฑ๋Šฅ ์ตœ์ ํ™”์™€ ๋ชจ๋ฒ” ์‚ฌ๋ก€

EF Core 10์˜ ์„ฑ๋Šฅ ์ตœ์ ํ™”๋ฅผ ๋‹ค๋ฃน๋‹ˆ๋‹ค. AsNoTracking, ์ปดํŒŒ์ผ๋œ ์ฟผ๋ฆฌ, ๋ฐฐ์น˜ ์ž‘์—…, ๋ถ„ํ•  ์ฟผ๋ฆฌ, LeftJoin ์—ฐ์‚ฐ์ž ๋“ฑ .NET 10 ํ”„๋กœ๋•์…˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์œ„ํ•œ ์‹ค์šฉ์ ์ธ C# ์ฝ”๋“œ ์˜ˆ์ œ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

.NET๊ณผ C#์„ ํ™œ์šฉํ•œ Clean Architecture ๊ฐ€์ด๋“œ

.NET์„ ํ™œ์šฉํ•œ Clean Architecture ์‹ค์ „ ๊ฐ€์ด๋“œ

C#๊ณผ .NET์œผ๋กœ Clean Architecture๋ฅผ ๋งˆ์Šคํ„ฐํ•ฉ๋‹ˆ๋‹ค. SOLID ์›์น™, ๊ณ„์ธต ๋ถ„๋ฆฌ, ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์‰ฌ์šด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์œ„ํ•œ ๊ตฌํ˜„ ํŒจํ„ด์„ ํ•™์Šตํ•ฉ๋‹ˆ๋‹ค.

C# ๋ฐ .NET ๋ฉด์ ‘ ์งˆ๋ฌธ - ์™„๋ฒฝ ๊ฐ€์ด๋“œ

C# ๋ฐ .NET ๋ฉด์ ‘ ์งˆ๋ฌธ: 2026๋…„ ์™„๋ฒฝ ๊ฐ€์ด๋“œ

๊ฐ€์žฅ ์ž์ฃผ ์ถœ์ œ๋˜๋Š” C# ๋ฐ .NET ๋ฉด์ ‘ ์งˆ๋ฌธ 17์„ ์ž…๋‹ˆ๋‹ค. LINQ, async/await, ์˜์กด์„ฑ ์ฃผ์ž…, Entity Framework, ASP.NET Core ๋“ฑ ์ƒ์„ธํ•œ ๋‹ต๋ณ€๊ณผ ์ฝ”๋“œ ์˜ˆ์ œ๋ฅผ ๋‹ค๋ฃน๋‹ˆ๋‹ค.