다음을 통해 공유


Blazor 앱의 프로젝트 구조

이 콘텐츠는 .NET Docs 또는 오프라인으로 읽을 수 있는 다운로드 가능한 무료 PDF로 제공되는 Blazor for ASP NET Web Forms Developers for Azure eBook에서 발췌한 것입니다.

Blazor-for-ASP-NET-Web-Forms-Developers eBook cover thumbnail.

상당한 프로젝트 구조 차이에도 불구하고 ASP.NET Web Forms 및 Blazor는 많은 비슷한 개념을 공유합니다. 여기서는 Blazor 프로젝트의 구조를 살펴보고 ASP.NET Web Forms 프로젝트와 비교합니다.

첫 번째 Blazor 앱을 만들려면 Blazor 시작 단계의 지침을 따릅니다. 지침에 따라 Blazor Server 앱 또는 ASP.NET Core에서 호스트되는 BlazorWebAssembly 앱을 만들 수 있습니다. 호스팅 모델 관련 논리를 제외하고 두 프로젝트의 대부분 코드는 동일합니다.

프로젝트 파일

Blazor Server 앱은 .NET 프로젝트입니다. Blazor Server 앱의 프로젝트 파일은 다음과 같이 간단하게 가져올 수 있습니다.

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>

</Project>

BlazorWebAssembly 앱의 프로젝트 파일은 약간 더 복잡해 보입니다(정확한 버전 번호는 다를 수 있음).

<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.0" />
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.0" PrivateAssets="all" />
  </ItemGroup>

</Project>

BlazorWebAssembly 프로젝트는 WebAssembly 기반 .NET 런타임의 브라우저에서 실행되기 때문에 Microsoft.NET.Sdk.Web SDK 대신 Microsoft.NET.Sdk.BlazorWebAssembly를 대상으로 지정합니다. .NET은 서버 또는 개발자 머신에 설치하는 것처럼 웹 브라우저에 설치할 수 없습니다. 따라서 프로젝트는 개별 패키지 참조를 사용하여 Blazor 프레임워크를 참조합니다.

그에 비해 기본 ASP.NET Web Forms 프로젝트에는 .csproj 파일에 거의 300줄의 XML이 포함되며 대부분의 XML은 프로젝트의 다양한 코드 및 콘텐츠 파일을 명시적으로 나열합니다. .NET 5 릴리스 이후부터는 Blazor 서버와 BlazorWebAssembly 앱 모두가 통합된 단일 런타임을 쉽게 공유할 수 있습니다.

지원되지만 개별 어셈블리 참조는 .NET 프로젝트에서 덜 일반적입니다. 대부분 프로젝트 종속성은 NuGet 패키지 참조로 처리됩니다. .NET 프로젝트에서 최상위 패키지 종속성을 참조하면 됩니다. 전이적 종속성은 자동으로 포함됩니다. 일반적으로 ASP.NET Web Forms 프로젝트에 있는 packages.config 파일을 사용하는 대신, <PackageReference> 요소를 사용하여 패키지 참조가 프로젝트 파일에 추가됩니다.

<ItemGroup>
  <PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
</ItemGroup>

진입점

Blazor Server 앱의 진입점은 콘솔 앱에서 볼 수 있듯이 Program.cs 파일에 정의됩니다. 앱이 실행되면 웹앱과 관련된 기본값을 사용하여 웹 호스트 인스턴스를 만들고 실행합니다. 웹 호스트는 Blazor Server 앱의 수명 주기를 관리하고 호스트 수준 서비스를 설정합니다. 해당 서비스의 예로는 구성, 로깅, 종속성 주입, HTTP 서버가 있습니다. 이 코드는 대부분 상용구이며 대개 변경되지 않습니다.

using BlazorApp3.Areas.Identity;
using BlazorApp3.Data;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddScoped<AuthenticationStateProvider, RevalidatingIdentityAuthenticationStateProvider<IdentityUser>>();
builder.Services.AddSingleton<WeatherForecastService>();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();

app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapControllers();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");

app.Run();

BlazorWebAssembly 앱도 Program.cs에서 진입점을 정의합니다. 코드는 약간 다르게 보입니다. 코드는 앱 호스트를 설정하여 앱에 동일한 호스트 수준 서비스를 제공한다는 점에서 유사합니다. 그러나 WebAssembly 앱 호스트는 브라우저에서 직접 실행되므로 HTTP 서버를 설정하지 않습니다.

Blazor 앱은 앱의 시작 논리를 정의하는 데 Global.asax 파일을 사용하지 않습니다. 그 대신 이 논리는 Program.cs 또는 Program.cs에서 참조되는 관련 Startup클래스에 포함됩니다. 어느 쪽이든 이 코드는 앱 및 앱별 서비스를 구성하는 데 사용됩니다.

Blazor Server 앱에서 표시된 Program.cs 파일을 사용하여 클라이언트 브라우저와 서버 간에 Blazor서 사용되는 실시간 연결용 엔드포인트를 설정합니다.

BlazorWebAssembly 앱에서 Program.cs 파일은 앱의 루트 구성 요소 및 해당 구성 요소를 렌더링해야 하는 위치를 정의합니다.

using BlazorApp1;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;

var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");

builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });

await builder.Build().RunAsync();

정적 파일

ASP.NET Web Forms 프로젝트와 달리 Blazor 프로젝트의 일부 파일은 정적 파일로 요청할 수 없습니다. wwwroot 폴더의 파일만 웹 주소를 지정할 수 있습니다. 이 폴더를 앱의 “웹 루트”라고 합니다. 앱의 웹 루트 외부에 있는 항목은 웹 주소를 지정할 수 ‘없습니다’. 이 설정은 웹을 통해 프로젝트 파일이 실수로 공개되는 것을 방지하는 추가 보안 수준을 제공합니다.

구성

ASP.NET Web Forms 앱의 구성은 일반적으로 하나 이상의 web.config 파일을 사용하여 처리됩니다. Blazor 앱은 일반적으로 web.config 파일을 사용하지 않습니다. 해당 파일을 사용하는 경우 파일은 IIS에서 호스트될 때 IIS 관련 설정을 구성하는 데만 사용됩니다. 대신 Blazor 서버 앱은 ASP.NET Core 구성 추상화로 사용합니다. (BlazorWebAssembly 앱은 현재 동일한 구성 추상화는 지원하지 않지만 나중에 추가된 기능일 수 있음). 예를 들어 기본 Blazor 서버 앱은 appsettings.json에 일부 설정을 저장합니다.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

구성 섹션에서 ASP.NET Core 프로젝트의 구성에 관해 자세히 알아봅니다.

Razor 구성 요소

Blazor 프로젝트의 대부분 파일은 .razor 파일입니다. Razor는 웹 UI를 동적으로 생성하는 데 사용되는 HTML 및 C#을 기반으로 하는 템플릿 지정 언어입니다. .razor 파일은 앱 UI를 구성하는 구성 요소를 정의합니다. 대부분의 경우 구성 요소는 Blazor Server 및 BlazorWebAssembly 앱에서 둘 다 동일합니다. Blazor의 구성 요소는 ASP.NET Web Forms의 사용자 정의 컨트롤과 유사합니다.

각 Razor 구성 요소 파일은 프로젝트가 빌드될 때 .NET 클래스로 컴파일됩니다. 생성된 클래스는 구성 요소의 상태, 렌더링 논리, 수명 주기 메서드, 이벤트 처리기, 기타 논리를 캡처합니다. Blazor를 사용하여 재사용 가능한 UI 구성 요소 빌드 섹션에서 구성 요소 작성에 대해 자세히 알아봅니다.

_Imports.razor 파일은 Razor 구성 요소 파일이 아닙니다. 대신, 동일한 폴더와 해당 하위 폴더에 있는 다른 .razor 파일로 가져올 Razor 지시문 세트를 정의합니다. 예를 들어 _Imports.razor 파일은 일반적으로 사용되는 네임스페이스의 using 지시문을 추가하는 기존 방법입니다.

@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.AspNetCore.Components.WebAssembly.Http
@using Microsoft.JSInterop
@using BlazorApp1
@using BlazorApp1.Shared

페이지

Blazor 앱의 페이지는 어디에 있나요? Blazor는 ASP.NET Web Forms 앱의 .aspx 파일과 같이 주소 지정 가능한 페이지의 별도 파일 확장명을 정의하지 않습니다. 대신, 페이지는 구성 요소에 경로를 할당하여 정의됩니다. 경로는 일반적으로 @page Razor 지시문을 사용하여 할당됩니다. 예를 들어 Pages/Counter.razor 파일에서 작성된 Counter 구성 요소는 다음 경로를 정의합니다.

@page "/counter"

Blazor의 라우팅은 서버가 아닌 클라이언트 쪽에서 처리됩니다. 사용자가 브라우저에서 탐색할 때 Blazor는 탐색을 가로챈 다음, 일치하는 경로를 사용하여 구성 요소를 렌더링합니다.

현재 구성 요소 경로는 .aspx 페이지 또는 ASP.NET Core Razor Pages에서 유추되는 것처럼 구성 요소의 파일 위치에 따라 유추되지 않습니다. 이 기능은 향후 추가될 수 있습니다. 각 경로는 구성 요소에서 명시적으로 지정해야 합니다. 라우팅 가능한 구성 요소를 Pages 폴더에 저장하는 것은 특별한 의미가 없으며 규칙일 뿐입니다.

페이지, 라우팅, 레이아웃 섹션의 Blazor에서 라우팅에 대해 자세히 알아봅니다.

Layout

ASP.NET Web Forms 앱에서 일반적인 페이지 레이아웃은 마스터 페이지(Site.Master)를 사용하여 처리됩니다. Blazor 앱에서 페이지 레이아웃은 레이아웃 구성 요소(Shared/MainLayout.razor)를 사용하여 처리됩니다. 레이아웃 구성 요소는 페이지, 라우팅, 레이아웃 섹션에 더 자세히 설명합니다.

Blazor 부트스트랩

Blazor를 부트스트랩하려면 앱에서 다음을 수행해야 합니다.

  • 페이지에서 루트 구성 요소(App.Razor)를 렌더링해야 하는 위치를 지정합니다.
  • 해당 Blazor 프레임워크 스크립트를 추가합니다.

Blazor Server 앱에서 루트 구성 요소의 호스트 페이지는 _Host.cshtml 파일에 정의됩니다. 이 파일은 구성 요소가 아닌 Razor 페이지를 정의합니다. Razor Pages는 Razor 구문을 사용하여 .aspx 페이지와 매우 유사한 서버 주소 지정 가능 페이지를 정의합니다.

@page "/"
@namespace BlazorApp3.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
    Layout = "_Layout";
}

<component type="typeof(App)" render-mode="ServerPrerendered" />

render-mode 특성은 루트 수준 구성 요소를 렌더링해야 하는 위치를 정의하는 데 사용됩니다. RenderMode 옵션은 구성 요소를 렌더링하는 방식을 나타냅니다. 다음 표에서는 지원되는 RenderMode 옵션을 간략하게 설명합니다.

옵션 설명
RenderMode.Server 브라우저와 연결이 설정된 후 대화형으로 렌더링됨
RenderMode.ServerPrerendered 먼저 미리 렌더링된 후 대화형으로 렌더링됨
RenderMode.Static 정적 콘텐츠로 렌더링됨

_Layout cshtml 파일에는 앱 및 해당 구성 요소의 기본 HTML이 포함됩니다.

@using Microsoft.AspNetCore.Components.Web
@namespace BlazorApp3.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <base href="~/" />
    <link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
    <link href="css/site.css" rel="stylesheet" />
    <link href="BlazorApp3.styles.css" rel="stylesheet" />
    <component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" />
</head>
<body>
    @RenderBody()

    <div id="blazor-error-ui">
        <environment include="Staging,Production">
            An error has occurred. This application may no longer respond until reloaded.
        </environment>
        <environment include="Development">
            An unhandled exception has occurred. See browser dev tools for details.
        </environment>
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>

    <script src="_framework/blazor.server.js"></script>
</body>
</html>

_framework/blazor.server.js에 대한 스크립트 참조는 서버와 실시간 연결을 설정한 후 모든 사용자 상호 작용 및 UI 업데이트를 처리합니다.

BlazorWebAssembly 앱에서 호스트 페이지는 wwwroot/index.html 아래 간단한 정적 HTML 파일입니다. ID가 app<div> 요소는 루트 구성 요소를 렌더링해야 하는 위치를 나타내는 데 사용됩니다.

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <title>BlazorApp1</title>
    <base href="/" />
    <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
    <link href="css/app.css" rel="stylesheet" />
    <link href="BlazorApp1.styles.css" rel="stylesheet" />
</head>

<body>
    <div id="app">Loading...</div>

    <div id="blazor-error-ui">
        An unhandled error has occurred.
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>
    <script src="_framework/blazor.webassembly.js"></script>
</body>

</html>

렌더링할 루트 구성 요소는 유연하게 종속성 주입을 통해 서비스를 등록하는 앱의 Program.cs 파일에서 지정됩니다. 자세한 내용은 ASP.NET Core Blazor 종속성 주입을 참조하세요.

var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");

빌드 출력

Blazor 프로젝트가 빌드되면 모든 Razor 구성 요소와 코드 파일은 단일 어셈블리로 컴파일됩니다. ASP.NET Web Forms 프로젝트와 달리 Blazor는 UI 논리의 런타임 컴파일을 지원하지 않습니다.

핫 다시 로드로 앱 실행

Blazor 서버 앱을 실행하려면 디버거가 연결된 상태에서 실행하려면 Visual Studio의 F5를 누르거나, 디버거를 연결하지 않고 실행하려면 Ctrl + F5를 누릅니다.

BlazorWebAssembly 앱을 실행하려면 다음 접근 방식 중 하나를 선택합니다.

  • 개발 서버를 사용하여 직접 클라이언트 프로젝트를 실행합니다.
  • ASP.NET Core를 사용하여 앱을 호스트할 때 서버 프로젝트를 실행합니다.

브라우저와 Visual Studio에서 모두 BlazorWebAssembly 앱을 디버그할 수 있습니다. 자세한 내용은 ASP.NET Core 디버그BlazorWebAssembly를 참조하세요.

Blazor서버와 BlazorWebAssembly 앱 모두 Visual Studio에서 핫 다시 로드를 지원합니다. 핫 다시 로드는 브라우저에서 Blazor 앱 라이브의 변경 내용을 자동으로 업데이트하는 기능입니다. 도구 모음의 해당 아이콘에서 핫 다시 로드의 사용 여부를 전환할 수 있습니다.

Visual Studio 2022: Hot Reload menu item and icon.

아이콘 옆에 있는 caret를 선택하면 추가 옵션이 표시됩니다. 핫 다시 로드 설정 또는 해제, 애플리케이션 다시 시작, 파일을 저장할 때마다 핫 다시 로드 진행 여부 전환을 실행할 수 있습니다.

Visual Studio 2022: Hot Reload menu item with expanded options.

추가 구성 옵션에 액세스할 수도 있습니다. 구성 대화 상자에서 디버깅할 때(편집하며 계속하기와 함께), 디버깅하지 않고 시작할 때 또는 파일을 저장할 때 핫 다시 로드 사용 여부를 지정할 수 있습니다.

Visual Studio 2022: Hot Reload configuration options from the

"개발자 내부 루프"가 핫 다시 로드를 통해 크게 간소화되었습니다. Blazor 개발자는 일반적으로 핫 다시 로드를 사용하여 변경 후 매번 앱을 다시 시작하고 다시 실행하여, 필요에 따라 앱의 적절한 부분으로 이동해야 합니다. 핫 다시 로드 사용하면 대부분의 경우 다시 시작할 필요 없이 실행 중인 앱을 변경할 수 있습니다. 핫 다시 로드는 페이지 상태도 유지하므로 양식 값을 다시 입력하거나 필요한 위치로 앱을 다시 가져올 필요가 없습니다.