구성 요소 상태를 유지하지 않으면 미리 렌더링하는 동안 사용되는 상태가 손실되고 앱이 완전히 로드될 때 다시 만들어야 합니다. 비동기적으로 상태가 만들어지면 구성 요소가 다시 렌더링될 때 미리 렌더링된 UI가 대체되므로 UI가 깜박일 수 있습니다.
다음 PrerenderedCounter1 카운터 구성 요소를 고려합니다. 구성 요소는 OnInitialized 수명 주기 메서드의 미리 렌더링 동안 초기 임의 카운터 값을 설정합니다. 그런 다음 구성 요소가 대화형으로 렌더링되면 두 번째로 실행할 때 OnInitialized 초기 개수 값이 바뀝니다.
PrerenderedCounter1.razor:
@page "/prerendered-counter-1"
@inject ILogger<PrerenderedCounter1> Logger
<PageTitle>Prerendered Counter 1</PageTitle>
<h1>Prerendered Counter 1</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount;
protected override void OnInitialized()
{
currentCount = Random.Shared.Next(100);
Logger.LogInformation("currentCount set to {Count}", currentCount);
}
private void IncrementCount() => currentCount++;
}
비고
앱이 대화형 라우팅을 채택하고 내부 향상된 탐색을 통해 페이지에 도달하면 미리 렌더링이 발생하지 않습니다. 따라서 다음 출력을 보려면 구성 요소에 PrerenderedCounter1 대한 전체 페이지 다시 로드를 수행해야 합니다. 자세한 내용은 대화형 라우팅 및 미리 렌더링 섹션을 참조하세요.
앱을 실행하고 구성 요소에서 로깅을 검사합니다. 예제 출력은 다음과 같습니다.
info: BlazorSample.Components.Pages.PrerenderedCounter1[0]
currentCount set to 41
info: BlazorSample.Components.Pages.PrerenderedCounter1[0]
currentCount set to 92
첫 번째 로깅된 개수는 미리 렌더링하는 동안 발생합니다. 구성 요소를 다시 렌더링할 때 미리 렌더링한 후 개수가 다시 설정됩니다. 개수가 41에서 92로 업데이트되면 UI에도 깜박임이 있습니다.
초기 카운터 값을 미리 렌더링 동안 유지하려면 Blazor 서비스는 미리 렌더링된 페이지에서 상태 지속을 지원합니다. 또한 MVC 앱의 Pages나 뷰에 포함된 구성 요소의 경우, PersistentComponentState를 사용할 수 있습니다.
미리 렌더링하는 동안 사용된 것과 동일한 상태로 구성 요소를 초기화하면 비용이 많이 드는 초기화 단계가 한 번만 실행됩니다. 렌더링된 UI도 미리 렌더링된 UI와 일치하므로 브라우저에서 깜박임이 발생하지 않습니다.
지속형 미리 렌더링된 상태는 구성 요소 상태를 복원하는 데 사용되는 클라이언트로 전송됩니다. 클라이언트 쪽 렌더링(CSR InteractiveWebAssembly) 중에 데이터는 브라우저에 노출되며 중요한 개인 정보를 포함해서는 안 됩니다. 대화형 서버 쪽 렌더링 중(대화형 SSR, InteractiveServer) ASP.NET Core Data Protection은 데이터가 안전하게 전송되도록 합니다. 렌더링 모드는 InteractiveAuto WebAssembly와 서버 대화형 작업을 결합하므로 CSR 사례와 같이 브라우저에 대한 데이터 노출을 고려해야 합니다.
미리 렌더링된 상태를 유지하려면 특성을 사용하여 [PersistentState] 속성의 상태를 유지합니다. 이 특성이 있는 속성은 미리 렌더링하는 동안 서비스를 사용하여 PersistentComponentState 자동으로 유지됩니다. 구성 요소가 대화형으로 렌더링되거나 서비스가 인스턴스화될 때 상태가 검색됩니다.
기본적으로 속성은 기본 설정이 있는 serializer를 System.Text.Json 사용하여 직렬화되고 미리 렌더링된 HTML에 유지됩니다. Serialization은 트리밍 안전성을 보장하지 않으며, 사용된 형식을 반드시 보존해야 합니다. 자세한 내용은 ASP.NET Core용 트리머 구성을 참조하세요 Blazor.
다음 카운터 구성 요소는 미리 렌더링하는 동안 카운터 상태를 유지하며 구성 요소를 초기화하는 상태를 검색합니다.
-
[PersistentState]이 특성은 nullableint형식()에CurrentCount적용됩니다. - 카운터의 상태는
null의OnInitialized에서 할당되며, 구성 요소가 대화형으로 렌더링될 때 자동으로 복원됩니다.
PrerenderedCounter2.razor:
@page "/prerendered-counter-2"
@inject ILogger<PrerenderedCounter2> Logger
<PageTitle>Prerendered Counter 2</PageTitle>
<h1>Prerendered Counter 2</h1>
<p role="status">Current count: @CurrentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
[PersistentState]
public int? CurrentCount { get; set; }
protected override void OnInitialized()
{
if (CurrentCount is null)
{
CurrentCount = Random.Shared.Next(100);
Logger.LogInformation("CurrentCount set to {Count}", CurrentCount);
}
else
{
Logger.LogInformation("CurrentCount restored to {Count}", CurrentCount);
}
}
private void IncrementCount() => CurrentCount++;
}
구성 요소가 실행되면 CurrentCount 미리 렌더링하는 동안 한 번만 설정됩니다. 구성 요소를 다시 렌더링하면 값이 복원됩니다. 예제 출력은 다음과 같습니다.
비고
앱이 대화형 라우팅을 채택하고 내부 향상된 탐색을 통해 페이지에 도달하면 미리 렌더링이 발생하지 않습니다. 따라서 다음 출력을 보려면 구성 요소에 대한 전체 페이지 다시 로드를 수행해야 합니다. 자세한 내용은 대화형 라우팅 및 미리 렌더링 섹션을 참조하세요.
info: BlazorSample.Components.Pages.PrerenderedCounter2[0]
CurrentCount set to 96
info: BlazorSample.Components.Pages.PrerenderedCounter2[0]
CurrentCount restored to 96
다음 예제에서는 동일한 형식의 여러 구성 요소에 대한 상태를 serialize합니다.
-
[PersistentState]주석이 달린 속성은 미리 렌더링하는 동안 직렬화됩니다. -
@key지시문 특성은 상태가 구성 요소 인스턴스와 올바르게 연결되어 있는지 확인하는 데 사용됩니다. - 이
Element속성은 쿼리 매개 변수 및 양식 데이터에 대해 null 참조를 방지하는 방법과 유사하게 null 참조 예외를 방지하기 위해 수명 주기 메서드에서 초기화OnInitialized됩니다.
PersistentChild.razor:
<div>
<p>Current count: @Element.CurrentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
</div>
@code {
[PersistentState]
public State Element { get; set; }
protected override void OnInitialized()
{
Element ??= new State();
}
private void IncrementCount()
{
Element.CurrentCount++;
}
private class State
{
public int CurrentCount { get; set; }
}
}
Parent.razor:
@page "/parent"
@foreach (var element in elements)
{
<PersistentChild @key="element.Name" />
}
서비스의 상태 직렬화
다음 예제에서는 종속성 주입 서비스에 대한 상태를 직렬화합니다.
-
[PersistentState]속성에 주석이 추가된 속성은 미리 렌더링할 때 직렬화되고, 앱이 대화형이 될 때 역직렬화됩니다. -
RegisterPersistentService 확장 메서드는 지속성을 위해 서비스를 등록하는 데 사용됩니다. 렌더링 모드는 서비스 유형에서 유추할 수 없기 때문에 필요합니다. 다음 값 중에서 사용합니다.
-
RenderMode.Server: 이 서비스는 대화형 서버 렌더링 모드에 사용할 수 있습니다. -
RenderMode.Webassembly: 이 서비스는 대화형 Webassembly 렌더링 모드에 사용할 수 있습니다. -
RenderMode.InteractiveAuto: 구성 요소가 해당 모드 중 하나에서 렌더링되는 경우 대화형 서버 및 대화형 Webassembly 렌더링 모드 모두에 서비스를 사용할 수 있습니다.
-
- 서비스는 대화형 렌더링 모드를 초기화하는 동안 설정되며,
[PersistentState]속성을 갖는 주석이 추가된 속성은 역직렬화됩니다.
비고
범위가 지정된 서비스만 유지됩니다.
직렬화된 속성은 실제 서비스 인스턴스에서 식별됩니다.
- 이 접근 방식은 추상화를 지속적인 서비스로 표시할 수 있게 합니다.
- 실제 구현을 내부 또는 다른 형식으로 설정할 수 있습니다.
- 다른 어셈블리에서 공유 코드를 지원합니다.
- 각 인스턴스에서 동일한 속성을 드러냅니다.
다음 카운터 서비스는 CounterTracker을 사용하여 현재 count 속성 CurrentCount을 표시합니다. 이 속성은 미리 렌더링하는 동안 직렬화되고 서비스가 삽입될 때마다 앱이 대화형으로 전환될 때 역직렬화됩니다.
CounterTracker.cs:
public class CounterTracker
{
[PersistentState]
public int CurrentCount { get; set; }
public void IncrementCount()
{
CurrentCount++;
}
}
파일에서 Program 범위가 지정된 서비스를 등록한 후, RegisterPersistentService를 사용하여 지속성을 위한 서비스를 등록합니다. 다음 예제에서는, 구성 요소가 CounterTracker에 등록되어 있는 경우, 대화형 서버 및 대화형 WebAssembly 렌더링 모드 모두에서 RenderMode.InteractiveAuto 서비스를 사용할 수 있습니다.
파일에서 ProgramMicrosoft.AspNetCore.Components.Web 이름공간을 아직 사용하지 않는 경우, 파일 맨 위에 다음 using 문을 추가합니다.
using Microsoft.AspNetCore.Components.Web;
파일에 서비스가 등록되는 Program 위치:
builder.Services.AddScoped<CounterTracker>();
builder.Services.AddRazorComponents()
.RegisterPersistentService<CounterTracker>(RenderMode.InteractiveAuto);
CounterTracker 서비스를 구성 요소에 삽입하고 이를 사용하여 카운터를 증분합니다. 다음 예제의 데모를 위해 서비스 CurrentCount 속성 값은 미리 렌더링하는 동안에만 10으로 설정됩니다.
Pages/Counter.razor:
@page "/counter"
@inject CounterTracker CounterTracker
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p>Rendering: @RendererInfo.Name</p>
<p role="status">Current count: @CounterTracker.CurrentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
protected override void OnInitialized()
{
if (!RendererInfo.IsInteractive)
{
CounterTracker.CurrentCount = 10;
}
}
private void IncrementCount()
{
CounterTracker.IncrementCount();
}
}
이전 구성 요소를 사용하여 10 CounterTracker.CurrentCount개 수를 유지하는 방법을 보여 주려면 구성 요소로 이동하여 미리 렌더링을 트리거하는 브라우저를 새로 고칩니다. 미리 렌더링이 발생하면, "RendererInfo.Name"가 "Static"를 가리키며 잠시 나타났다가, 최종 렌더링 후 "Server"가 표시됩니다. 카운터는 10에서 시작합니다.
PersistentComponentState 선언적 모델 대신 직접 서비스 사용
특성을 사용하여 상태를 [PersistentState]유지하기 위해 선언적 모델을 사용하는 대신 서비스를 직접 사용할 PersistentComponentState 수 있습니다. 이 서비스는 복잡한 상태 지속성 시나리오에 더 큰 유연성을 제공합니다. 미리 렌더링하는 동안 구성 요소 상태를 유지하기 위해 콜백을 등록하기 위한 호출 PersistentComponentState.RegisterOnPersisting 입니다. 구성 요소가 대화형으로 렌더링될 때 상태가 검색됩니다. 앱 종료 중에 잠재적인 경합 상태를 방지하기 위해 초기화 코드가 끝날 때 호출합니다.
다음 카운터 구성 요소 예제는 미리 렌더링하는 동안 카운터 상태를 유지하며 구성 요소를 초기화하는 상태를 검색합니다.
PrerenderedCounter3.razor:
@page "/prerendered-counter-3"
@implements IDisposable
@inject ILogger<PrerenderedCounter3> Logger
@inject PersistentComponentState ApplicationState
<PageTitle>Prerendered Counter 3</PageTitle>
<h1>Prerendered Counter 3</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount;
private PersistingComponentStateSubscription persistingSubscription;
protected override void OnInitialized()
{
if (!ApplicationState.TryTakeFromJson<int>(
nameof(currentCount), out var restoredCount))
{
currentCount = Random.Shared.Next(100);
Logger.LogInformation("currentCount set to {Count}", currentCount);
}
else
{
currentCount = restoredCount!;
Logger.LogInformation("currentCount restored to {Count}", currentCount);
}
// Call at the end to avoid a potential race condition at app shutdown
persistingSubscription = ApplicationState.RegisterOnPersisting(PersistCount);
}
private Task PersistCount()
{
ApplicationState.PersistAsJson(nameof(currentCount), currentCount);
return Task.CompletedTask;
}
private void IncrementCount() => currentCount++;
void IDisposable.Dispose() => persistingSubscription.Dispose();
}
구성 요소가 실행되면 currentCount 미리 렌더링하는 동안 한 번만 설정됩니다. 구성 요소를 다시 렌더링하면 값이 복원됩니다. 예제 출력은 다음과 같습니다.
비고
앱이 대화형 라우팅을 채택하고 내부 향상된 탐색을 통해 페이지에 도달하면 미리 렌더링이 발생하지 않습니다. 따라서 다음 출력을 보려면 구성 요소에 대한 전체 페이지 다시 로드를 수행해야 합니다. 자세한 내용은 대화형 라우팅 및 미리 렌더링 섹션을 참조하세요.
info: BlazorSample.Components.Pages.PrerenderedCounter3[0]
currentCount set to 96
info: BlazorSample.Components.Pages.PrerenderedCounter3[0]
currentCount restored to 96
미리 렌더링된 상태를 보존하려면 PersistentComponentState 서비스를 사용하여 어떤 상태를 유지할지 결정하세요. PersistentComponentState.RegisterOnPersisting 는 미리 렌더링하는 동안 구성 요소 상태를 유지하기 위해 콜백을 등록합니다. 구성 요소가 대화형으로 렌더링될 때 상태가 검색됩니다. 앱 종료 중에 잠재적인 경합 상태를 방지하기 위해 초기화 코드가 끝날 때 호출합니다.
다음 카운터 구성 요소 예제는 미리 렌더링하는 동안 카운터 상태를 유지하며 구성 요소를 초기화하는 상태를 검색합니다.
PrerenderedCounter2.razor:
@page "/prerendered-counter-2"
@implements IDisposable
@inject ILogger<PrerenderedCounter2> Logger
@inject PersistentComponentState ApplicationState
<PageTitle>Prerendered Counter 2</PageTitle>
<h1>Prerendered Counter 2</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount;
private PersistingComponentStateSubscription persistingSubscription;
protected override void OnInitialized()
{
if (!ApplicationState.TryTakeFromJson<int>(
nameof(currentCount), out var restoredCount))
{
currentCount = Random.Shared.Next(100);
Logger.LogInformation("currentCount set to {Count}", currentCount);
}
else
{
currentCount = restoredCount!;
Logger.LogInformation("currentCount restored to {Count}", currentCount);
}
// Call at the end to avoid a potential race condition at app shutdown
persistingSubscription = ApplicationState.RegisterOnPersisting(PersistCount);
}
private Task PersistCount()
{
ApplicationState.PersistAsJson(nameof(currentCount), currentCount);
return Task.CompletedTask;
}
void IDisposable.Dispose() => persistingSubscription.Dispose();
private void IncrementCount() => currentCount++;
}
구성 요소가 실행되면 currentCount 미리 렌더링하는 동안 한 번만 설정됩니다. 구성 요소를 다시 렌더링하면 값이 복원됩니다. 예제 출력은 다음과 같습니다.
비고
앱이 대화형 라우팅을 채택하고 내부 향상된 탐색을 통해 페이지에 도달하면 미리 렌더링이 발생하지 않습니다. 따라서 다음 출력을 보려면 구성 요소에 대한 전체 페이지 다시 로드를 수행해야 합니다. 자세한 내용은 대화형 라우팅 및 미리 렌더링 섹션을 참조하세요.
info: BlazorSample.Components.Pages.PrerenderedCounter2[0]
currentCount set to 96
info: BlazorSample.Components.Pages.PrerenderedCounter2[0]
currentCount restored to 96
영구 구성 요소 상태에 대한 Serialization 확장성
사용자 지정 직렬 변환기를 PersistentComponentStateSerializer<T>와 함께 구현합니다. 등록된 사용자 지정 serializer가 없으면 serialization은 기존 JSON serialization으로 돌아갑니다.
사용자 지정 serializer는 앱의 Program 파일에 등록됩니다. 다음 예제 CustomUserSerializer 에서는 형식에 대해 TUser 등록됩니다.
builder.Services.AddSingleton<PersistentComponentStateSerializer<TUser>,
CustomUserSerializer>();
사용자 지정 serializer를 사용하여 형식이 자동으로 유지되고 복원됩니다.
[PersistentState]
public User? CurrentUser { get; set; } = new();
페이지 및 뷰에 포함된 구성 요소(Razor Pages/MVC)
페이지 또는 MVC 앱의 페이지나 보기에 포함된 구성 요소의 경우, 앱 레이아웃에서 Razor 종료 태그 내부에 <persist-component-state />와 </body> HTML 태그를 추가해야 합니다. 이것은 Pages 및 MVC 앱에만 Razor 필요합니다. 자세한 내용은 ASP.NET Core에서 구성 요소 상태를 유지하는 태그 도우미를 참조하세요.
Pages/Shared/_Layout.cshtml:
<body>
...
<persist-component-state />
</body>
대화형 라우팅 및 미리 렌더링
Routes 구성 요소가 렌더링 모드를 정의하지 않으면 앱은 페이지/구성 요소별 대화형 작업 및 탐색을 사용합니다. 페이지별/구성 요소 탐색을 사용하면 앱이 대화형으로 설정되면 향상된 라우팅을 통해 내부 탐색이 처리됩니다. 이 컨텍스트의 "내부 탐색"은 탐색 이벤트의 URL 대상이 앱 내의 엔드포인트임을 Blazor 의미합니다.
Blazor 는 향상된 탐색 중에 영구 구성 요소 상태 처리를 지원합니다. 향상된 탐색 중에 유지되는 상태는 페이지의 대화형 구성 요소에서 읽을 수 있습니다.
기본적으로 영구 구성 요소 상태는 처음에 페이지에 로드될 때만 대화형 구성 요소에 의해 로드됩니다. 이렇게 하면 구성 요소가 로드된 후 동일한 페이지에 대한 추가 향상된 탐색 이벤트가 발생하는 경우 편집된 웹 폼의 데이터와 같은 중요한 상태를 덮어쓰지 않습니다.
데이터가 읽기 전용이고 자주 변경되지 않는 경우 특성을 설정 AllowUpdates = true[PersistentState]하여 향상된 탐색 중에 업데이트를 허용하도록 옵트인합니다. 이는 페치 비용이 많이 들지만 자주 변경되지 않는 캐시된 데이터를 표시하는 등의 시나리오에 유용합니다. 다음 예제에서는 일기 예보 데이터를 사용하는 AllowUpdates 방법을 보여 줍니다.
[PersistentState(AllowUpdates = true)]
public WeatherForecast[]? Forecasts { get; set; }
protected override async Task OnInitializedAsync()
{
Forecasts ??= await ForecastService.GetForecastAsync();
}
미리 렌더링하는 동안 복원 상태를 건너뛰려면 다음으로 RestoreBehavior설정합니다SkipInitialValue.
[PersistentState(RestoreBehavior = RestoreBehavior.SkipInitialValue)]
public string NoPrerenderedData { get; set; }
다시 연결하는 동안 상태 복원을 건너뛰려면 .로 RestoreBehavior설정합니다SkipLastSnapshot. 다시 연결한 후 새 데이터를 확인하는 데 유용할 수 있습니다.
[PersistentState(RestoreBehavior = RestoreBehavior.SkipLastSnapshot)]
public int CounterNotRestoredOnReconnect { get; set; }
상태를 유지하는 방법을 완전히 제어하는 방법과 PersistentComponentState.RegisterOnRestoring 유사하게 상태를 복원하는 방법을 명령적으로 제어하기 위한 콜백을 등록하기 위한 호출 PersistentComponentState.RegisterOnPersisting 입니다.
PersistentComponentState 서비스는 내부 향상된 페이지 탐색 이벤트가 아닌 첫 페이지 로드 시에만 작동합니다.
앱이 영구 구성 요소 상태를 활용하는 페이지에 대한 전체(비확장) 탐색을 수행하는 경우 앱이 대화형이 될 때 사용할 수 있는 지속형 상태가 제공됩니다.
대화형 회로가 이미 설정되었고 영구적 구성 요소 상태를 사용하는 페이지로 고급 탐색이 수행될 경우, 해당 구성 요소가을/를 사용하기 위해 기존 회로에서 상태
향상된 탐색을 비활성화하면 성능이 저하될 수 있지만, 내부 페이지 요청에 대한 상태 PersistentComponentState 로드 문제를 방지할 수 있습니다. 이에 대한 내용은 ASP.NET Core Blazor 탐색에서 설명합니다. 또는 향상된 탐색 중에 영구 구성 요소 상태 처리를 지원하는 .NET 10 이상으로 Blazor 앱을 업데이트합니다.
ASP.NET Core