Blazor Server
1. Blazor Server 흐름분석

Blazor Server프로젝트를 생성하면 위와같은 폴더와 파일들이 생성이되는데 이를 실행하면 아래의 웹 페이지가 출력이된다.

코드의 흐름을 보면 다음의 순서와 같다.
Program.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace HelloBlazorServer
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
먼저 메인함수안에서 Startup.cs파일을 실행시킨다.
Startup.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using HelloBlazorServer.Data;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace HelloBlazorServer
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddSingleton<WeatherForecastService>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
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.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
}
}
}
Startup에서 코드 흐름을 따라가다(코드 전부를 이해할 필요는 없다) 마지막에 "endpoints.MapFallbackToPage("/_Host");"에 의미는 _Host.cshtml을 참조한다는 뜻이다.
_Host.cshtml
@page "/"
@namespace HelloBlazorServer.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
Layout = null;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>HelloBlazorServer</title>
<base href="~/" />
<link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
<link href="css/site.css" rel="stylesheet" />
</head>
<body>
<app>
<component type="typeof(App)" render-mode="ServerPrerendered" />
</app>
<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>
코드 중간에 <app> <component type="typeof(App)" render-mode="ServerPrerendered" /> </app>이 입력되어 있다. 여기서 typeof(App)가 의미하는 바는 App.razor파일을 참조한다는 뜻이다.
참고로, <app>은 정식 html태그가 아니고 따로 정의해준 태그이다.
App.razor
<Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
세 번째 줄에 "@typeof(MainLayout)"이라고 적혀있는데 MainLaout.razor파일을 참조한다는 뜻이다.
MainLayout.razor
@inherits LayoutComponentBase
<div class="sidebar">
<NavMenu />
</div>
<div class="main">
<div class="top-row px-4">
<a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
</div>
<div class="content px-4">
@Body
</div>
</div>
<div class="sidebar">가 무슨 뜻인지는 정확히 몰라도 사이드 메뉴를 정해주는 역할이라는 것을 추론할 수 있다.
그리고 다음줄에 <NavMenu />라고 적혀있는데 NavMenu.razor파일을 참조한다는 뜻이다.
NavMenu.razor
<div class="top-row pl-4 navbar navbar-dark">
<a class="navbar-brand" href="">HelloBlazorServer</a>
<button class="navbar-toggler" @onclick="ToggleNavMenu">
<span class="navbar-toggler-icon"></span>
</button>
</div>
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
<ul class="nav flex-column">
<li class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="oi oi-home" aria-hidden="true"></span> Home
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="counter">
<span class="oi oi-plus" aria-hidden="true"></span> Counter
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="fetchdata">
<span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
</NavLink>
</li>
</ul>
</div>
@code {
private bool collapseNavMenu = true;
private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;
private void ToggleNavMenu()
{
collapseNavMenu = !collapseNavMenu;
}
}

html을 분석해보면 sidebar에 있는 메뉴들을 정의해준다는 것을 어느정도 짐작할 수 있다.
특이한점은 @code라는 부분에서 C#코드가 나오는 것을 알 수 있는데, Razor Page에서는 V와 C가 두 개의 파일로 묶여있다고 했었다. 그런데 Blazor에서는 Razor Page보다 한술 더 떠서 V와 C가 하나의 파일안에 모두 작성되었다고 생각하면 된다.
다시 html로 돌아와서 사이드메뉴에는 Home, Counter, Fetch data의 총 3개의 메뉴가 있다.
각각의 기능들을 정의하는 .razor파일들이 Pages 어딘가에 정의 되어있다. 이 파일들은 각각 href="", href="counter", href="fetchdata"라는 html코드를 통해 접근하게되는데. 이 중에 href="counter"를 분석해보기 위해서 Counter.razor파일을 참조해보자.
Counter.razor
@page "/counter"
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
맨 위에 @page "/counter"이라고 정의되어 있는데 href="counter"에서의 counter의 의미는 @page "/counter"의 counter를 뜻한다. 그렇다면 href="fetchdata"는 Pages폴더 어딘가에 정의되어있는 razor파일에 @page "/fetchdata"를 참조한다는 것을 짐작할 수 있다.

웹 페이지로 가서 Counter사이드 메뉴를 누르면 Click me 버튼이 있는 화면으로 바뀌는데, Click me 버튼을 누르면 Current count: 0의 값이 1로 증가한다. 이러한 기능은 Counter.razor파일안에 @code부분에서 정의되어있는데 다시 한 번 V와 C의 기능이 하나의 파일에 묶여있음을 확인 할 수 있다.