핵심 구성 요소 ASP.NET Razor 미리 렌더링

이 문서에서는 Razor Web Apps의 서버 렌더링 구성 요소에 대한 구성 요소 미리 렌더링 시나리오를 Blazor 설명합니다.

미리 렌더링 은 렌더링된 컨트롤에 대한 이벤트 처리기를 사용하도록 설정하지 않고 서버에서 페이지 콘텐츠를 처음 렌더링하는 프로세스입니다. 서버는 초기 요청에 대한 응답으로 가능한 한 빨리 페이지의 HTML UI를 출력하므로 앱이 사용자에게 더 반응할 수 있습니다. 사전 렌더링은 검색 엔진이 페이지 순위를 계산하는 데 사용하는 초기 HTTP 응답에 대한 콘텐츠를 렌더링하여 SEO(검색 엔진 최적화)를 향상시킬 수도 있습니다.

미리 렌더링된 상태 유지

미리 렌더링된 상태를 유지하지 않으면 미리 렌더링하는 동안 사용된 상태를 잃게 되며 앱이 완전히 로드되면 다시 만들어야 합니다. 비동기적으로 상태가 만들어지면 구성 요소가 다시 렌더링될 때 미리 렌더링된 UI가 대체되므로 UI가 깜박일 수 있습니다.

다음 PrerenderedCounter1 카운터 구성 요소를 고려합니다. 구성 요소는 수명 주기 메서드에서 OnInitialized 미리 렌더링하는 동안 초기 임의 카운터 값을 설정합니다. 클라이언트에 SignalR 대한 연결이 설정되면 구성 요소가 다시 라우팅되고 두 번째로 실행될 때 OnInitialized 초기 개수 값이 바뀝니다.

PrerenderedCounter1.razor:

@page "/prerendered-counter-1"
@rendermode @(new InteractiveServerRenderMode(prerender: true))
@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 동안 카운터의 초기 값을 유지하려면 서비스를 사용하여 PersistentComponentState 미리 렌더링된 페이지에서 상태를 유지하도록 지원합니다(및 페이지 또는 MVC 앱의 페이지 또는 뷰에 포함된 구성 요소의 Razor경우 구성 요소 상태 태그 도우미 유지).

미리 렌더링된 상태를 유지하려면 서비스를 사용하여 PersistentComponentState 유지할 상태를 결정합니다. PersistentComponentState.RegisterOnPersisting은 앱이 일시 중지되기 전에 구성 요소 상태를 유지하도록 콜백을 등록합니다. 앱이 다시 시작될 때 상태가 검색됩니다.

다음 예제에서는 일반적인 패턴을 보여 줍니다.

  • {TYPE} 자리 표시자는 유지할 데이터의 형식을 나타냅니다.
  • {TOKEN} 자리 표시자는 상태 식별자 문자열입니다. 자리 표시자가 상태를 보유하는 변수의 이름인 경우 {VARIABLE} 를 사용하는 nameof({VARIABLE})것이 좋습니다. 상태 식별자에 사용하면 nameof() 따옴표 붙은 문자열을 사용하지 않습니다.
@implements IDisposable
@inject PersistentComponentState ApplicationState

...

@code {
    private {TYPE} data;
    private PersistingComponentStateSubscription persistingSubscription;

    protected override async Task OnInitializedAsync()
    {
        persistingSubscription = 
            ApplicationState.RegisterOnPersisting(PersistData);

        if (!ApplicationState.TryTakeFromJson<{TYPE}>(
            "{TOKEN}", out var restored))
        {
            data = await ...;
        }
        else
        {
            data = restored!;
        }
    }

    private Task PersistData()
    {
        ApplicationState.PersistAsJson("{TOKEN}", data);

        return Task.CompletedTask;
    }

    void IDisposable.Dispose()
    {
        persistingSubscription.Dispose();
    }
}

다음 카운터 구성 요소 예제는 미리 렌더링하는 동안 카운터 상태를 유지하며 구성 요소를 초기화하는 상태를 검색합니다.

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()
    {
        persistingSubscription =
            ApplicationState.RegisterOnPersisting(PersistCount);

        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);
        }
    }

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

        return Task.CompletedTask;
    }

    void IDisposable.Dispose() => persistingSubscription.Dispose();

    private void IncrementCount()
    {
        currentCount++;
    }
}

구성 요소가 실행되면 currentCount 미리 렌더링하는 동안 한 번만 설정됩니다. 구성 요소를 다시 렌더링하면 값이 복원됩니다. 예제 출력은 다음과 같습니다.

참고 항목

앱이 대화형 라우팅을 채택하고 내부 탐색을 통해 페이지에 도달하면 사전 렌더링이 발생하지 않습니다. 따라서 다음 출력을 보려면 구성 요소에 PrerenderedCounter2 대한 전체 페이지 다시 로드를 수행해야 합니다.

info: BlazorSample.Components.Pages.PrerenderedCounter2[0]
currentCount set to 96
info: BlazorSample.Components.Pages.PrerenderedCounter2[0]
currentCount restored to 96

미리 렌더링하는 동안 사용된 것과 동일한 상태로 구성 요소를 초기화하면 비용이 많이 드는 초기화 단계가 한 번만 실행됩니다. 렌더링된 UI도 미리 렌더링된 UI와 일치하므로 브라우저에서 깜박임이 발생하지 않습니다.

페이지 및 뷰에 포함된 구성 요소(Razor Pages/MVC)

페이지 또는 MVC 앱의 페이지 또는 보기에 포함된 구성 요소의 경우 앱 레이아웃의 Razor 닫는 </body> 태그 내에 HTML 태그를 사용하여 <persist-component-state /> 구성 요소 상태 태그 유지 도우미를 추가해야 합니다. Pages 및 MVC 앱에 Razor 만 필요합니다. 자세한 내용은 ASP.NET Core에서 구성 요소 상태 태그 도우미 유지를 참조 하세요.

Pages/Shared/_Layout.cshtml:

<body>
    ...

    <persist-component-state />
</body>

대화형 라우팅 및 미리 렌더링

대화형 라우팅에 대한 내부 탐색에는 서버에서 새 페이지 콘텐츠를 요청하는 작업이 포함되지 않습니다. 따라서 내부 페이지 요청에 대해 미리 렌더링이 발생하지 않습니다.

PersistentComponentState 서비스는 향상된 페이지 탐색 이벤트가 아닌 초기 페이지 로드에서 만 작동합니다. 앱이 영구 구성 요소 상태를 활용하는 페이지에 대한 전체(비확장) 탐색을 수행하는 경우 앱이 대화형이 될 때 사용할 수 있는 지속형 상태가 제공됩니다. 그러나 대화형 회로가 이미 설정되었고 지속형 구성 요소 상태를 렌더링하는 페이지로 향상된 탐색이 수행되는 경우 해당 상태는 기존 회로에서 사용할 수 없습니다. 서비스는 PersistentComponentState 향상된 탐색을 인식하지 못하며 이미 실행 중인 구성 요소에 상태 업데이트를 제공하는 메커니즘이 없습니다.

미리 렌더링 지침

미리 렌더링 지침은 주제별로 설명서에 Blazor 정리되어 있습니다. 다음 링크는 주체가 설정한 설명서 전체의 모든 미리 렌더링 지침을 다룹니다.