.NET 9 Blazor 탐ꡬ - slog

λͺ©μ 

.NET 9 Blazor의 μΆ”κ°€, λ³€κ²½λœ κΈ°λŠ₯ 탐ꡬ

λͺ©ν‘œ

  • Static SSR 및 ν•˜μ΄λΈŒλ¦¬λ“œ λ™μž‘μ„± 확인
  • κ°„λ‹¨ν•œ λΈ”λ‘œκ·Έ μ„œλΉ„μŠ€ λ§Œλ“€κΈ°
4 Likes

λ Œλ” λͺ¨λ“œ

1 Like

@BigSquare λ‹˜μ΄ μ˜ˆμ „μ— Static SSR의 κΈ°λ³Έ λ™μž‘μ„±μ„ ν™•μΈν•˜μ…¨μŠ΅λ‹ˆλ‹€.

Static SSRλŠ” μ„œλ²„ λ˜λŠ” ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ 이전 Blazor νŽ˜μ΄μ§€ 처럼 μƒν˜Έ μž‘μš©ν•˜λŠ” κ²ƒκ³ΌλŠ” λ‹€λ₯΄κ²Œ – μ—¬κΈ°μ„œ μƒν˜Έμž‘μš©μ΄λž€ μƒνƒœκ°€ μœ μ§€λ˜κ³  클릭 등에 μ˜ν•΄ μƒνƒœκ°€ λ³€κ²½λ˜λ©° κ·Έ μƒνƒœλ₯Ό λ‹΄κ³  μžˆλŠ” μΈμŠ€ν„΄μŠ€κ°€ 계속 μœ μ§€λ˜λŠ” 것을 μ˜λ―Έν•©λ‹ˆλ‹€ – Static SSR은 μ„œλ²„μ—μ„œ νŽ˜μ΄μ§€λ₯Ό λ Œλ”λ§ ν•˜κ³  λ‚œ ν›„ μΈμŠ€ν„΄μŠ€λŠ” νκΈ°λ˜λŠ” 방식 μž…λ‹ˆλ‹€.

λ‹€λ§Œ Blazorμ—μ„œ μ œκ³΅ν•˜λŠ” Static SSR의 경우 전체 νŽ˜μ΄μ§€κ°€ λ¦¬ν”Œλ ˆμ‹œ λ˜μ–΄ μ‚¬μš©μ„±μ΄ μ €ν•˜λ˜κΈ° λ•Œλ¬Έμ— Blazorμ—μ„œλŠ” ν–₯μƒλœ 양식 처리λ₯Ό 톡해 이λ₯Ό λ³΄μ™„ν•©λ‹ˆλ‹€.

2 Likes

.NET MAUI Blazor ν•˜μ΄λΈŒλ¦¬λ“œ μ›Ή μ•± ν…œν”Œλ¦Ώ

.NET MAUI μ›Œν¬λ‘œλ“œλ₯Ό μ„€μΉ˜ν•œ ν›„

dotnet workload install maui

λ‹€μŒμ˜ λͺ…령을 μ΄μš©ν•΄μ„œ Blazor μ›Ήμ•± MAUI ν…œν”Œλ¦ΏμœΌλ‘œ ν”„λ‘œμ νŠΈλ₯Ό μ‹œμž‘ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

dotnet new maui-blazor-web

이 ν…œν”Œλ¦ΏμœΌλ‘œ 앱을 μ‹€ν–‰ν•˜λ©΄ Blazor둜 MAUI ν™˜κ²½μ—μ„œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ κ°œλ°œν•  수 있으며 Server λ˜λŠ” WebAssembly λ°©μ‹μœΌλ‘œ λ™μž‘ν•˜λŠ” 것이 μ•„λ‹ˆλΌ 인베딩 된 WebViewμ—μ„œ λ Œλ”λ§ ν•˜λŠ” λ°©μ‹μœΌλ‘œ λ™μž‘ν•˜κ²Œ λ©λ‹ˆλ‹€.

1 Like

9.0 μ—μ„œ μ†Œκ°œλœ 이 μ†”λ£¨μ…˜ ν…œν”Œλ¦Ώμ€ μ›Ήμ•±κ³Ό 크둜슀 ν”Œλž«νΌ λ°μŠ€ν¬νƒ‘ μ•±μ˜ UIλ₯Ό Blazor ν•˜λ‚˜λ‘œ μž‘μ„±ν•  수 μžˆλŠ” 방법을 μ•Œλ €μ£ΌλŠ” 것 κ°™μŠ΅λ‹ˆλ‹€.

  • Maui Blazor Hybrid
    • Window
    • Mac
    • Andriod
    • iOS
    • Tizen
  • Blazor Web App
    • Blazor Webassembly
  • RCL

8.0 κΉŒμ§€λ§Œ 해도, μœ„μ™€ 같은 ꡬ성을 ν•˜κΈ° μœ„ν•΄μ„œλŠ” μ•„λž˜μ²˜λŸΌ ꡬ성을 ν–ˆμ–΄μ•Ό ν–ˆμŠ΅λ‹ˆλ‹€.

  • Maui Blazor Hybrid
    • Window (선택적)
    • Mac
    • Andriod
    • iOS
    • Tizen
  • WPF Blazor Hosting (선택적)
  • Windows Forms Blazor Hosting (선택적)
  • Blazor Webassembly Standalone
  • RCL
  • Asp.Net Core Web api

μœ„μ™€ 같이 κ΅¬μ„±ν•œ μ΄μœ λŠ” Blazor hybrid/Hosting κ³Ό Blazor Webassembly의 μΈν„°λ ‰ν‹°λΈŒ λͺ¨λ“œκ°€ 기본적으둜 κ°™κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€.

μ‹€μ œλ‘œ ν•΄λ³΄μ‹œλ©΄, μ„œλ²„ μΈ‘ μΈν„°λ ‰ν‹°λΈŒμ™€ ν΄λΌμ΄μ–ΈνŠΈ μΈ‘ μΈν„°λ ‰ν‹°λΈŒλ₯Ό λͺ¨λ‘ λ§Œμ‘±ν•˜λŠ” λ ˆμ΄μ € μš”μ†Œλ₯Ό RCL에 μž‘μ„±ν•˜λŠ” 것은 사싀 κ½€ μ–΄λ ΅λ‹€λŠ” 점을 μ•Œκ²Œ λ©λ‹ˆλ‹€.

9.0 ν…œν”Œλ¦Ώμ€ μ„œλ²„ μΈ‘ λ Œλ”λ§μ΄ λ‚„ 수 밖에 μ—†μ–΄ λ³΄μ΄λŠ”λ°, μ•„λ‹ˆλ‚˜ λ‹€λ₯ΌκΉŒ λ Œλ” λͺ¨λ“œ μ²˜λ¦¬μ— κ΄€ν•œ 주의 사항이 λ¬Έμ„œμ— 많이 λ‚˜μ˜€λ„€μš”.

프리뷰 딱지 λ–Όκ³  λ‚˜μ˜€λ©΄, μ–΄λ–»κ²Œ κ΅¬μ„±ν–ˆλŠ”μ§€ μ‚΄νŽ΄ λ΄μ•Όκ² μŠ΅λ‹ˆλ‹€.

1 Like

ν•˜μ΄λΈŒλ¦¬λ“œμ—μ„œ λ Œλ” λͺ¨λ“œ 적용이 아직은 μ „μ—­λ§Œ μ§€μ›ν•œλ‹€λŠ” λ‚΄μš©μ€ ν™•μΈν•˜μ˜€λŠ”λ° μ–΄λ–€ 뢀뢄이 RCLμ—μ„œ μΈν„°λ ‰ν‹°λΈŒ κ΅¬ν˜„μ„ μ–΄λ ΅κ²Œ ν•˜λŠ”μ§€ κΆκΈˆν•©λ‹ˆλ‹€.

μ œκ°€ μ΄ν•΄ν•œ λ°”λ‘œλŠ” Blazor ν•˜μ΄λΈŒλ¦¬λ“œ 방식은 Server / WebAssembly 방식이 μ•„λ‹ˆλΌ WebViewλ₯Ό 직접 μ‚¬μš©ν•΄μ„œ λ Œλ”λ§ν•˜λŠ” κ²ƒμœΌλ‘œ μ•Œκ³  μžˆλŠ”λ°μš” μ΄λŠ” WebAssembly보닀 Server의 μΈν„°λ ‰ν‹°λΈŒ λ™μž‘μ— κ°€κΉκ² μ§€λ§Œ μ–΄μ¨Œλ“  Server λͺ¨λ“œλŠ” μ•„λ‹™λ‹ˆλ‹€.

κ΄€λ ¨ν•΄μ„œ 쑰금 더 μ„€λͺ… 뢀탁 λ“œλ¦½λ‹ˆλ‹€.

RCL에 μžˆλŠ” μš”μ†Œλ“€μ€ 기본적으둜 μΈν„°λ ‰ν‹°λΈŒ λͺ¨λ“œλ₯Ό μ§€μ •ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. (κ·Έλ ‡κ²Œ ꢌ고되고 μžˆμŠ΅λ‹ˆλ‹€)

λ¬Έμ œλŠ” μš”μ†Œκ°€ Routable Component(νŽ˜μ΄μ§€)이고, λΈ”λ ˆμ΄μ € μ›Ήμ•±μ—μ„œ κ°€μ Έλ‹€ μ“°λŠ” κ²½μš°μž…λ‹ˆλ‹€.

μ›Ήμ•±μ—μ„œ λ Œλ”λ§ λͺ¨λ“œλ₯Ό μ§€μ •ν•˜μ§€ μ•ŠμœΌλ©΄, Static SSR 이 λ˜λŠ”λ°, 이 경우, μš”μ†Œμ˜ μΈν„°λ ‰ν‹°λΈŒ μ½”λ“œκ°€ λ™μž‘ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. (form νƒœκ·Έλ§Œ λ™μž‘ν•©λ‹ˆλ‹€)

μΈν„°λ ‰ν‹°λΈŒ μ½”λ“œκ°€ λ™μž‘ν•˜κΈ° μœ„ν•΄μ„œλŠ”, μ›Ήμ•±μ˜ μΈν„°λ ‰ν‹°λΈŒ λͺ¨λ“œλ₯Ό μ „μ—­μœΌλ‘œ ν•˜λ‚˜λ₯Ό 지정해야 ν•©λ‹ˆλ‹€. 이 지정에 λ”°λΌμ„œ, μ„œλ²„ μΈ‘ ν”„λ‘œμ νŠΈλ‚˜ ν΄λΌμ΄μ–ΈνŠΈ μΈ‘ ν”„λ‘œμ νŠΈ 쀑 ν•˜λ‚˜λ§Œ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

λ¬Έμ„œμ— λ³΄λ‹ˆ, 여기에 더해 MAUIμ—μ„œ λ Œλ” λͺ¨λ“œ κ΄€λ ¨ μ—λŸ¬λ„ μžˆλ„€μš”.

The app automatically adopts global interactivity, which is important because MAUI apps always run interactively and throw errors on Razor component pages that explicitly specify a render mode. For more information, see BlazorWebView needs a way to enable overriding ResolveComponentForRenderMode (dotnet/aspnetcore #51235).

μ΄λŸ¬ν•œ 고민을 μ—†μ• λ €λ©΄, RCL μ—λŠ” μΈν„°λ ‰ν‹°λΈŒ λͺ¨λ“œλ₯Ό κ°€λ³€ν•  수 μžˆλŠ” non-routable μš”μ†Œλ§Œ μ •μ˜ν•΄μ•Ό ν•˜λŠ”λ°, 이 경우, λͺ¨λ“  μ‹€ν–‰ ν”„λ‘œμ νŠΈμ— λ™μΌν•œ νŽ˜μ΄μ§€ μš”μ†Œλ“€μ„ μ€‘λ³΅μ μœΌλ‘œ λ„£μ–΄ μ€˜μ•Ό ν•˜λŠ” 뢈편이 λ°œμƒν•©λ‹ˆλ‹€.

웹뷰와 λΈŒλΌμš°μ €λŠ” 같은 μ‹€ν–‰ μ»¨ν…μŠ€νŠΈλΌ ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
즉, λΈ”λ ˆμ΄μ €κ°€ ν΄λΌμ΄μ–ΈνŠΈ μΈ‘μ—μ„œ 싀행됨을 μ˜λ―Έν•©λ‹ˆλ‹€.

원 λŒ“κΈ€μ— μ œμ‹œν•œ 8.0 μ†”λ£¨μ…˜ κ΅¬μ‘°λŠ”, μ•„μ˜ˆ μ„œλ²„ μΈ‘ μ»¨ν…μŠ€νŠΈλ₯Ό λ°°μ œν•˜μ—¬, ν΄λΌμ΄μ–ΈνŠΈ μΈ‘ ν•˜λ‚˜λ‘œ μ‹€ν–‰ μ»¨ν…μŠ€νŠΈλ₯Ό λ‹¨μˆœν™”ν•œ κ²ƒμž…λ‹ˆλ‹€. (이 λ•Œλ¬Έμ—, μ„œλ²„ μΈ‘ λΈ”λ ˆμ΄μ €λ₯Ό κ°€μ •ν•˜λŠ” Blazor Wep app을 μ‚¬μš©ν•˜μ§€ μ•Šκ²Œ λ©λ‹ˆλ‹€.)

μ΄λŸ¬ν•œ 가정이 있으면, RCL의 λͺ¨λ“  μš”μ†ŒλŠ” ν•˜λ‚˜μ˜ μ‹€ν–‰ μ»¨ν…ŒμŠ€νŠΈλ§Œ κ°€μ •ν•˜μ—¬ ν†΅μΌμ μœΌλ‘œ μž‘μ„±ν•  수 있고, λͺ¨λ“  ν΄λΌμ΄μ–ΈνŠΈ μΈ‘ ν”„λ‘œμ νŠΈμ—μ„œ μ‚¬μš©ν•  있게 λ˜λŠ” 것이죠.

μ΄λŸ¬ν•œ ν†΅μΌμ„±μœΌλ‘œ 인해, RCL ν”„λ‘œμ νŠΈλŠ” νŽ˜μ΄μ§€λŠ” λ¬Όλ‘ , App.razor κΉŒμ§€ λ³΄μœ ν•  수 있게 λ©λ‹ˆλ‹€. RCL 이 App 의 λͺ¨λ“  둜직과 μžμ‚°μ„ κ°–κ²Œ λ˜λŠ” 것이죠.

μ΄λŠ” MAUI λ‚˜ XAMARIN μ—μ„œ 곡유 ν”„λ‘œμ νŠΈμ— App 클래슀λ₯Ό λ‘λŠ” 것과 같은 λ°©μ‹μœΌλ‘œ, ν•˜λ‚˜μ˜ UI μ½”λ“œλ₯Ό (ν”„λ‘ νŠΈ)μ›Ήμ•±, 데슀트 탑 μ•±, λͺ¨λ°”일 μ•±μ—μ„œ (μ½”λ“œ μˆ˜μ • 없이) μ‚¬μš©ν•˜λŠ” κ΅¬μ‘°μž…λ‹ˆλ‹€.

2 Likes

λ Œλ” λͺ¨λ“œμ— λ”°λ₯Έ μΈμŠ€ν„΄μŠ€ 확인

λ Œλ” λͺ¨λ“œκ°€ interactiveμ΄λ©΄μ„œ prerenderκ°€ true(κΈ°λ³Έ μ„€μ •) 일 경우 νŽ˜μ΄μ§€λŠ” μ‚¬μš©μžμ—κ²Œ λΉ λ₯΄κ²Œ 보이기 μœ„ν•΄ μƒν˜Έμž‘μš© μ „ μ •μ μœΌλ‘œ λ¨Όμ € λ Œλ”λ§μ„ ν•œ 이후 μƒν˜Έμž‘μš©μ΄ κ°€λŠ₯ν•œ νŽ˜μ΄μ§€λ‘œ λ‹€μ‹œ λ Œλ”λ§μ„ ν•΄μ„œ 총 두 번 ν•΄λ‹Ή νŽ˜μ΄μ§€ (λ˜λŠ” μ»΄ν¬λ„ŒνŠΈ)의 μΈμŠ€ν„΄μŠ€κ°€ μƒμ„±λ©λ‹ˆλ‹€.

이에 따라 화면에 λ³΄μ—¬μ§€λŠ” 값이 μœ μ§€ λ˜μ§€ μ•ŠλŠ”λ° 이λ₯Ό ν™•μΈν•˜λŠ” κ°„λ‹¨ν•œ 방법은 λ‹€μŒμ˜ μ½”λ“œλ‘œ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

@page "/counter"
@rendermode InteractiveAuto
@*@rendermode InteractiveWebAssembly*@
@* @rendermode InteractiveServer *@

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>
<p>@RendererInfo.Name</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    int currentCount = Random.Shared.Next(0, 100);

    void IncrementCount()
    {
        currentCount++;
    }
}

@rendermodeκ°€ InteractiveServer일 경우 λΉ λ₯΄κ²Œ 화면이 μ „ν™˜λ˜κΈ° λ•Œλ¬Έμ— Autoλ‚˜ WebAssembly이어야 μ „ν™˜ λ˜λŠ” 것을 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

μ΅œμ΄ˆμ—λŠ” μ•„λž˜μ²˜λŸΌ Static으둜 λ Œλ”λ§μ΄ λ˜μ—ˆκ³  초기 카운트 값이 82μ˜€λŠ”λ°

image

νŽ˜μ΄μ§€κ°€ λͺ¨λ‘ μ€€λΉ„κ°€ 되면 WebAssembly으둜 λ Œλ”λ§ 되고 초기 값이 91둜 바뀐 것을 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

image

미리 λ Œλ”λ§λœ μƒνƒœ μœ μ§€

미리 λ Œλ”λ§λœ μƒνƒœλ₯Ό 계속 μœ μ§€ν•˜λ €λ©΄ PersistentComponentState μ„œλΉ„μŠ€λ₯Ό μ•„λž˜μ™€ 같이 μ΄μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

@page "/counter"
@implements IDisposable
@rendermode InteractiveAuto
@*@rendermode InteractiveWebAssembly*@
@* @rendermode InteractiveServer *@
@inject PersistentComponentState ApplicationState

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>
<p>@RendererInfo.Name</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount;
    private PersistingComponentStateSubscription persistingComponentStateSubscription;

    protected override void OnInitialized()
    {
        persistingComponentStateSubscription = ApplicationState.RegisterOnPersisting(PersistCount);

        if (!ApplicationState.TryTakeFromJson<int>(nameof(currentCount), out var restoredCount))
        {
            currentCount = Random.Shared.Next(100);
        }
        else
        {
            currentCount = restoredCount;
        }
    }

    private Task PersistCount()
    {
        ApplicationState.PersistAsJson(nameof(currentCount), currentCount);

        return Task.CompletedTask;
    }


    private void IncrementCount()
    {
        currentCount++;
    }

    void IDisposable.Dispose() => persistingComponentStateSubscription.Dispose();
}

| 미리 λ Œλ”λ§
image

| μƒν˜Έμž‘μš© 적용 ν›„
image

PersistentComponentState에 κ΄€λ ¨λœ λ‹·λ„·λ°λΈŒ 글은 @BigSquare λ‹˜μ˜ λΈ”λ ˆμ΄μ € 8.0 선택 μž₯μ•  μ—μ„œ 찾을 수 μžˆμŠ΅λ‹ˆλ‹€.

1 Like

PersistentComponentState μ„œλΉ„μŠ€λŠ” μ–΄λ–»κ²Œ 미리 λ Œλ”λ§λœ μ‹œμ μ—μ„œμ˜ μƒνƒœλ₯Ό 보쑴할 수 μžˆμ„κΉŒμš”? 미리 λ Œλ”λ§λœ html을 μ‚΄νŽ΄λ³΄λ©΄ λ‹€μŒμ˜ 주석이 λˆˆμ— λ•λ‹ˆλ‹€.

...
<!--Blazor-Server-Component-State:CfDJ8K93lMGTYKpAgncimEFm28D7H3mLLAO3PvR2WrIlslhYg7Vd82+05p2l...
<!--Blazor-WebAssembly-Component-State:eyJfX1Jlc291cmNlQ29sbGVjdGlvblVybCI6Iklp...

이 쀑 Blazor-WebAssembly-Component-Stateλ₯Ό base64 λ³΅ν˜Έν™” ν•˜λ©΄,

{...,"currentCount":"NDk="}

즉, ApplicationState.PersistAsJson(nameof(currentCount), currentCount);에 μ˜ν•΄μ„œ JSON으둜 μ €μž₯된 currentCount κ°’μ΄λΌλŠ” 것을 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

Blazor-Server-Component-State의 경우 base64 디코딩을 ν•˜λ”λΌλ„ μ•”ν˜Έν™” λ˜μ–΄ μžˆλŠ”λ° μ΄λŠ” μ„œλ²„μ—μ„œ λ³΅ν˜Έν™” ν•΄μ„œ μ‚¬μš©ν•΄μ•Ό ν•  μƒνƒœμ΄λ―€λ‘œ ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ λ³€μ‘°ν•  수 없도둝 λ˜μ–΄ μžˆλŠ”κ²ƒμœΌλ‘œ λ³΄μž…λ‹ˆλ‹€.

2 Likes

정적 μ›Ή μžμ‚° 전달 μ΅œμ ν™”

κΈ°μ‘΄ app.UseStaticFiles()μ—μ„œ app.MapStaticAssets()으둜 정적 μžλ£Œμ— μ ‘κ·Όν•˜λŠ” 방식이 λ‹¬λΌμ‘ŒμŠ΅λ‹ˆλ‹€. λ‹€λ§Œ UseStaticAssets()λŠ” 컴파일 및 κ²Œμ‹œ μ‹œμ μ—μ„œ 정적 μžμ›μ„ μ΅œμ ν™” ν•˜λ―€λ‘œ λŸ°νƒ€μž„ μ‹œ λ“±λ‘λ˜λŠ” 정적 μžλ£Œμ— μ ‘κ·Όν•˜κΈ° μœ„ν•΄μ„œλŠ” UseStaticFiles()λ₯Ό μ—¬μ „νžˆ μ‚¬μš©ν•΄μ•Ό ν•œλ‹€κ³  ν•©λ‹ˆλ‹€.

κ·Έλ ‡λ‹€λ©΄ 정적 μžμ‚°μ€ 정적 파일과 μ–΄λ–€ 차이가 μžˆμ„κΉŒμš”? μžμ›μ„ μ••μΆ•ν•˜κ³  또 μ œλŒ€λ‘œ 캐싱될 수 μžˆλ„λ‘ ν•˜λŠ” 일련의 μ²˜λ¦¬κ°€ λ©λ‹ˆλ‹€.

2 Likes

λŸ°νƒ€μž„ μ‹œ λ Œλ”λ§ κ΄€λ ¨ 정보 제곡

RendererInfo 속성을 μ΄μš©ν•˜λ©΄ λŸ°νƒ€μž„ μ‹œ λ Œλ”λ§ κ΄€λ ¨ 정보λ₯Ό 얻을 수 μžˆμŠ΅λ‹ˆλ‹€.

2 Likes