연습 - Blazor 구성 요소에서 데이터 액세스

완료됨

현재 앱에 하드 코드된 피자를 데이터베이스로 바꾸어야 합니다. Microsoft Entity Framework를 사용하면 데이터 원본에 대한 연결을 추가할 수 있습니다. 이 앱에서 SQLite 데이터베이스를 사용하여 피자를 저장합니다.

이 연습에서는 데이터베이스 기능을 지원하는 패키지를 추가하고, 클래스를 백 엔드 데이터베이스에 연결하고, 회사의 피자 데이터를 미리 로드하는 도우미 클래스를 추가합니다.

데이터베이스 액세스를 지원하는 패키지 추가

  1. 앱이 계속 실행되고 있으면 중지합니다.

  2. Visual Studio Code 내에서 터미널>새 터미널을 선택합니다.

  3. 새 터미널 창에서 위치를 BlazingPizza 디렉터리로 설정합니다.

    cd BlazingPizza
    
  4. 다음 명령을 실행하여 Microsoft.EntityFrameworkCore, Microsoft.EntityFrameworkCore.SqliteSystem.Net.Http.Json 패키지를 추가합니다.

    dotnet add package Microsoft.EntityFrameworkCore --version 6.0.8
    dotnet add package Microsoft.EntityFrameworkCore.Sqlite --version 6.0.8
    dotnet add package System.Net.Http.Json --version 6.0.0
    

    다음 명령은 BlazingPizza.csproj 파일에 패키지 참조를 추가합니다.

      <ItemGroup>
        <PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.8" />
        <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.8" />
        <PackageReference Include="System.Net.Http.Json" Version="6.0.0" />
      </ItemGroup>
    

데이터베이스 컨텍스트 추가

  1. Visual Studio Code에서 BlazingPizza 폴더에 새 폴더를 만듭니다. 이름을 Data로 지정합니다.

  2. Data 폴더에 새 파일을 만듭니다. 이름을 PizzaStoreContext.cs로 지정합니다.

  3. 클래스에 대해 다음 코드를 입력합니다.

    using Microsoft.EntityFrameworkCore;
    
    namespace BlazingPizza.Data;
    
    public class PizzaStoreContext : DbContext
    {
        public PizzaStoreContext(DbContextOptions options) : base(options)
        {
        }
    
        public DbSet<PizzaSpecial> Specials { get; set; }
    }    
    

    이 클래스는 데이터베이스 서비스를 등록하는 데 사용할 수 있는 데이터베이스 컨텍스트를 만듭니다. 컨텍스트를 사용하면 데이터베이스에 액세스하는 컨트롤러를 사용할 수도 있습니다.

  4. 변경 내용을 저장합니다.

컨트롤러 추가

  1. BlazingPizza 폴더에 새 폴더를 만듭니다. 이름을 Controllers로 지정합니다.

  2. Controllers 폴더에 새 파일을 만듭니다. 이름을 SpecialsController.cs로 지정합니다.

  3. 클래스에 대해 다음 코드를 입력합니다.

    using Microsoft.AspNetCore.Mvc;
    using Microsoft.EntityFrameworkCore;
    using BlazingPizza.Data;
    
    namespace BlazingPizza.Controllers;
    
    [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으로 반환할 수 있는 컨트롤러를 만듭니다.

  4. 변경 내용을 저장합니다.

데이터베이스에 데이터 로드

앱은 기존 SQLite 데이터베이스가 있는지 확인하고 미리 만든 피자를 사용하여 데이터베이스를 하나 만듭니다.

  1. Data 디렉터리에 새 파일을 만듭니다. 이름을 SeedData.cs로 지정합니다.

  2. 클래스에 대해 다음 코드를 입력합니다.

    namespace BlazingPizza.Data;
    
    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 개체를 만든 다음 저장합니다.

  3. 파일 탐색기에서 Program.cs를 선택합니다.

  4. 맨 위쪽에서 새 PizzaStoreContext에 대한 참조를 추가합니다.

    using BlazingPizza.Data;
    

    이 문을 사용하면 앱이 새 서비스를 사용할 수 있습니다.

  5. 다음 세그먼트를 app.Run(); 메서드 위에 삽입합니다.

    ...
    // Initialize the database
    var scopeFactory = app.Services.GetRequiredService<IServiceScopeFactory>();
    using (var scope = scopeFactory.CreateScope())
    {
        var db = scope.ServiceProvider.GetRequiredService<PizzaStoreContext>();
        if (db.Database.EnsureCreated())
        {
            SeedData.Initialize(db);
        }
    }
    
    app.Run();
    

    이 변경으로 인해 PizzaStoreContext가 있는 데이터베이스 범위가 만들어집니다. 데이터베이스가 아직 만들어지지 않은 경우 SeedData 정적 클래스를 호출하여 데이터베이스를 만듭니다.

  6. 지금은 PizzaStoreContext를 초기화하지 않았기 때문에 앱이 작동하지 않습니다. Program.cs 파일의 Add Services to the container 상위 섹션에서 현재 서비스(builder.Services.를 시작하는 줄) 아래에 다음 코드를 추가합니다.

      builder.Services.AddHttpClient();
      builder.Services.AddSqlite<PizzaStoreContext>("Data Source=pizza.db");
    
    

    이 코드는 두 서비스를 등록합니다. 첫 번째 AddHttpClient 문을 사용하면 앱이 HTTP 명령에 액세스할 수 있습니다. 앱은 HttpClient를 사용하여 피자 스페셜에 대한 JSON을 가져옵니다. 두 번째 문은 새 PizzaStoreContext를 등록하고 SQLite 데이터베이스의 파일 이름을 제공합니다.

데이터베이스를 사용하여 피자 표시

이제 Index.razor 페이지에서 하드 코드된 피자를 바꿀 수 있습니다.

  1. 파일 탐색기에서 Index.razor를 선택합니다.

  2. 기존 OnInitialized() 메서드를 다음으로 바꿉니다.

    protected override async Task OnInitializedAsync()
    {
        specials = await HttpClient.GetFromJsonAsync<List<PizzaSpecial>>(NavigationManager.BaseUri + "specials");
    }
    

    참고

    이 코드는 OnInitialized()OnInitializedAsync()로 대체합니다. 이제 스페셜이 앱에서 비동기식으로 JSON으로 반환됩니다.

  3. 수정해야 하는 몇 가지 오류가 있습니다. @page 지시문 아래에 다음 @inject 문을 추가합니다.

    @inject HttpClient HttpClient
    @inject NavigationManager NavigationManager
    
  4. 모든 변경 내용을 저장한 다음, F5를 선택하거나 실행을 선택합니다. 그런 다음, 디버깅 시작을 선택합니다.

    앱을 실행할 때 런타임 오류가 발생합니다. JsonReader에서 예외가 발생했습니다.

  5. 앱은 (http://localhost:5000/specials)에서 JSON을 만들어야 합니다. 해당 URL로 이동합니다.

    앱은 이 요청을 라우팅하는 방법을 모릅니다. 라우팅에 대해서는 Blazor 라우팅 모듈에서 알아봅니다. 이제 오류를 해결해 보겠습니다.

  6. Shift + F5를 선택하거나 디버깅 중지를 선택합니다.

  7. 파일 탐색기에서 Program.cs를 선택합니다.

  8. 파일 중간에 app.를 시작하는 줄 뒤에 다음 엔드포인트를 추가합니다.

    app.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
    

    이제 코드는 다음과 같습니다.

    ...
    app.MapRazorPages();
    app.MapBlazorHub();
    app.MapFallbackToPage("/_Host");
    app.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
    ...
    
  9. F5를 선택하거나 실행을 선택합니다. 그런 다음, 디버깅 시작을 선택합니다.

    이제 앱이 작동하지만 JSON이 올바르게 생성되고 있는지 확인해 보겠습니다.

  10. (http://localhost:5000/specials)로 이동하여 다음을 확인하세요.

    Screenshot showing the browser that shows JSON for pizzas.

    JSON에는 스페셜 피자 컨트롤러에 지정된 대로 가격이 내림차순으로 나열된 피자가 있습니다.

    Screenshot showing even more blazing pizzas.