일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- Python
- 블레이저
- codeup
- GenAI
- C#
- nlp
- 파이썬기초100제
- 알고리즘
- 파이썬알고리즘
- DataScience
- 생성형AI
- GenerativeAI
- 클라우드
- 구글퀵랩
- 코드업파이썬
- 데이터분석
- gcp
- 코드업100제
- 코드업
- 파이썬기초
- Microsoft
- 빅데이터
- Blazor
- 자연어처리
- 한빛미디어
- Azure
- attention
- 데이터사이언스
- 파이썬
- 머신러닝
- Today
- Total
Tech for good
[Blazor, C#] Blazor WebAssembly Blog Series (1~3) 본문
* 해당 영상을 공부하며 정리한 자료입니다.
https://www.youtube.com/playlist?list=PLF1jhYUTnHo5XFX9lgS0YsNSDJHpYnRxK
1. First Steps with Blazor WebAssembly & Razor Components
Blazor WebAssembly 기본 세팅 후 앱 빌드하기
- Blazor WebAssembly, .NET6으로 진행
* 폴더 및 파일 구조는 크게 아래와 같이 구성된다.
- BlazorBlog.Client
- Pages
- Index.razor
- Post.razor
- Services
- BlogService.cs
- IBlogService.cs
- Shared
- BlogPost.razor
- MainLayout.razor
- NavMenu.razor
- _Imports.razor
- App.razor
- Program.cs
- Pages
- BlazorBlog.Server
- Controllers
- BlogController.cs
- Pages
- Error.cshtml
- appsetting.json
- Program.cs
- Controllers
- BlazorBlog.Shared
- BlogPost.cs
※ Tips!
- Class 이름은 보통 단수로 쓴다. BlogServices.cs 이렇게 복수로 쓰지 않는다.
- 네임스페이스에 정의된 형식을 사용할 수 있게 해주는 @using 지시문(e.g. @using BlazorBlog.Shared)은 Client/_Imports.razor 파일에 작성해준다.
2. Services, Dependency Injection & Page Parameters with Blazor
2.1. Services
인터페이스와 클래스의 개념을 배울 수 있다.
인터페이스
: 메서드, 속성, 이벤트 등을 갖긴 하지만 이를 직접 구현하지 않고 단지 정의(prototype definition)만을 갖는다.
클래스
: 메서드, 속성, 이벤트 등을 직접 구현한다!
- IBlazorService.cs (인터페이스)
// BlazorBlog.Client > Services > IBlogService.cs
namespace BlazorBlog.Client.Services
{
interface IBlogService
{
List<BlazorBlog.Shared.BlogPost> GetBlogPosts(); // 블로그의 모든 포스트를 받는다.
BlazorBlog.Shared.BlogPost GetBlogPostByUrl(string url); // URL을 받는다.
}
}
// BlazorBlog.Shared > BlogPost.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BlazorBlog.Shared
{
public class BlogPost
{
public int Id { get; set; }
public string Url { get; set; } // 새로운 프로퍼티 추가
public string Title { get; set; }
public string Content { get; set; }
public string Description { get; set; }
public string Author { get; set; }
public DateTime DateCreated { get; set; } = DateTime.Now;
public bool IsPublished { get; set; } = true;
public bool IsDeleted { get; set; } = false;
}
}
- BlazorService.cs (클래스)
// BlazorBlog.Client > Shared > BlogPosts.razor
@code
public List<BlogPosts> Posts {get; set; } = new List</BlogPosts>()
{
new BlogPost { Url = "new-tutorial", Title = "A New Tutorial about Blazor with Web API", Description="This is a new tutorial, showing you how to build a blog with Blazor"},
new BlogPost { Url = "first-post", Title = "My First Blog Post with Web API", Description = "Hi! This is my new shiny blog."}
};
위의 부분을 복사하여 BlogService.cs 파일에 아래와 같이 추가한다.
// BlazorBlog.Client > Services > BlogService.cs
namespace BlazorBlog.Client.Services
{
public class BlogService : IBlogService
{
public List<BlogPost> Posts { get; set; } = new List<BlogPost>
{
new BlogPost { Url = "new-tutorial", Title = "A New Tutorial about Blazor with Web API", Description="This is a new tutorial, showing you how to build a blog with Blazor"},
new BlogPost { Url = "first-post", Title = "My First Blog Post with Web API", Description = "Hi! This is my new shiny blog."}
};
public BlogPost GetBlogPostByUrl(string url)
{
throw new NotImplementedException();
}
public List<BlogPost> GetBlogPosts()
{
return Posts;
}
}
}
2.2. Dependency Injection
@inject 지시문을 사용하여 .razor 파일에서 종속성을 주입받는 방법을 배울 수 있다.
// BlazorBlog.Client/Program.cs
using BlazorBlog.Client;
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) });
builder.Services.AddScoped<BlazorBlog.Client.Services.IBlogService, BlazorBlog.Client.Services.BlogService>();
await builder.Build().RunAsync();
그리고 나서 BlogPosts.razor 파일로 돌아가, 상단에 @inject BlazorBlog.Client.Services.IBlogService BlogService를 추가한다.
// BlazorBlog.Client > Shared > BlogPosts.razor
@inject BlazorBlog.Client.Services.IBlogService BlogService
~~~~
@code {
private List<BlogPost> Posts = new List<BlogPost>();
protected override void OnInitialized()
{
Posts = BlogService.GetBlogPosts();
}
}
@inject 지시문을 사용하는 이유는 .razor 파일에서 종속성을 주입받기 위함이다.
즉, 해당 인터페이스(IBlogService)를 변수(Property; BlogService)로 선언해서 사용할 수 있게 해주는 것이다.
이렇게 razor 페이지에서 사용할 변수를 선언하기 위해 사용하는 것이 @inject 지시문이다.
2.3. Page Parameters with Blazor
Read more... 클릭 시 웹 주소가 변경되며 다른 화면이 나오게 만드는 방법을 배울 수 있다.
// BlazorBlog.Client > Shared > BlogPosts.razor
@inject BlazorBlog.Client.Services.IBlogService BlogService
@foreach (var post in Posts)
{
<div style="margin: 20px;">
<div>
<h3>@post.Title</h3>
</div>
<div>
@post.Description
</div>
<div>
<a href="/posts/@post.Url">Read more...</a>
</div>
</div>
}
~~~
// BlazorBlog.Client > Pages > Post.razor
@page "/posts/{url}" // 파라미터 값 전달
@inject BlazorBlog.Client.Services.IBlogService BlogService
<h3>@CurrentPost.Title</h3>
<div>
@CurrentPost.Content
</div>
@code {
private BlazorBlog.Shared.BlogPost CurrentPost;
[Parameter]
public string Url { get; set; }
protected override void OnInitialized()
{
CurrentPost = BlogService.GetBlogPostByUrl(Url);
}
}
// BlazorBlog.Client > Services > BlogService.cs
namespace BlazorBlog.Client.Services
{
public class BlogService : IBlogService
{
public List<BlazorBlog.Shared.BlogPost> Posts { get; set; } = new List<BlazorBlog.Shared.BlogPost>
{
new BlazorBlog.Shared.BlogPost { Url = "new-tutorial", Title = "A New Tutorial about Blazor with Web API", Description="This is a new tutorial, showing you how to build a blog with Blazor", Content = "I'm going to introduce what Blazor is from now on. Enjoy!"},
new BlazorBlog.Shared.BlogPost { Url = "first-post", Title = "My First Blog Post with Web API", Description = "Hi! This is my new shiny blog.", Content = "This is my 'Tech for good Blog'. I'm going to write it down more often in 2022 :)"}
};
public BlazorBlog.Shared.BlogPost GetBlogPostByUrl(string url)
{
return Posts.FirstOrDefault(p => p.Url.ToLower().Equals(url.ToLower()));
}
public List<BlazorBlog.Shared.BlogPost> GetBlogPosts()
{
return Posts;
}
}
}
FirstOrDefault() 메서드는 리턴되는 ResultSet이 Null일 수 있을 때 사용하며, 여러 데이터 중 1개의 데이터가 무조건 조회된다.
(cf. First()메서드는 리턴값이 반드시 존재할 때 그 해당 조건의 첫번째 row를 리턴하기 위해 사용된다. 만약 First() 호출에서 리턴 결과가 없으면 Exception을 발생시킨다.)
3. Build a Web API & Make HTTP Calls with Blazor WebAssembly
API Controller - Empty (No methods)로 Web API를 생성하고, 비동기로 서버-클라이언트가 통신하는 방법을 배운다.
// BlazorBlog.Server > Controllers > BlogController.cs
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace BlazorBlog.Server.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class BlogController : ControllerBase
{
public List<BlazorBlog.Shared.BlogPost> Posts { get; set; } = new List<BlazorBlog.Shared.BlogPost>
{
new BlazorBlog.Shared.BlogPost { Url = "new-tutorial", Title = "A New Tutorial about Blazor with Web API", Description="This is a new tutorial, showing you how to build a blog with Blazor", Content = "I'm going to introduce what Blazor is from now on. Enjoy!"},
new BlazorBlog.Shared.BlogPost { Url = "first-post", Title = "My First Blog Post with Web API", Description = "Hi! This is my new shiny blog.", Content = "This is my 'Tech for good Blog'. I'm going to write it down more often in 2022 :)"}
};
[HttpGet]
public ActionResult<List<BlazorBlog.Shared.BlogPost>> GiveMeAllTheBlogPosts()
{
return Ok(Posts);
}
[HttpGet("{url}")]
public ActionResult<BlazorBlog.Shared.BlogPost> GiveMeThatSingleBlogPost(string url)
{
var post = Posts.FirstOrDefault(p => p.Url.ToLower().Equals(url.ToLower()));
if (post is null)
{
return NotFound("This post does not exist.");
}
return Ok(post);
}
}
}
위의 파일을 보면 BlogController가 ControllerBase를 상속받고 있음을 알 수 있다. 기존에 MVC에서 사용되던 Controller는 Controller 클래스를 상속받는 데 반해, API 전용의 Controller에서는 API만을 위해 좀 더 경량화된 버전의 ControllerBase를 기본으로 상속받는다.
Routing 경로는 api/[controller]이며, [controller]는 생성된 Controller 클래스 이름에 해당한다. 따라서 실제 Routing 경로는 api/Blog가 된다.
만약 다른 경로로 Routing 되기를 희망한다면 아래와 같이 [Route("api/abc")] 고정적인 경로를 줄 수도 있고, [Route("api/abc/def")] 처럼 서브경로를 포함해 지정해 줄 수도 있다.
// 고정 라우팅 설정 예시
namespace BlazorBlog.Server.Controllers
{
[Route("api/abc")]
[ApiController]
public class BlogController : ControllerBase
{
public List<BlazorBlog.Shared.BlogPost> Posts { get; set; } = new List<BlazorBlog.Shared.BlogPost>
{
new BlazorBlog.Shared.BlogPost { Url = "new-tutorial", Title = "A New Tutorial about Blazor with Web API", Description="This is a new tutorial, showing you how to build a blog with Blazor", Content = "I'm going to introduce what Blazor is from now on. Enjoy!"},
new BlazorBlog.Shared.BlogPost { Url = "first-post", Title = "My First Blog Post with Web API", Description = "Hi! This is my new shiny blog.", Content = "This is my 'Tech for good Blog'. I'm going to write it down more often in 2022 :)"}
};
// BlazorBlog.Client > Services > BlogService.cs
namespace BlazorBlog.Client.Services
{
public class BlogService : IBlogService
{
private readonly HttpClient _http;
// HttpClient 의존성 주입받을 생성자 BlogService
public BlogService(HttpClient http)
{
_http = http;
}
// 비동기 처리
public async Task<BlazorBlog.Shared.BlogPost> GetBlogPostByUrl(string url)
{
var post = await _http.GetFromJsonAsync<BlazorBlog.Shared.BlogPost>($"api/Blog/{url}");
return post;
}
public async Task<List<BlazorBlog.Shared.BlogPost>> GetBlogPosts()
{
return await _http.GetFromJsonAsync<List<BlazorBlog.Shared.BlogPost>>("api/Blog");
}
}
}
// BlazorBlog.Client > Services > IBlogService.cs
namespace BlazorBlog.Client.Services
{
interface IBlogService
{
Task<List<BlazorBlog.Shared.BlogPost>> GetBlogPosts();
Task<BlazorBlog.Shared.BlogPost> GetBlogPostByUrl(string url);
}
}
인터페이스도 마찬가지로 앞에 Task<> 리턴 타입을 붙여 비동기 처리한다.
// BlazorBlog.Client > Shared > BlogPosts.razor
@inject BlazorBlog.Client.Services.IBlogService BlogService
@foreach (var post in Posts)
{
<div style="margin: 20px;">
<div>
<h3>@post.Title</h3>
</div>
<div>
@post.Description
</div>
<div>
<a href="/posts/@post.Url">Read more...</a>
</div>
</div>
}
@code {
private List<BlogPost> Posts = new List<BlogPost>();
protected override async Task OnInitializedAsync()
{
Posts = await BlogService.GetBlogPosts();
}
}
// BlazorBlog.Client > Pages > Post.razor
@page "/posts/{url}"
@inject BlazorBlog.Client.Services.IBlogService BlogService
<h3>@CurrentPost.Title</h3>
<div>
@CurrentPost.Content
</div>
@code {
private BlazorBlog.Shared.BlogPost CurrentPost;
[Parameter]
public string Url { get; set; }
protected override async Task OnInitializedAsync()
{
CurrentPost = await BlogService.GetBlogPostByUrl(Url);
}
}
.razor 파일들도 마찬가지로 비동기로 구현하기 위해 위와 같이 코드를 수정한다.
여기까지 하면 웹 통신은 정상적으로 잘 되지만, CurrentPost가 null값일 때는 위와 같은 오류가 발생한다는 것을 알 수 있다. 따라서 아래와 같이 @if(CurrentPost == null)문으로 조건을 추가해준다.
// BlazorBlog.Client > Pages > Post.razor
@page "/posts/{url}"
@inject BlazorBlog.Client.Services.IBlogService BlogService
@if (CurrentPost == null)
{
<span>Getting that blog post from the service...</span>
}
else
{
<h3>@CurrentPost.Title</h3>
<div>
@CurrentPost.Content
</div>
}
@code {
private BlazorBlog.Shared.BlogPost CurrentPost;
[Parameter]
public string Url { get; set; }
protected override async Task OnInitializedAsync()
{
CurrentPost = await BlogService.GetBlogPostByUrl(Url);
}
}
그러면 위와 같이 오류 없는 화면이 잘 나타난다!
4. Outro
이번 시간에는 크게 아래의 세 가지 항목들을 학습하였다.
- 1) First Steps with Blazor WebAssembly & Razor Components
- Blazor WebAssembly 기본 세팅 후 앱 빌드하는 방법을 살펴보았다.
- 2) Services, Dependency Injection & Page Parameters with Blazor
- 인터페이스와 클래스의 차이를 학습하였다.
- @inject 지시문을 사용하여 .razor 파일에서 종속성을 주입받는 방법을 살펴보았다.
- 경로 매개변수를 활용하여 라우팅하는 방법을 살펴보았다.
- 3) Build a Web API & Make HTTP Calls with Blazor WebAssembly
- API Controller로 Web API를 생성하고 비동기로 서버-클라이언트 통신하는 방법을 배웠다.
'IT > Computer Science' 카테고리의 다른 글
[Blazor, C#] Blazor WebAssembly Blog Series 5 (0) | 2022.02.24 |
---|---|
[Blazor, C#] Blazor WebAssembly Blog Series 4 (0) | 2022.02.23 |
[파이썬/Python] CodeUp 파이썬 기초 100제 6046 - 6058 (0) | 2022.02.20 |
[파이썬/Python] CodeUp 파이썬 기초 100제 6032- 6045 (0) | 2022.02.13 |
[파이썬/Python] CodeUp 파이썬 기초 100제 6014 - 6031 (0) | 2022.02.06 |