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