재우니의 블로그

 
ASP.NET Core Blazor 를 알아보고 TODO 도 만들어보자.
 
ASP.NET Core 2.1 preview 버전에서 작동되는 Blazor 는 c# 과 razor 그리고 html 형태로 구성되며 이는 브라우저나 WebAssembly 에서 가동이 되는 web ui framework 로 보시면 됩니다.
 
Blazor 는 매우 심플하고 빌드도 빠르며, single page application 에서 어떤 브라우저 든 잘 작동이 됩니다.
사용 용도를 살펴보면 이는 .net 에서 운영되며 서버와 클라우드 기반 서비스 또는 native 모바일 데스크탑 app 을 기준으로 개발이 가능합니다. 
 
이를 사용하기 위해서는 아래의 3가지 조건을 만족해야 합니다
 
첫번째 ; .net core 2.1 preview 1 sdk 설치 (https://www.microsoft.com/net/download/dotnet-core/sdk-2.1.300-preview1)
두번째 : visual studio 2017 (15.7) 최신 preview버전 (https://www.visualstudio.com/ko/vs/preview/)
세번째 : asp.net core blazor language services extension 설치 (https://marketplace.visualstudio.com/items?itemName=aspnet.blazor)
 
 
전부 설치가 완료 되었으면 visual studio 를 실행합니다.
.net core 중에 asp.net core 2.0 을 선택하면 blazor 템플릿을 제공해 줍니다.
 
 
ok  버튼을 누르고 나서 f5 버튼을 누르면 웹화면에서 실행 화면을 보여 줍니다.
 
visual studio 를 설치 하지 않고 command  으로 간단히 생성 하여 실행 가능합니다.
 
dotnet new -i Microsoft.AspNetCore.Blazor.Templates
dotnet new blazor -o BlazorApp1
cd BlazorApp1
dotnet run
 
 
실행하면 아래 처럼 부트스트랩 형태로 메뉴와 컨텐츠 형태를 구성하여 제공합니다.
페이지는 home , counter, fectch data 로 3가지 제공합니다.
 
 
이는 page folder 에 Index.cshtml, Counter.cshtml, FetchData.cshtml  파일 형태로 구성되어 있습니다.
 
 
 
새로운 blazor view 를 생성하면 어떤 형태의 html 이 구성될까요?
 
 
 
1. @page "/calculator"  
2.   
3. <h1>Basic Calculator Demo Using Blazor</h1>  
4. <hr />  
5. <div>  
6.     <div class="row">  
7.         <div class="col-sm-3">  
8.             <p>First Number</p>  
9.         </div>  
10.         <div class="col-sm-4">  
11.             <input placeholder="Enter First Number" @bind(num1)>  
12.         </div>  
13.     </div>  
14.     <br />  
15.     <div class="row">  
16.         <div class="col-sm-3">  
17.             <p>Second Number</p>  
18.         </div>  
19.         <div class="col-sm-4">  
20.             <input placeholder="Enter Second Number" @bind(num2)>  
21.         </div>  
22.     </div>  
23.     <br />  
24.     <div class="row">  
25.         <div class="col-sm-3">  
26.             <p>Result</p>  
27.         </div>  
28.         <div class="col-sm-4">  
29.             <input readonly @bind(finalresult)>  
30.         </div>  
31.     </div>  
32.     <br />  
33.     <div class="row">  
34.         <div class="col-sm-2">  
35.             <button @onclick(AddNumbers) class="btn">Add (+)</button>  
36.         </div>  
37.         <div class="col-sm-2">  
38.             <button @onclick(SubtractNumbers) class="btn btn-primary">Subtract (−)</button>  
39.         </div>  
40.         <div class="col-sm-2">  
41.             <button @onclick(MultiplyNumbers) class="btn btn-success ">Multiply (X)</button>  
42.         </div>  
43.         <div class="col-sm-2">  
44.             <button @onclick(DivideNumbers) class="btn btn-info">Divide (X)</button>  
45.         </div>  
46.     </div>  
47. </div>  
48.   
49. @functions {  
50.   
51. string num1;  
52. string num2;  
53. string finalresult;  
54.   
55. void AddNumbers()  
56. {  
57.     finalresult = (Convert.ToDouble(num1) + Convert.ToDouble(num2)).ToString();  
58. }  
59.   
60. void SubtractNumbers()  
61. {  
62.     finalresult = (Convert.ToDouble(num1) - Convert.ToDouble(num2)).ToString();  
63. }  
64.   
65. void MultiplyNumbers()  
66. {  
67.     finalresult = (Convert.ToDouble(num1) * Convert.ToDouble(num2)).ToString();  
68. }  
69.   
70. void DivideNumbers()  
71. {  
72.     if (Convert.ToDouble(num2) != 0)  
73.     {  
74.         finalresult = (Convert.ToDouble(num1) / Convert.ToDouble(num2)).ToString();  
75.     }  
76.   
77.     else  
78.     {  
79.         finalresult = "Cannot Divide by Zero";  
80.     }  
81. }  
82. }  
 
 
각각의 파일에는 blazor 컴포넌트로 개발되어 있으며, home 페이지만 오직 static markup 이 포함되어 있습니다.
counter 나 fetch data 페이지는 c# 로직이 포함되어 있으며, 브라우저에서 클라이언트 단이 실행되고 컴파일이 됩니다.
 
counter 페이지는 버튼을 클릭할 때마다 페이지 새로고침 없이 카운트 개수가 증가됩니다.
 
 
일반적으로 클라이언트 단 behavior 의 종류들은 javascript 에서 핸들링되며, Counter 컴포넌트에 의해 .net 과 c# 으로 구현됩니다. 
 
 
@page "/counter"
<h1>Counter</h1>
 
<p>Current count: @currentCount</p>
 
<button @onclick(IncrementCount)>Click me</button>
 
@functions {
    int currentCount = 0;
 
    void IncrementCount()
    {
        currentCount++;
    }
}
 
 
cshtml 인 razor 파일은 blazor 컴포넌트로 구성되어 있으며, blazor 컴포넌트는 web ui 로 재사용이 가능한 .net class 로 되어 있습니다. counter 컴포넌트 의 ui 는 일반적인 html 형태로 구성되어 있고 동적 랜더링 로직 즉, loop , 조건문, 표현식 등등 이런 것은 razor 구문을 사용하여 추가되어 있습니다.
html markup 과 랜더링된 로직은 빌드 시점에 컴포넌트 class 안에 convert 됩니다. generate 된 .net 클래스의 이름은 file 이름과 매칭 됩니다.
 
컴포넌트 class 의 맴버들은 @function 블럭 형태로 구성되어 있으며, @functions 에 component 의 상태 (속성, 필드들) 이 명시할 수 있습니다. 또한 이벤트 핸들러에 대한 메소드나 또 다른 컴포넌트 로직을 정리할 수 도 있습니다. 이러한 맴버들은 컴포넌트의 랜더링 로직 그리고 이벤트 핸들링의 일부분으로 사용이 가능합니다.
 
note : single razor 파일에 정의된 컴포넌트들은 typical 즉 정형화 되어야 하며, 향후 update 버전에선 코드 바하인드 파일에 컴포넌트 로직의 define 이 가능 할 거라고 합니다.
 
component 에서 이벤트가 발생할 때마다 (Counter component 의 onclick 이벤트와 같은) 해당 component 는 해당 render tree 를 재생성합니다. Blazor는 새로운 render tree 를 이전의 render tree 와 비교하여 브라우저 DOM에 수정 사항을 적용합니다.
 

Routing to components

 
Counter.cshtml 파일 맨 위에있는 @page 지시문은이 구성 요소가 요청을 라우팅 할 수있는 페이지임을 지정합니다. 특히 Counter 구성 요소는 / counter로 전송 된 요청을 처리합니다. @page 지시어가 없으면 컴포넌트는 라우트 된 요청을 처리하지 못하지만 다른 컴포넌트가 사용할 수 있습니다.
 
특정 구성 요소에 대한 요청 라우팅은 App.cshtml의 루트 App 구성 요소에서 사용하는 Router 구성 요소에 의해 처리됩니다.
 
 
<!--
Configuring this here is temporary. Later we'll move the app config
into Program.cs, and it won't be necessary to specify AppAssembly.
-->
 
<Router AppAssembly=typeof(Program).Assembly />
 
 

Using components

 
구성 요소를 정의하면 다른 구성 요소를 구현하는 데 사용할 수 있습니다. 예를 들어 app 의 home 페이지에 Counter component 를 추가 할 수 있습니다.
 
@page "/"
 
<h1>Hello, world!</h1>
 
Welcome to your new app.
 
<SurveyPrompt Title="How is Blazor working for you?" />
 
<Counter />
 
app 을  다시 빌드하고 실행하면 (곧 다시로드됩니다!) home 페이지에  Counter component 인스턴스가 별도로 표시됩니다.
 
 
 

Component parameters

 
 
Components 에는 Components 클래스의public 속성을 사용하여 정의하는 매개 변수가 있을 수도 있습니다. 
Counter component  를 업데이트하여 IncrementAmount 속성이 기본적으로 1이지만 다른 것으로 변경할 수 있도록 해보죠.
 
@page "/counter"
<h1>Counter</h1>

<p>Current count: @currentCount</p>
<button @onclick(IncrementCount)>Click me</button>

@functions {
  int currentCount = 0; 
  
  [Parameter]
  public int IncrementAmount { get; set; } = 1;
  
  void IncrementCount()
  {
     currentCount += IncrementAmount;
   }
}
 
component parameters  는 component  태그의 속성으로 설정할 수 있습니다. home 페이지에서 counter 증가량을 10 으로 변경하십시오.
 
@page "/"
 
<h1>Hello, world!</h1>
 
Welcome to your new app.
 
<Counter IncrementAmount="10" />
 
app 을 만들고 실행할 때 Counter 페이지가 여전히 1 씩 증가하는 반면, Home 페이지의 카운터는 10 씩 증가합니다.
 
 

Layouts


app 의 레이아웃은 Pages 폴더의 _ViewImports.cshtml에서 @layout 지시문을 사용하여 지정됩니다.
 
@layout MainLayout
 
Blazor의 레이아웃 또한 component 로 구축됩니다. 우리의 app에서는 Shared / MainLayout.cshtml의 MainLayout 구성 요소가 app 레이아웃을 정의합니다.
 
@implements ILayoutComponent
 
<div class='container-fluid'>
  <div class='row'>
    <div class='col-sm-3'>
      <NavMenu />
    </div>
    <div class='col-sm-9'>
      @Body
    </div>
  </div>
</div>
 
@functions {
  public RenderFragment Body { get; set; }
}
 
레이아웃 구성 요소는 ILayoutComponent를 구현합니다. Razor 구문 인터페이스는 @implements 지시어를 사용하여 구현할 수 있습니다. ILayoutComponent 인터페이스의 Body 속성은 레이아웃 구성 요소에서 body 내용을 렌더링 할 위치를 지정하는 데 사용됩니다. app 에서 MainLayout 구성 요소는 NavMenu 구성 요소를 추가 한 다음, body을 페이지의 기본 섹션에 렌더링합니다.
 
NavMenu 구성 요소는 Shared / NavMenu.cshtml에 구현되고 부트 스트랩 nav bar 을 만듭니다. nav 의 링크는 기본 제공 NavLink 구성 요소를 사용하여 생성됩니다.이 구성 요소는 현재 페이지가 지정된 href와 일치하는 경우 활성 CSS 클래스가 있는 anchor(앵커) 태그를 생성합니다.
 

The root component

 
app 의 root  component  는 Program.cs 에 정의 된 app 의 Program.Main 진입 점에 지정됩니다.
또한 app 의 service provider services  를 구성 configure (환경설정)을 하는 곳이기도 합니다.
 
class Program
{
  static void Main(string[] args)
  {
    var serviceProvider = new BrowserServiceProvider(configure =>
    {
      // Add any custom services here
    });
 
    new BrowserRenderer(serviceProvider).AddComponent<App>("app");
  }
}
 
DOM 의 selector argument 는 root  구성 요소가 렌더링되는 위치를 결정합니다. 이 경우 index.html 의 app element 가 사용됩니다.
 
<!DOCTYPE html><html><head>
<meta charset="utf-8" />
<title>BlazorApp1</title>
<base href="/" />
<link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
<link href="css/site.css" rel="stylesheet" /></head><body>

<app>Loading...</app> <!--Root component goes here-->
 
<script src="css/bootstrap/bootstrap-native.min.js"></script>
<script type="blazor-boot"></script></body></html>
 


Bootstrapping the runtime

 
 
blazor-boot 스크립트 태그가 필드된 시점에 .NET runtime 이 시작되고 app 진입 점에서 실행 시, 이는 부트 스트랩 스크립트로 대체(변환)이 됩니다.
브라우저 개발자 도구를 사용해서 보면, 대체 된 스크립트 태그를 볼 수 있습니다.

<script src="_framework/blazor.js" main="BlazorApp1.dll" entrypoint="BlazorApp1.Program::Main" references="Microsoft.AspNetCore.Blazor.dll,netstandard.dll,..."></script>
 
Blazor 를 빌드된 시점에 참조 된 어셈블리에서 사용중인 code paths 를 분석 한 다음, 사용하지 않은 어셈블리와 코드는 제거 합니다.
 

Dependency injection

 
app 의 service provider  에 등록 된 서비스는 dependency injection 을 통해 components 에서 사용할 수 있습니다.
constructor injection 또는 @inject 지시문을 사용하여 구성 요소에 서비스를 주입 할 수 있습니다. 후자는 HttpClient가 FetchData 구성 요소에 주입되는 방법입니다.
 
@page "/fetchdata"@inject HttpClient Http
 
FetchData 구성 요소는 삽입 된 HttpClient를 사용하여 구성 요소가 초기화 될 때 서버에서 일부 JSON을 검색합니다.
 
@functions {
WeatherForecast[] forecasts;
 
protected override async Task OnInitAsync()
{
    forecasts = await Http.GetJsonAsync<WeatherForecast[]>("/sample-data/weather.json");
}
 
class WeatherForecast
{
  public DateTime Date { get; set; }
  public int TemperatureC { get; set; }
  public int TemperatureF { get; set; }
  public string Summary { get; set; }
}
 
}
 
데이터는 WeatherForecast 개체의 배열로 forecasts 에 C# 변수로 deserialize 된 다음, weather 테이블을 렌더링하는 데 사용됩니다.
 
 

Hosting with ASP.NET Core

 
 
Blazor 템플릿은 백 엔드와 함께 사용할 수 있는 클라이언트 측 웹 응용 프로그램을 만듭니다. ASP.NET Core로 Blazor 응용 프로그램을 hosting 하면 .NET으로 전체 stack web 개발을 수행 할 수 있습니다.
Blazor (ASP.NET Core Hosted) 프로젝트 템플릿은 Blazor 클라이언트 프로젝트, ASP.NET 핵심 서버 프로젝트 및 공유 .NET 표준 클래스 라이브러리 프로젝트의 세 가지 프로젝트로 솔루션을 만듭니다. ASP.NET Core 에서 Blazor 앱을 hosting 하려면 server project 에 client project 에 대한 reference 가 있어야합니다. Blazor-specific middleware 는 Blazor 클라이언트 어플리케이션을 브라우저에서 사용할 수 있도록 합니다.
 
 
독립형 Blazor 앱에서  weather forecast data 는 정적 JSON 파일 이었지만, hosting 된 프로젝트에서 SampleDataController는 ASP.NET Core를 사용하여 weather  데이터를 제공합니다.
 
shared class library 프로젝트는 Blazor 클라이언트 프로젝트와 ASP.NET Core 서버 프로젝트 모두에서 참조되므로 두 프로젝트간에 코드를 공유 할 수 있습니다. 이는 도메인 유형을 배치하기에 좋은 장소입니다. 예를 들어, WeatherForecast 클래스는 ASP.NET Core 프로젝트에서 직렬화하고 Blazor 프로젝트에서 역직렬화 해서 이를 web api 를 통해 사용됩니다.
 

Build a todo list

 
간단한 todo list 을 구현하는 새 페이지를 app 에 추가합시다. 프로젝트에 Pages / Todo.cshtml 파일을 추가 합니다.. 
Visual Studio 에 Blazor component item template 이 아직 없지만 Razor View item template 를 대신 사용할 수 있습니다. 파일의 내용을 초기 마크 업으로 바꿉니다.
 
@page "/todo"
 
<h1>Todo</h1>
 
 
Shared/Navbar.cshtml을 업데이트하여 todo list 에 대한 nav link 를 추가합니다.
 
<li>
  <NavLink href="/todo">
  <span class='glyphicon glyphicon-th-list'></span> Todo
  </NavLink>
</li>
 
앱을 빌드하고 실행하십시오. 새로운 To Do 페이지가 나타납니다.
 
 
todo item 에 대한 클래스를 생성합니다.
 
public class TodoItem
{
  public string Title { get; set; }
  public bool IsDone { get; set; }
}
 
Todo 구성 요소는 todo list 의 상태를 유지 관리합니다. Todo component 에서 @functions 블록의 todos 필드와 foreach 루프를 추가하여 필드를 렌더링하십시오.
 
 
 
이제 todos 를 목록에 추가하기 위한 UI가 필요합니다. 텍스트 입력 및 버튼을 목록 하단에 추가하십시오.
 
 
우리가 이벤트 핸들러를 연결하지 않았기 때문에 " Add todo "버튼을 클릭하면 아무런 변화가 없습니다.
AddTodo 메서드를 Todo 구성 요소에 추가하고 @onclick 특성을 사용하여 button click 에 등록합니다.
 
 
버튼을 클릭 할 때마다 AddTodo 메서드가 호출됩니다. 새로운 todo 항목의 제목을 얻으려면 newTodo 문자열 필드를 추가하고 @bind 속성을 사용하여 텍스트 입력 값에 bind 합니다.
이는  two-way 즉 , 양방향 bind를 설정합니다. 이제 AddTodo 메서드를 업데이트하여 지정된 제목이있는 TodoItem을 목록에 추가 할 수 있습니다. newTodo를 빈 문자열로 설정하여 텍스트 입력의 값을 지우는 것을 잊지 말자~
 
 
 
마지막으로 체크 박스가 없는 할 일 목록은 무엇입니까? 그리고 그 동안 우리는 편집 할 수있는 각 할일 항목의 제목 텍스트를 만들 수 있습니다. 각 할 일 목록 항목에 대한 확인란 입력 및 텍스트 입력을 추가하고 각각의 값을 Title 및 IsDone 등록 정보에 bind 합니다..
이러한 값이 바인딩되는지 확인하려면, 아직 완료되지 않은 todo item 의 수를 header 에 추가합니다.
 
 
app 을 실행하고 약간의 todo item 을 추가해 보죠.
 
 
 

Publishing and deployment

 
이제 Azure에 Blazor 앱을 게시 해 보겠습니다. 프로젝트를 마우스 오른쪽 단추로 클릭하고 게시를 선택합니다 (ASP.NET Core 가 hosting 된 템플릿을 사용하는 경우 server project 로 게시해야합니다). "게시 대상 선택"대화 상자에서 "응용 프로그램 서비스"및 "새로 만들기"를 선택하십시오.
 
 
 
"app service 생성" 대화 상자에서 app name 을 선택하고 사용할 subscription, resource Group, 및 hosting Plan 을 선택하십시오.
'Create' 를 탭하여 App Service 를 만들고 App 을 게시합니다.
 
 
아래 화면은 azure 에 배포한 내용이 실행된 것을 브라우저에서 볼 수 있습니다. 
 
 
참고로 blazor 샘플  app 은 해당 github 에서 얻을 수 있습니다. (https://github.com/aspnet/samples/tree/master/samples/aspnetcore/blazor)
 
 

Summary


이것은 Blazor의 첫 번째 프리뷰 버전입니다. 이미 .NET을 사용하여 브라우저에서 실행되는 구성 요소 기반 웹 응용 프로그램을 구축 할 수 있습니다. Blazor를 사용해 보시고 지금까지의 경험에 대해 어떻게 생각하는지 알려 주시기 바랍니다. Github에서 문제를 신고하고, 제품 내 설문 조사를 실시하고, Gitter에서 채팅 할 수 있습니다. 그러나 이것은 시작에 불과합니다! 향후 업데이트를 위해 많은 새로운 기능과 개선 사항을 계획하고 있습니다. 이 첫 번째 릴리스를 사용해 보는 것이 즐겁기를 바랍니다. 가까운 시일 내에 새로운 개선 사항을 공유하기를 기대합니다.