일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 한빛미디어
- codeup
- DataScience
- 생성형AI
- 빅데이터
- gcp
- 코드업100제
- GenAI
- 알고리즘
- 코드업파이썬
- 파이썬기초100제
- 블레이저
- Python
- C#
- nlp
- 코드업
- Azure
- 파이썬기초
- 머신러닝
- 클라우드
- 파이썬
- attention
- 파이썬알고리즘
- Microsoft
- 구글퀵랩
- GenerativeAI
- 데이터사이언스
- Blazor
- 데이터분석
- 자연어처리
- Today
- Total
Tech for good
[Blazor & C# 핸즈온] Blazor 웹 앱에서 데이터와 상호작용 본문
https://docs.microsoft.com/ko-kr/learn/modules/interact-with-data-blazor-web-apps/
위의 모듈을 공부하며 정리한 자료입니다.
(Tip! 피자 앱을 만들기 위해서는
목차에 '[연습] - '이라고 되어있는 모듈만 따라하시면 됩니다.)
학습목표
- Blazor 컴포넌트를 만들어 웹앱용 사용자 인터페이스를 어셈블리한다.
- 웹 앱에 표시할 데이터에 액세스한다.
- 여러 Blazor 컴포넌트 간에 웹 앱의 데이터를 공유한다.
- HTML 요소를 Blazor 컴포넌트의 변수에 바인딩한다.
1. 소개
Blazor는 클라이언트 측 JavaScript 라이브러리를 관리하는 복잡성 없이 서버 측과 클라이언트 측 모두에서 앱 논리를 공유할 수 있는 .NET을 사용하여 대화형 웹 응용 프로그램을 만든다.
당신은 피자 배달 회사에 개발자로 고용되었으며, C#을 통해 피자 배달 주문 사이트를 구축하려고 한다.
이 모듈에서는 Blazor 컴포넌트를 활용하여 동적 데이터를 표현하는 사용자 인터페이스를 만드는 방법에 대해 알아볼 것이다.
2. Blazor 컴포넌트를 사용하여 사용자 인터페이스 만들기
Blazor 컴포넌트를 사용하면 .NET 코드(C#)를 사용하여 동적 콘텐츠를 포함하는 HTML의 일부 또는 웹 페이지를 정의할 수 있다.
2.1. Blazor 컴포넌트 이해
Blazor는 개발자가 C# 코드를 작성하여 풍부한 대화형 UI(사용자 인터페이스)를 만드는 데 사용할 수 있는 프레임워크이다. Blazor를 사용하면 서버 측과 클라이언트 측 모두에서 모든 코드에 동일한 언어를 사용하고 모바일 장치의 브라우저를 비롯한 다양한 브라우저에서 표시하도록 렌더링할 수 있다.
Blazor 앱에는 두 가지 코드 호스팅 모델이 있다.
- 블레이저 서버(Blazor Server)
: 이 모델에서 앱은 ASP.NET Core 앱 내의 웹 서버에서 실행된다. 클라이언트 측의 UI 업데이터, 이벤트 및 JavaScript 호출은 클라이언트와 서버 간의 SingalR 연결을 통해 전송된다.
- 블레이저 웹어셈블리(Blazor WebAssembly)
: 이 모델에서 Blazor 앱, 해당 종속성 및 .NET 런타임이 다운로드되어 브라우저에서 실행된다.
Blazor에서는 컴포넌트를 통해 코드의 자체 포함 부분에서 UI를 빌드한다. 각 컴포넌트는 HTML과 C# 코드를 혼합하여 포함할 수 있다. 컴포넌트는 Razor 구문(i.g @code지시문, 다른 지시문 ..)을 통해 변수에 액세스하고, 값에 바인딩하며 다른 렌더링 작업을 수행할 수 있다. 앱이 컴파일되면 HTML과 코드가 컴포넌트 클래스로 컴파일되며, 컴포넌트 확장자는 .razor 파일이 된다.
// Blazor 컴포넌트 예시 -> .razor
@page "/index"
<h1>Welcome to Blazing Pizza</h1>
<p>@welcomeMessage</p>
@code {
private string welcomeMessage = "However you like your pizzas, we can deliver them blazing fast!";
}
2.2. Blazor 컴포넌트 만들기
1) terminal(cmd)를 실행하여 아래의 명령을 작성한다.
// 터미널 실행
dotnet new blazorserver -o BlazingPizzaSite -f net6.0
2) 기존 웹 앱에 새 컴포넌트를 추가하기 위해 다음 명령을 사용한다.
// 터미널 실행
dotnet new razorcomponent -n PizzaBrowser -o Pages -f net6.0
- -n 다음에 오는 텍스트 -> 컴포넌트 이름
- 해당 명령을 실행하면 PizzaBrowser.razor 라는 새 파일이 추가된다.
- -o 다음에 오는 텍스트 -> 폴더 이름
- -f 다음에 오는 텍스트 -> 프레임워크 버전
3) Visual Studio Code를 사용하여 편집할 수 있다.
// 터미널 실행
code Pages/PizzaBrowser
2.3. Blazor 컴포넌트에서 코드 작성
Blazor에서 사용자 인터페이스를 빌드할 때 정적 HTML 및 CSS 마크업을 C# 코드와 함께 종종 동일한 파일에서 혼합한다. 이러한 유형의 코드를 구별하기 위해 Razor 구문을 사용한다. Razor 구문에는 @C#코드, 라우팅 매개 변수, 바인딩된 데이터, 가져온 클래스 및 기타 기능을 구분하는 기호가 접두사로 붙는다.
여기서 Tip :)
: 접두사@ 유무로 Blazor와 C#의 차이를 확인할 수 있다. Blazor 코드에서는 앞에 @가 붙는 반면, C# 코드에서는 @가 붙지 않는다.
- .razor 파일
- .cs 파일
// .razor 파일 예시
@page "/index"
<h1>Welcome to Blazing Pizza</h1>
<p>@welcomeMessage</p>
@code {
private string welcomeMessage = "However you like your pizzas, we can deliver them fast!";
}
그럼 다시 예제 컴포넌트를 살펴보자. <h1>과 <p>태그를 사용하여 HTML 마크업을 인식할 수 있다. 이 마크업은 코드가 동적 콘텐츠를 삽입할 페이지의 정적 프레임워크이다. Razor 마크업은 다음으로 구성된다.
- @page
- Blazor에 대한 경로 템플릿을 제공하는 지시어이다. 런타임 시 Blazor는 이 템플릿을 사용자가 요청한 URL과 일치시켜 렌더링할 페이지를 찾는다.
- 예를들어 @page "/index"의 경우, main url + /index 페이지를 렌더링할 수 있다.
- @code
- @code 다음 블록의 텍스트는 C# 코드여야한다. 여기에는 컴포넌트에 필요한 만큼의 코드 블록을 넣을 수 있다.
- 이 코드 블럭에서는 컴포넌트 클래스 구성원을 정의하고 계산, 데이터 조회 작업 또는 기타 소스에서 해당 값을 설정할 수 있다.
- 위의 코드 예시에서는 호출되는 단일 구성 요소 멤버를 정의하고 welcomeMessage에 해당 문자열 값을 설정한다.
- 멤버 액세스 지시문 -> 렌더링 논리에 멤버 값을 포함하기 위해서는 멤버 @이름 뒤에 C# 표현식을 사용한다. 이 경우, @welcomeMessage지시문은 <p> 태그의 welcomeMessage 멤버값을 렌더링하는데 사용된다.
3. [연습] Blazor 컴포넌트로 사용자 인터페이스 만들기
피자 배달 회사를 위한 새로운 Blazing Pizza 앱을 만들어보자!
3.1. 새 Blazor 앱 만들기
이 모듈에서는 .NET 6.0 SDK를 사용한다. 모듈을 시작하기 전 터미널에서 다음 명령을 실행하여 .NET 6.0이 설치되어있는지 확인한다.
// 터미널 실행 - .NET SDK 리스트 확인
dotnet --list-sdks
// 콘솔에 뜨는 내용 확인
// 버전 6이 나열 되는지 확인하고, 찾을 수 없으면 .NET 6.0 SDK를 설치
3.1.100 [C:\program files\dotnet\sdk]
5.0.100 [C:\program files\dotnet\sdk]
6.0.100 [C:\program files\dotnet\sdk]
그 다음, 터미널에 아래의 명령을 실행하여 새 프로젝트를 생성한다.
// 터미널 실행 - BlazingPizza라는 이름의 폴더에 새로운 Blazor 서버 프로젝트를 생성 (HTTPS 해제)
dotnet new blazorserver -o BlazingPizza --no-https true -f net6.0
그 후, BlazingPizza 폴더를 열고 아래와 같은 메시지가 표시되면 예(Yes)를 선택한다.
그 다음, 프로젝트의 .vscode 폴더에 launch.json 및 tasks.json을 추가한다.
3.2. 설정 테스트
i) 앱 빌드 및 실행 첫번째 방법
1. 터미널 창에서 아래의 명령을 사용하여 Blazor 앱을 시작한다.
// 터미널 실행 - 앱 빌드 및 실행 & 모든 프로젝트 파일 보기
dotnet watch
컴퓨터의 기본 브라우저는 http://localhost:5000 에서 새 페이지를 열어야 한다.
2. 앱 실행을 중지하려면 터미널 창에서 Ctrl + C를 누른다.
ii) 앱 빌드 및 실행 두번째 방법
1. Visual Studio Code에서 F5를 누르거나 실행 - 디버깅 시작을 선택한다.
2. 앱이 빌드되고 새 브라우저 페이지가 열린다.
3. Shift + F5를 눌러 앱 실행을 중지한다.
3.3. Blazing Pizza 자산 및 스타터 파일 다운로드
이제부터 본격적인 시작이다! GitHub 리포지토리 https://github.com/MicrosoftDocs/mslearn-interact-with-data-blazor-web-apps.git 에서 클론을 진행해야 한다.
1. 파일 탐색기 또는 VS Code에서 기존의 BlazingPizza 폴더를 삭제한다.
2. 터미널에서 아래의 명령을 진행한다. 현재 작업 파일을 새 BlazingPizza 폴더에 복제하는 과정이다.
// 터미널 실행
git clone https://github.com/MicrosoftDocs/mslearn-interact-with-data-blazor-web-apps.git BlazingPizza
3. 클론이 완료되면 F5를 눌러 앱을 실행한다.
3.4. Pizzas 만들기
index.razor 컴포넌트에는 고객이 선택하고 주문하고 싶은 피자를 구성할 것이다. 컴포넌트는 앱의 루트 URL에 응답한다.
1) Visual Studio Code를 열면, Model 폴더가 있다. 거기서 PizzaSpecial를 선택한다.
// Model > PizzaSpecial.cs
namespace BlazingPizza
{
/// <summary>
/// Represents a pre-configured template for a pizza a user can order
/// </summary>
// PizzaSpecial은 Id, Name, BasePrice, Desciption, ImageUrl 등을 갖는다.
public class PizzaSpecial
{
public int Id { get; set; }
public string Name { get; set; }
public decimal BasePrice { get; set; }
public string Description { get; set; }
public string ImageUrl { get; set; }
public string GetFormattedBasePrice() => BasePrice.ToString("0.00");
}
}
namespace & class 비교
2) Pages 폴더에서 Index.razor를 선택한다.
// Pages > Index.razor
@page "/"
<h1>Blazing Pizzas</h1>
현재 제목에 대한 단일 H1 태그만 있다. 이제 몇 가지 코드를 더 추가하여 피자 스페셜을 만들 것이다.
3) <h1> 태그 아래에 이 C# 코드를 추가한다.
// Pages > Index.razor
@code {
List<PizzaSpecial> specials = new();
protected override void OnInitialized()
{
specials.AddRange(new List<PizzaSpecial>
{
new PizzaSpecial { Name = "The Baconatorizor", BasePrice = 11.99M, Description = "It has EVERY kind of bacon", ImageUrl="img/pizzas/bacon.jpg"},
new PizzaSpecial { Name = "Buffalo chicken", BasePrice = 12.75M, Description = "Spicy chicken, hot sauce and bleu cheese, guaranteed to warm you up", ImageUrl="img/pizzas/meaty.jpg"},
new PizzaSpecial { Name = "Veggie Delight", BasePrice = 11.5M, Description = "It's like salad, but on a pizza", ImageUrl="img/pizzas/salad.jpg"},
new PizzaSpecial { Name = "Margherita", BasePrice = 9.99M, Description = "Traditional Italian pizza with tomatoes and basil", ImageUrl="img/pizzas/margherita.jpg"},
new PizzaSpecial { Name = "Basic Cheese Pizza", BasePrice = 11.99M, Description = "It's cheesy and delicious. Why wouldn't you want one?", ImageUrl="img/pizzas/cheese.jpg"},
new PizzaSpecial { Name = "Classic pepperoni", BasePrice = 10.5M, Description = "It's the pizza you grew up with, but Blazing hot!", ImageUrl="img/pizzas/pepperoni.jpg" }
});
}
}
@code 블록은 피자 스페셜을 보유하는 배열을 생성한다. 페이지가 초기화되면 배열에 6개의 피자가 추가된다.
4) F5를 눌러 디버깅을 시작한다. 변동사항은 없을 것이다.
5) Shift + F5를 눌러 디버깅 중지를 선택한다.
6) index.razor 파일의 <h1> 태그가 있는 코드를 아래와 같이 변경한다.
// Pages > index.razor
<div class="main">
<h1>Blazing Pizzas</h1>
<ul class="pizza-cards">
@if (specials != null)
{
@foreach (var special in specials)
{
<li style="background-image: url('@special.ImageUrl')">
<div class="pizza-info">
<span class="title">@special.Name</span>
@special.Description
<span class="price">@special.GetFormattedBasePrice()</span>
</div>
</li>
}
}
</ul>
</div>
해당 코드는 foreach 반복문 및 멤버 접근 지시문과 일반 HTML 코드를 결합하고 있다. @foreach 구문이 <li> 태그로 각 피자 specials의 배열을 생성한다. 루프 내에서 각 special 피자들은 멤버 지시문과 함께 이름, 설명, 가격 및 이미지를 표시한다.
7) F5를 눌러 디버깅을 시작하면, 고객이 피자를 주문할 수 있도록 하는 피자 컴포넌트를 확인할 수 있다.
C# 한정자
접근 한정자란 우리가 만드는 메서드를 호출할 수 있는 권한을 정해 놓는 것이다.
C#에서는 한정자가 없다면 자동으로 private로 지정된다.
public: 공개(누구나 접근하여 사용)
protected: 일부 공개(자신이 만들어진 class 또는 이를 상속 받는 하위 class에서만 사용)
private: 비공개(자신의 class에서만 사용)
C# 메소드
- 메소드 선언
class 클래스 이름
{
[한정자] [반환 형식] [메소드 이름] ( [매개 변수] )
{
// 실행하고자 하는 코드 1
// 실행하고자 하는 코드 2
// ...
// 실행하고자 하는 코드 n
return [반환 형식 결과];
}
}
- 메소드 정의 및 호출
class Calculator
{
public int Plus(int a, int b)
{
Console.WriteLine("Input : {0}, {1}", a, b);
int result = a + b;
return result;
}
}
void, int, string, class... 등 해당 메서드의 데이터 반환 형식을 지정할 수 있다.
반환 형식은 선언된 반환 형식과 동일하다.
void: 아무것도 반환할 게 없는 경우. return문도 필요하지 않다.
int: 코드를 실행하고 마지막에 return문으로 int 형식의 반환값을 보낸다.
4. Blazor 컴포넌트에서 데이터 액세스
웹사이트는 항상 변경될 수 있는 동적 콘텐츠를 표시해야 한다. 따라서 데이터베이스나 웹 서비스와 같은 동적 소스에서 데이터를 얻어야만한다. 피자 배달 회사가 취급하고 있는 배달 주문 홈페이지를 예로 들어보자. Blazor 컴포넌트로 레이아웃 및 디자인 된 웹 페이지가 존재하면, 데이터베이스로부터 원하는 피자 토핑 및 주문 정보로 해당 페이지를 채워야한다. 그럼 이제 데이터에 액세스하고 사용자에게 표시하기 위해 HTML 마크업 내에서 렌더링하는 방법을 배워보자.
4.1. 등록된 데이터 서비스 생성
사용자에게 변화하는 정보를 보여주는 동적 웹사이트를 만들려면 어딘가에서 해당 데이터를 가져오는 코드를 작성해야 한다. 예를 들어 회사에서 판매하는 모든 피자를 저장하는 데이터베이스가 있다고 가정해보자. 피자는 항상 변하기 때문에 웹사이트 HTML에 하드코딩하는 것은 좋지 않다. 대신 C# 코드와 Blazor를 사용하여 데이터베이스를 쿼리한 다음, 사용자가 즐겨찾기를 선택할 수 있도록 세부 정보를 HTML로 형식을 지정한다. 즉, Blazor 서버 앱에서 등록된 서비스를 만들어 데이터 원본을 나타내고 여기에서 데이터를 가져올 수 있다.
+ 참고:)
: Blazor 앱에서 사용할 수 있는 데이터 원본에는 관계형 데이터베이스, NoSQL 데이터베이스, 웹 서비스, 다양한 Azure 서비스 및 기타 여러 시스템이 포함된다. Entity Framework, HTTP 클라이언트, ODBC 등과 같은 .NET 기술을 사용하여 해당 소스를 쿼리할 수 있다.
// Data > Pizza.cs
// 중요 팁! 파일이름은 클래스 이름으로 맞추기!!
// '피자 속성 정의 클래스' 작성 → 등록된 데이터 서비스 생성
using System;
namespace BlazingPizza.Data
{
public class Pizza
{
public int PizzaId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public bool Vegetarian { get; set; }
public bool Vegan { get; set; }
}
}
이 클래스는 피자의 속성과 데이터 유형을 정의한다. 우리는 이러한 속성이 데이터 원본의 피자 스키마와 일치하는지 확인해야한다. 이를 위해서는 프로젝트에 Data 폴더를 추가적으로 만들어 Data라는 멤버 네임 스페이스를 사용해야한다.
다음으로는 PizzaService를 정의한다.
// Data > PizzaService.cs
using System;
using System.Threading.Tasks;
namespace BlazingPizza.Data
{
public class PizzaService
{
public Task<Pizza[]> GetPizzasAsync()
{
// Call your data access technology here
}
}
}
이 PizzaService는 비동기 호출을 사용하여 데이터에 액세스하고 Pizza 개체 컬렉션을 반환한다. 데이터 원본은 Blazor 코드가 실행되는 서버에서 멀리 떨어져 있을 수 있으므로 비동기 호출을 사용하여 데이터 원본이 느리게 응답하는 경우 응답을 기다리는 동안 다른 코드가 계속 실행될 수 있도록 하는 것이 좋다.
C# Task 클래스
Task클래스와 이의 Generic 형태인 Task<T>클래스는 .NET4.0에 도입된 새로운 클래스들로서 쓰레드풀로부터 쓰레드를 가져와 비동기 작업을 실행한다.
Task 관련 클래스들과 Parallel 클래스들을 합쳐 Task Parallel Library(TPL)라 부르는데, 이들은 기본적으로 다중 CPU 병렬처리를 염두에 두고 만들었다.
(출처: https://www.csharpstudy.com/Threads/task.aspx)
또한 Program.cs파일의 Add Services to the container 섹션에 다음과 같이 코드를 추가하여 서비스를 등록해야 한다.
// Program.cs
...
// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
// Register the pizzas service
builder.services.AddSingleton<PizzaService>();
...
4.2. 서비스를 사용하여 데이터 가져오기
다음으로 할 일은 Blazor 컴포넌트에서 서비스를 호출하여 데이터를 가져오는 것이다. 다음 컴포넌트가 있고 피자를 표시하려고 한다고 가정해보자.
// Pages > Pizzas.razor
@page "/pizzas"
<h1>Choose your pizza</a>
<p>We have all these delicious recipes:</p>
컴포넌트에서 서비스를 호출하려면 먼저 종속성 주입을 사용하여 서비스를 추가해야한다. 그렇게 하기 위해서는 @page 지시문 바로 뒤에 다음 코드를 추가한다.
4.2.1. 서비스 주입
// Pages > Pizzas.razor
@using Blazor.Data
@inject PizzaService PizzaSvc
일반적으로 컴포넌트와 서비스는 서로 다른 네임스페이스 멤버에 있으므로 @using 지시문을 포함해야 한다. @inject 지시어는 현재 컴포넌트에 대한 서비스를 추가하고 인스턴스를 시작한다. 지시문에서 서비스 클래스의 이름을 지정하고 그 다음 이 컴포넌트의 서비스 인스턴스에 사용할 이름을 지정한다.
4.2.2. OnInitializedAsync 메서드 재정의
서비스를 호출하고 데이터를 가져오기 위해서는 OnInitializedAsync 메서드를 사용하는 것이 좋다. 이 이벤트는 컴포넌트의 초기화가 완료되고 초기 매개변수를 받았지만 페이지가 렌더링되어 사용자에게 표시되기 전에 발생한다. 이벤트는 Blazor 컴포넌트의 기본 클래스에 정의되며 다음과 같이 코드 블록에서 재정의할 수 있다.
// Pages > Pizzas.razor
protected override async Task OnInitializedAsync()
{
\\ Call the service here
}
4.2.3. 서비스를 호출하여 데이터 가져오기
서비스를 호출할 때 비동기식인 await 호출을 사용한다.
// Pages > Pizzas.razor
private Pizza[] todaysPizzas;
protected override async Task OnInitializedAsync()
{
todaysPizzas = await PizzaSvc.GetPizzasAsync();
}
4.3. 사용자에게 데이터 표시하기
이제 서비스에서 일부 데이터를 얻었으므로 사용자에게 이것을 표시해야한다. 피자 예시에서 우리는 서비스가 사용자들이 선택할 수 있는 pizzas 목록을 반환할 것으로 기대한다. Blazor에는 사용자에게 표시되는 페이지에 이 데이터를 삽입할수 있는 풍부한 지시문 집합이 포함되어 있다.
4.3.1. 데이터 확인하기
먼저 피자가 로드되기 전에 페이지에 표시되는 내용을 결정해보자. todaysPizzas 컬렉션이 null인지 여부를 확인하여 이를 수행할 수 있다. Blazor 컴포넌트에서 조건부 렌더링 코드를 실행하기 위해서는 @if 지시문을 사용한다.
// Pages > Pizzas.razor
@if (todaysPizzas == null)
{
<p>We're finding out what pizzas are available today...</p>
}
else
{
<!-- This markup will be rendered once the pizzas are loaded -->
}
4.3.2. 개체 컬렉션 렌더링하기
위의 if ~ else 구문에서 else 부분에 아래의 코드를 삽입한다. 코드를 자세히 살펴보면, @foreach 지시문을 사용하여 todaysPizzas 컬렉션의 모든 개체를 반복하고 각 개체에 대한 행을 렌더링할 수 있다.
// Pages > Pizzas.razor
<table>
<thead>
<tr>
<th>Pizza Name</th>
<th>Description</th>
<th>Vegetarian?</th>
<th>Vegan?</th>
<th>Price</th>
</tr>
</thead>
<tbody>
@foreach (var pizza in todaysPizzas)
{
<tr>
<td>@pizza.Name</td>
<td>@pizza.Description</td>
<td>@pizza.Vegetarian</td>
<td>@pizza.Vegan</td>
<td>@pizza.Price</td>
</tr>
}
</tbody>
</table>
5. [연습] Blazor 컴포넌트에서 데이터 액세스
애플리케이션에 현재 하드코딩된 피자들을 데이터베이스로 교체해야한다. Microsoft Entity Framework를 사용하면 데이터 원본에 대한 연결을 추가할 수 있다. 앱에서 SQLite 데이터베이스를 사용하여 피자를 저장한다.
이 연습에서는 데이터베이스 기능을 지원하기 위해 패키지를 지원하고, 백엔드 데이터베이스에 클래스를 연결하고, 피자에 대한 데이터를 미리 로드하는 도우미 클래스를 추가한다.
5.1. 데이터베이스 액세스를 지원하는 패키지 추가
// 터미널 열어 아래의 명령 수행
dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
dotnet add package System.Net.Http.Json
위의 명령을 실행하면 BlazingPizza.csproj 파일에 아래의 코드가 추가된다.
// BlazingPizza.csproj
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.0" />
<PackageReference Include="System.Net.Http.Json" Version="6.0.0" />
</ItemGroup>
5.2. 데이터베이스 컨텍스트 및 컨트롤러 추가
1) VS Code에서 파일 - 새 파일을 선택한다.
2) 언어로 C#을 선택한다.
3) PizzaStoreContext 클래스에 대해 아래의 코드를 입력한다.
// 데이터베이스 컨텍스트 생성 - PizzaStoreContext.cs
using Microsoft.EntityFrameworkCore;
namespace BlazingPizza
{
public class PizzaStoreContext : DbContext
{
public PizzaStoreContext(
DbContextOptions options) : base(options)
{
}
public DbSet<PizzaSpecial> Specials { get; set; }
}
}
PizzaStoreContext 클래스는 데이터베이스 서비스를 등록하는 데 사용할 수 있는 데이터베이스 컨텍스트를 생성한다. 컨텍스트를 통해 데이터베이스에 액세스할 컨트롤러를 가질 수도 있다.
4) Ctrl + S를 눌러 위의 코드를 PizzaStoreContext.cs로 저장한다.
5) 파일 - 새 파일을 선택하여 새로운 파일을 하나 더 생성한다.
6) 이 파일도 마찬가지로 C# 언어를 선택한다.
7) SpecialController 클래스에 대해 아래의 코드를 입력한다.
// 컨트롤러 생성 - SpecialsController.cs
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace BlazingPizza
{
[Route("specials")]
[ApiController]
public class SpecialsController : Controller
{
private readonly PizzaStoreContext _db;
public SpecialsController(PizzaStoreContext db)
{
_db = db;
}
[HttpGet]
public async Task<ActionResult<List<PizzaSpecial>>> GetSpecials()
{
return (await _db.Specials.ToListAsync()).OrderByDescending(s => s.BasePrice).ToList();
}
}
}
이 클래스는 피자 스페셜에 대한 데이터베이스를 쿼리하고, http://localhost:5000/specials URL에서 JSON으로 반환할 수 있는 컨트롤러를 만든다.
8) Ctrl + S를 눌러 위의 코드를 SpecialsController.cs로 저장한다.
5.3. 데이터베이스에 데이터 로드
기존 SQLite 데이터베이스가 있는지 확인하고 미리 만들어진 피자로 데이터베이스를 만든다.
1) 파일 - 새 파일을 선택한다.
2) 언어로 C#을 선택한다.
3) SeedData 클래스에 대해 아래의 코드를 입력한다.
// SeedData.cs
namespace BlazingPizza
{
public static class SeedData
{
public static void Initialize(PizzaStoreContext db)
{
var specials = new PizzaSpecial[]
{
new PizzaSpecial()
{
Name = "Basic Cheese Pizza",
Description = "It's cheesy and delicious. Why wouldn't you want one?",
BasePrice = 9.99m,
ImageUrl = "img/pizzas/cheese.jpg",
},
new PizzaSpecial()
{
Id = 2,
Name = "The Baconatorizor",
Description = "It has EVERY kind of bacon",
BasePrice = 11.99m,
ImageUrl = "img/pizzas/bacon.jpg",
},
new PizzaSpecial()
{
Id = 3,
Name = "Classic pepperoni",
Description = "It's the pizza you grew up with, but Blazing hot!",
BasePrice = 10.50m,
ImageUrl = "img/pizzas/pepperoni.jpg",
},
new PizzaSpecial()
{
Id = 4,
Name = "Buffalo chicken",
Description = "Spicy chicken, hot sauce and bleu cheese, guaranteed to warm you up",
BasePrice = 12.75m,
ImageUrl = "img/pizzas/meaty.jpg",
},
new PizzaSpecial()
{
Id = 5,
Name = "Mushroom Lovers",
Description = "It has mushrooms. Isn't that obvious?",
BasePrice = 11.00m,
ImageUrl = "img/pizzas/mushroom.jpg",
},
new PizzaSpecial()
{
Id = 7,
Name = "Veggie Delight",
Description = "It's like salad, but on a pizza",
BasePrice = 11.50m,
ImageUrl = "img/pizzas/salad.jpg",
},
new PizzaSpecial()
{
Id = 8,
Name = "Margherita",
Description = "Traditional Italian pizza with tomatoes and basil",
BasePrice = 9.99m,
ImageUrl = "img/pizzas/margherita.jpg",
},
};
db.Specials.AddRange(specials);
db.SaveChanges();
}
}
}
클래스는 앞서 전달된 데이터베이스 컨텍스트를 사용하고, PizzaSpecial 배열에 일부 객체를 만든 다음 저장한다.
* 함께 보면 좋은 글
https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=myjet1490&logNo=221369557011
4) Ctrl + S를 눌러 위의 코드를 SeedData.cs로 저장한다.
5) 탐색기에서 Program.cs를 선택한다.
6) 상단에 새 패키지에 대한 참조를 추가한다.
// Program.cs
using Microsoft.Extensions.DependencyInjection;
위의 명령을 실행하면 앱에서 종속성 주입을 사용하여 새 서비스를 등록할 수 있다.
7) app.Run(); 메서드 바로 위에 아래의 코드를 삽입한다.
// Program.cs
// Initialize the database
var scopeFactory = app.Services.GetRequiredService<IServiceScopeFactory>();
using (var scope = scopeFactory.CreateScope())
{
// PizzaStoreContext 앞에 BlazingPizza.을 붙여야한다.
var db = scope.ServiceProvider.GetRequiredService<BlazingPizza.PizzaStoreContext>();
if (db.Database.EnsureCreated())
{
BlazingPizza.SeedData.Initialize(db);
}
}
app.Run();
해당 코드로 PizzaStoreContext라는 데이터베이스 범위가 생성되며, 데이터베이스가 생성되지 않는다면 SeedData 정적 클래스를 호출하여 생성한다. 현재로서는 PizzaStoreContext를 초기화하지 않았기 때문에 앱이 작동하지 않는다.
8) 현재 위치하고 있는 Program.cs 파일 상단을 보면 Add Services to the container 주석을 찾을 수 있다. 아래의 코드를 이 곳에 삽입한다.
// Program.cs
builder.services.AddHttpClient();
builder.services.AddDbContext<PizzaStoreContext>(options =>
options.UseSqlite("Data Source=pizza.db"));
위 코드는 두 개의 서비스를 등록한다. 첫번째 AddHttpClient 명령문은 앱이 HTTP 명령에 액세스하도록 허용하고 앱은 HttpClient를 사용하여 피자 스페셜에 대한 JSON을 가져온다. 두번째 명령문은 새로운 PizzaStoreContext를 등록하며 SQLite 데이터베이스에 파일 이름을 제공한다.
9) Visual Studio Code는 UseSqlite를 오류로 간주하므로, EntityFrameworkCore 패키지에 대한 참조를 추가해야 한다. 파일 상단의 기존 using 블록 아래에 다음을 추가한다.
// Program.cs
using Microsoft.EntityFrameworkCore;
5.4. 데이터베이스를 사용하여 피자 표시
이제 Index.razor 페이지로 가서 하드 코딩된 피자를 교체할 것이다.
1) 탐색기에서 Index.razor를 선택한다.
2) 기존 OnInitialized() 방법을 아래와 같이 바꾼다.
// Index.razor
protected override async Task OnInitializedAsync()
{
specials = await HttpClient.GetFromJsonAsync<List<PizzaSpecial>>(NavigationManager.BaseUri + "specials");
}
위의 코드는 기존의 OnInitialized()를 OnInitializedAsync()로 바꾼 것이다. Pizza Special은 이제 앱에서 비동기식으로 JSON으로 반환된다.
3) 그 다음 오류를 방지하기 위해 @page 지시문 아래에 @inject 명령문을 추가한다.
// Index.razor
@inject HttpClient HttpClient
@inject NavigationManager NavigationManager
4) 아래의 @using 명령문도 맨 아래에 추가한다.
// Index.razor
@using System.Net.Http.Json
5) F5를 눌러 디버깅을 시작한다. 하지만 앱을 실행할 때 런타임 오류가 있을 것이다.
6) 앱은 http://localhost:5000/specials 에서 JSON을 생성해야 한다. 그럼 이제 해당 URL로 이동해보자.
7) 아직 앱은 이 요청을 라우팅하는 방법을 모른다. 따라서 Blazor 라우팅 모듈에서 라우팅에 대해 학습해야한다.
8) Shift + F5를 눌러 디버깅 중지를 한다.
9) Program.cs 파일로 이동한다.
10) 파일의 하단, 즉 Configure the HTTP request pipeline 명령과 app.UseEndpoints 블록 아래에 해당 엔드포인트를 추가한다.
// Program.cs
app.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
코드는 아래와 같아진다.
// Program.cs
...
app.MapRazorPages();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
...
11) F5를 눌러 디버깅을 시작한다.
12) 이제 앱이 작동될 것이다. http://localhost:5000/specials 로 이동하여 JSON이 올바르게 생성되었는지 확인해보자.
JSON에는 special pizza controller에 지정된 대로 가격 내림차순으로 피자가 나열된 것을 확인할 수 있다.
(* 6단원은 생략합니다.)
7. [연습] - Blazor 애플리케이션에서 데이터 공유
이제 앱이 데이터베이스에 연결되었으므로 피자를 주문하고 구성하는 기능을 추가할 차례이다.
Blazing Pizza는 고객이 피자의 크기를 변경할 수 있는 기능을 구축하고자 한다. 따라서 주문을 저장해야하며 애플리케이션 상태를 컨테이너 서비스에 저장하도록 선택했다.
이 연습에서는 새 주문 구성 컴포넌트에 데이터를 전달하고 OrderState 범위 서비스에 앱의 상태를 저장하는 방법을 살펴본다.
7.1. 새 주문 구성 대화상자 추가
1) Visual Studio Code에서 Shared 폴더로 이동하여 우클릭 후, 새 파일을 선택한다.
2) ConfigurePizzaDialog.razor를 파일 이름으로 입력한다.
3) 해당 파일에 아래의 코드를 입력한다.
// Shared > ConfigurePizzaDialog.razor
@inject HttpClient HttpClient
<div class="dialog-container">
<div class="dialog">
<div class="dialog-title">
<h2>@Pizza.Special.Name</h2>
@Pizza.Special.Description
</div>
<form class="dialog-body">
<div>
<label>Size:</label>
<input type="range" min="@Pizza.MinimumSize" max="@Pizza.MaximumSize" step="1" />
<span class="size-label">
@(Pizza.Size)" (£@(Pizza.GetFormattedTotalPrice()))
</span>
</div>
</form>
<div class="dialog-buttons">
<button class="btn btn-secondary mr-auto" >Cancel</button>
<span class="mr-center">
Price: <span class="price">@(Pizza.GetFormattedTotalPrice())</span>
</span>
<button class="btn btn-success ml-auto" >Order ></button>
</div>
</div>
</div>
위의 컴포넌트는 선택한 피자를 표시하고 고객이 피자 크기를 선택할 수 있도록 하는 대화상자이다.
여기서는 피자의 멤버 값에 액세스하기 위해 인덱스 페이지 컴포넌트의 special pizza가 필요하다.
4) 해당 컴포넌트에 매개 변수를 전달할 수 있도록 아래의 @code 블록을 추가한다.
// Shared > ConfigurePizzaDialog.razor
@code {
[Parameter] public Pizza Pizza { get; set; }
}
7.2. 피자 주문하기
고객이 피자를 선택하면 대화상자에서 피자 크기를 변경할 수 있어야 한다. 이 상호 작용을 추가하기 위해 index.razor 컨트롤을 개선해보자.
1) 탐색기에서 Pages 폴더 - Index.razor 파일을 선택한다.
2) @code 블록 아래, 그 중에서도 List<PizzaSpecial> 변수 아래에 아래의 코드를 입력한다.
// Pages > Index.razor
Pizza configuringPizza;
bool showingConfigureDialog;
3) OnInitializedAsync() 메서드 아래에 피자를 만드는 코드를 추가한다.
// Pages > Index.razor
void ShowConfigurePizzaDialog(PizzaSpecial special)
{
configuringPizza = new Pizza()
{
Special = special,
SpecialId = special.Id,
Size = Pizza.DefaultSize
};
showingConfigureDialog = true;
}
4) 고객들이 피자를 선택할 수 있도록 만들기 위해 <li> 태그를 수정하여 웹 페이지에서 서버 측 메서드를 호출할 수 있도록 ShowConfigurePizzaDialog 메서드를 사용한다.
// Pages > Index.razor
<li @onclick="@(() => ShowConfigurePizzaDialog(special))" style="background-image: url('@special.ImageUrl')">
고객이 피자를 선택하면, 피자를 만들기 위한 서버 측의 ShowConfigurePizzaDialog 메서드가 실행이 된다. (이때 해당 메서드의 변수는 true값이다.)
5) 페이지에는 새 ConfigurePizzaDialog 컴포넌트를 표시할 방법이 필요하다. @code 블록 바로 위에 다음 코드를 추가한다.
// Pages > Index.razor
@if (showingConfigureDialog)
{
<ConfigurePizzaDialog Pizza="configuringPizza" />
}
이제 전체 Index.razor 파일이 다음과 같아진다.
// Pages > Index.razor
@page "/"
@inject HttpClient HttpClient
@inject NavigationManager NavigationManager
<div class="main">
<h1>Blazing Pizzas</h1>
<ul class="pizza-cards">
@if (specials != null)
{
@foreach (var special in specials)
{
<li @onclick="@(() => ShowConfigurePizzaDialog(special))" style="background-image: url('@special.ImageUrl')">
<div class="pizza-info">
<span class="title">@special.Name</span>
@special.Description
<span class="price">@special.GetFormattedBasePrice()</span>
</div>
</li>
}
}
</ul>
</div>
@if (showingConfigureDialog)
{
<ConfigurePizzaDialog Pizza="configuringPizza" />
}
@code {
List<PizzaSpecial> specials = new();
Pizza configuringPizza;
bool showingConfigureDialog;
protected override async Task OnInitializedAsync()
{
specials = await HttpClient.GetFromJsonAsync<List<PizzaSpecial>>(NavigationManager.BaseUri + "specials");
}
void ShowConfigurePizzaDialog(PizzaSpecial special)
{
configuringPizza = new Pizza()
{
Special = special,
SpecialId = special.Id,
Size = Pizza.DefaultSize
};
showingConfigureDialog = true;
}
}
6) 이제 F5를 눌러 디버깅을 시작한다.
7) 피자를 선택하고 새 대화상자가 나타나는지 확인한다.
7.3. 주문 상태 처리하기
현재 앱은 구성 대화 상자를 표시하지만 피자 주문을 취소하거나 주문 과정으로 이동할 수 없다. 주문 상태를 처리하기 위해 상태 컨테이너 서비스를 추가한다.
1) 파일 - 새 파일을 선택한다.
2) 언어로 C#을 선택한다.
3) 클래스에 대해 아래의 코드를 입력한다.
// OrderState.cs
using System.Collections.Generic;
namespace BlazingPizza
{
public class OrderState
{
public bool ShowingConfigureDialog { get; private set; }
public Pizza ConfiguringPizza { get; private set; }
public Order Order { get; private set; } = new Order();
public void ShowConfigurePizzaDialog(PizzaSpecial special)
{
ConfiguringPizza = new Pizza()
{
Special = special,
SpecialId = special.Id,
Size = Pizza.DefaultSize,
Toppings = new List<PizzaTopping>(),
};
ShowingConfigureDialog = true;
}
public void CancelConfigurePizzaDialog()
{
ConfiguringPizza = null;
ShowingConfigureDialog = false;
}
public void ConfirmConfigurePizzaDialog()
{
Order.Pizzas.Add(ConfiguringPizza);
ConfiguringPizza = null;
ShowingConfigureDialog = false;
}
}
}
현재 index.razor 컴포넌트에 새 클래스로 이동할 수 있는 코드가 많다는 것을 알 수 있다. 다음 단계는 이 서비스를 앱에서 사용할 수 있도록 하는 것이다.
4) Ctrl + S를 눌러 OrderState.cs라는 파일 이름으로 저장한다.
5) 탐색기에서 Program.cs를 선택한다.
6) Add services to the container 세그먼트 하단에 아래의 코드를 추가한다.
// Program.cs
// Add services to the container.
// ...
builder.Services.AddScoped<BlazingPizza.OrderState>();
이 코드는 새 OrderState 서비스를 추가한다. 이제 이를 Index.razor 컴포넌트에서 사용할 수 있다.
7) 탐색기로 돌아가 Pages 폴더 - Index.razor 파일을 선택한다.
8) 파일 상단의 NavigationManager 아래에 다음 코드를 추가한다.
// Pages > Index.razor
@inject OrderState OrderState
이제 주문 상태에 있는 모든 코드들을 삭제할 수 있다. @code블록은 다음과 같아야 한다.
// Pages > Index.razor
@code {
List<PizzaSpecial> specials = new List<PizzaSpecial>();
protected override async Task OnInitializedAsync()
{
specials = await HttpClient.GetFromJsonAsync<List<PizzaSpecial>>(NavigationManager.BaseUri + "specials");
}
}
이렇게만 하면 모든 코드가 삭제된 것을 참조하는 오류를 범하게 된다.
9) 따라서 OrderState 버전을 사용하기 위해 ShowConfigurePizzaDialog(special)) 호출을 변경한다.
// Pages > Index.razor
<li @onclick="@(() => OrderState.ShowConfigurePizzaDialog(special))" style="background-image: url('@special.ImageUrl')">
10) ShowConfigurePizzaDialog의 부울에 대한 참조를 변경한다.
// Pages > Index.razor
@if (OrderState.ShowingConfigureDialog)
11) ConfiguringPizza를 사용하여 파라미터를 변경한다.
// Pages > Index.razor
<ConfigurePizzaDialog Pizza="OrderState.ConfiguringPizza" />
12) F5를 눌러 디버깅을 시작한다. 디버깅 후에도 대화 상자는 이전과 같이 표시되며 차이는 없다.
7.4. 피자 주문 취소 및 만들기
OrderState 클래스에서 아직 사용하지 않은 두 가지 메서드를 확인할 수 있다. CancelConfigurePizzaDialog와 ConfirmConfigurePizzaDialog 메서드를 활용하여 고객이 주문을 확인하고 나면 대화상자를 닫고 피자를 Order 객체에 추가할 수 있다. 그럼 이제 이 메서드들을 대화 상자 버튼 구성에 연결해보자.
1) 탐색기에서 Shared 폴더 - ConfigurePizzaDialog.razor 파일을 선택한다.
2) @code 블록에 아래의 새로운 두가지 파라미터들을 추가한다. 코드는 다음과 같다.
// Shared > ConfigurePizzaDialog.razor
@code {
[Parameter] public Pizza Pizza { get; set; }
[Parameter] public EventCallback OnCancel { get; set; }
[Parameter] public EventCallback OnConfirm { get; set; }
}
3) 버튼에 @onclick 지시문을 추가한다. 현재의 대화상자 버튼 코드를 아래의 마크업으로 변경한다.
// Shared > ConfigurePizzaDialog.razor
<div class="dialog-buttons">
<button class="btn btn-secondary mr-auto" @onclick="OnCancel">Cancel</button>
<span class="mr-center">
Price: <span class="price">@(Pizza.GetFormattedTotalPrice())</span>
</span>
<button class="btn btn-success ml-auto" @onclick="OnConfirm">Order ></button>
</div>
4) 마지막 단계는 주문 취소 및 확인을 위해 OrderState 메서드를 전달하는 것이다. 탐색기에서 Pages 폴더 - Index.razor 파일을 클릭해보자.
5) ConfigurePizzaDialog 컴포넌트에 대한 호출 코드를 아래와 같이 변경한다.
// Pages > Index.razor
<ConfigurePizzaDialog
Pizza="OrderState.ConfiguringPizza"
OnCancel="OrderState.CancelConfigurePizzaDialog"
OnConfirm="OrderState.ConfirmConfigurePizzaDialog" />
6) F5를 눌러 디버깅을 시작한다.
이제 앱에서 고객이 피자 주문을 취소하거나 추가할 수 있다. 하지만 피자 크기가 변경되었을 때 이를 표시하거나 가격을 업데이트할 방법은 아직 없다. 다음 연습문제에서 이러한 기능들을 추가해보자!
(* 8단원은 생략합니다.)
9. [연습] - Blazor 애플리케이션의 데이터에 컨트롤 바인딩
Blazing Pizza 앱은 고객이 피자를 수정하고 주문에 추가할 때 인터페이스를 업데이트해야 한다. Blazor를 사용하면 HTML 컨트롤을 C# 속성에 바인딩하여 값이 변경될 때 업데이트할 수 있다.
고객은 주문하는 피자와 선택한 크기가 지불할 가격에 어떤 영향을 미치는지 확인해야 한다.
이번 단원 연습을 완료하면 Blazing Pizza 앱에서 주문을 업데이트하고 편집할 수 있게 된다. 그럼 이제부터 피자 속성에 컨트롤을 바인딩하고 이러한 변경 사항에 대한 가격을 다시 계산하는 방법을 알아보자!
9.1. 고객의 피자 주문 표시
고객이 주문한 모든 피자를 표시하는 사이드바를 만들어볼 차례이다.
1) Visual Studio Code 탐색기에서 Pages 폴더 - Index.razor 파일을 클릭한다.
2) @if 코드 블록아래에 다음의 코드를 추가한다.
// Pages > Index.razor
<div class="sidebar">
@if (order.Pizzas.Any())
{
<div class="order-contents">
<h2>Your order</h2>
@foreach (var configuredPizza in order.Pizzas)
{
<div class="cart-item">
<div class="title">@(configuredPizza.Size)" @configuredPizza.Special.Name</div>
<div class="item-price">
@configuredPizza.GetFormattedTotalPrice()
</div>
</div>
}
</div>
}
else
{
<div class="empty-cart">Choose a pizza<br>to get started</div>
}
<div class="order-total @(order.Pizzas.Any() ? "" : "hidden")">
Total:
<span class="total-price">@order.GetFormattedTotalPrice()</span>
<a href="checkout" class="@(OrderState.Order.Pizzas.Count == 0 ? "btn btn-warning disabled" : "btn btn-warning")">
Order >
</a>
</div>
</div>
이 HTML은 페이지에 사이드바를 추가하고, 피자가 OrderState.Order에 있으면 이를 표시할 수 있게 해준다. 마찬가지로 주문이 없으면 고객에게 주문을 추가하라는 메시지(Choose a pizza to get started)가 표시된다.
하지만 여기까지만 하면 컴포넌트가 주문이 무엇인지 알지 못하므로 몇 가지 오류를 발생시킨다.
3) @code 블록 아래에 다음 코드를 추가한다.
// Pages > Index.razor
// ...
@code {
Order order => OrderState.Order;
// ...
}
4) F5를 눌러 디버깅을 시작한다.
피자를 주문하고 일부를 취소해보자. 그럼 장바구니에 추가되고 주문 총계가 업데이트되는 것을 볼 수 있다.
5) Shift + F5를 눌러 디버깅을 중지한다.
9.2. 고객 주문에서 피자 제거
하지만 여기까지만 하면 고객의 장바구니에서 피자를 제거할 방법이 없다. 따라서 해당 기능을 추가해야 한다.
첫번째 단계는 OrderState 주문에서 피자를 제거할 수 있도록 서비스를 업데이트하는 것이다.
1) 탐색기에서 OrderState.cs를 선택한다.
2) 클래스 맨 아래에 다음 메서드를 추가한다.
// OrderState.cs
public void RemoveConfiguredPizza(Pizza pizza)
{
Order.Pizzas.Remove(pizza);
}
3) 탐색기에서 Pages 폴더 - Index.razor 파일을 선택한다.
4) <div class="cart-item>에 <a> 태그를 추가하여 제거 버튼을 생성한다.
// Pages > Index.razor
<a @onclick="@(() => OrderState.RemoveConfiguredPizza(configuredPizza))" class="delete-item">x</a>
이제 사이드바의 x 버튼을 클릭하면 OrderState 서비스의 RemoveConfiguredPizza 메서드를 호출하여 주문한 피자를 제거할 수 있다.
5) F5를 눌러 디버깅을 시작한다.
6) Shift + F5를 눌러 디버깅을 중지한다.
9.3. 동적으로 피자 크기 구성
크기 슬라이더가 변경되면 피자 구성 대화상자가 업데이트되지 않는다. 컴포넌트는 피자와 크기를 업데이트하고 가격을 다시 계산하는 방법이 필요하다.
1) 탐색기에서 Shared 폴더 - ConfigurePizzaDialog.razor 파일을 선택한다.
2) 피자 사이즈의 값을 바인딩하기 위해 HTML 컨트롤에 아래와 같이 input 명령을 추가한다.
// Shared > ConfigurePizzaDialog.razor
<input type="range" min="@Pizza.MinimumSize" max="@Pizza.MaximumSize" step="1" @bind="Pizza.Size"/>
3) F5를 눌러 디버깅을 시작한다.
업데이트된 대화 상자를 사용하여 다른 크기의 피자들을 주문해보자. 드래그 대신 슬라이더 바를 클릭하여 컨트롤의 마우스 업 이벤트에서 피자 크기가 업데이트되는지 확인한다.
(* 여기까지 하면 슬라이더를 드래그하여 마우스를 놓기 전까지 크기가 변경되지 않는 문제가 있다. 이 부분을 수정해야 한다. )
4) Shift + F5를 눌러 디버깅을 중지한다.
5) 컨트롤이 바인딩되어야 하는 이벤트를 추가한다.
// Shared > ConfigurePizzaDialog.razor
<input type="range" min="@Pizza.MinimumSize" max="@Pizza.MaximumSize" step="1" @bind="Pizza.Size" @bind:event="oninput" />
6) 다시 F5를 눌러 디버깅한다.
참고로 가격이 업데이트되는 것은 아래의 코드가 있기 때문이다.
// Shared > ConfigurePizzaDialog.razor
Price: <span class="price">@(Pizza.GetFormattedTotalPrice())</span>
피자의 GetFormattedTotalPrice() 메서드는 피자 크기를 사용하여 총 가격을 계산하기 때문에, 피자 크기가 변경되면 가격이 업데이트 된다.
10. 요약
지금까지 Blazor을 활용하여 C#으로 클라이언트 쪽 코드와 서버 쪽 코드를 모두 작성하는 방법을 살펴보았다.
상단의 학습목표를 다시 한 번 체크하며 이번 모듈을 마무리해보자 :)
'IT > Computer Science' 카테고리의 다른 글
[파이썬/Python] CodeUp 파이썬 기초 100제 6014 - 6031 (0) | 2022.02.06 |
---|---|
[Blazor & C# 핸즈온] 페이지, 라우팅 및 레이아웃을 사용하여 Blazor 탐색 개선 (0) | 2021.12.29 |
[Flask] Flask 기초 - API 만들기 (0) | 2021.11.06 |
[C#] 콘솔에 출력하기 - Console.Write()과 Console.WriteLine()차이 (0) | 2021.10.29 |
Terminal에서 code . 으로 vscode 열기 오류날 때 (0) | 2021.10.10 |