연습 - 마이그레이션 설정

완료됨

이 단원에서는 로컬 SQLite 데이터베이스의 테이블에 매핑할 C# 엔터티 클래스를 만듭니다. EF Core 마이그레이션 기능은 해당 엔터티에서 테이블을 생성합니다.

마이그레이션은 데이터베이스 스키마를 증분 업데이트하는 방법을 제공합니다.

프로젝트 파일 가져오기

시작하려면 프로젝트 파일을 가져옵니다. 프로젝트 파일을 가져오는 방법에 대한 옵션이 몇 가지 있습니다.

  • GitHub Codespaces 사용
  • GitHub 리포지토리를 복제합니다.

호환되는 컨테이너 런타임이 설치된 경우 개발 컨테이너 확장을 사용하여 도구가 사전 설치된 컨테이너에서 리포지토리를 열 수 있습니다.

GitHub Codespaces 사용

Codespace는 클라우드에서 호스팅되는 IDE입니다. GitHub Codespaces를 사용하는 경우 브라우저의 리포지토리로 이동합니다. 코드를 선택한 다음, main 분기에 새 codespace를 만듭니다.

GitHub 리포지토리를 복제합니다.

GitHub Codespaces를 사용하지 않는 경우 프로젝트 GitHub 리포지토리를 복제한 다음, Visual Studio Code에서 폴더로 파일을 열 수 있습니다.

  1. 명령 터미널을 연 다음, 명령 프롬프트를 사용하여 GitHub에서 프로젝트를 복제합니다.

    git clone https://github.com/MicrosoftDocs/mslearn-persist-data-ef-core
    
  2. mslearn-persist-data-ef-core 폴더로 이동한 다음, Visual Studio Code에서 프로젝트를 엽니다.

    cd mslearn-persist-data-ef-core
    code .
    

코드 검토

이제 작업할 프로젝트 파일이 있습니다. 프로젝트의 내용을 확인하고 코드를 검토합니다.

  • ASP.NET Core 웹 API인 프로젝트는 ContosoPizza 디렉터리에 있습니다. 이 모듈에서 참조되는 파일 경로는 ContosoPizza 디렉터리를 기준으로 합니다.
  • Services/PizzaService.cs는 CRUD(만들기, 읽기, 업데이트, 삭제) 메서드를 정의하는 서비스 클래스입니다. 모든 메서드는 현재 System.NotImplementedException을 throw합니다.
  • Program.cs에서 PizzaService가 ASP.NET Core의 종속성 삽입 시스템에 등록됩니다.
  • Controllers/PizzaController.csApiController에 대한 값으로 HTTP POST, GET, PUT, DELETE 동사의 엔드포인트를 공개합니다. 이러한 동사는 PizzaService에 해당 CRUD 메서드를 호출합니다. PizzaServicePizzaController 생성자에 삽입됩니다.
  • Models 폴더에는 PizzaServicePizzaController에서 사용하는 모델이 포함되어 있습니다.
  • 엔터티 모델 Pizza.cs, Topping.csSauce.cs에는 다음 관계가 있습니다.
    • 피자에 토핑을 하나 이상 얹을 수 있습니다.
    • 토핑을 피자 하나 또는 여러 피자에 사용할 수 있습니다.
    • 피자에는 소스를 하나 선택할 수 있지만 한 가지 소스를 많은 피자에서 사용할 수 있습니다.

앱 빌드

Visual Studio Code에서 앱을 빌드하려면 다음을 수행합니다.

  1. Explorer 창에서 ContosoPizza 디렉터리를 마우스 오른쪽 단추로 클릭하고 통합 터미널에서 열기를 선택합니다.

    ContosoPizza 디렉터리로 범위가 지정된 터미널 창이 열립니다.

  2. 다음 명령을 사용하여 앱을 빌드합니다.

    dotnet build
    

    코드가 경고 또는 오류 없이 빌드될 것입니다.

NuGet 패키지 및 EF Core 도구 추가

이 모듈에서 작업하는 데이터베이스 엔진은 SQLite입니다. SQLite는 간단한 파일 기반 데이터베이스 엔진입니다. 개발 및 테스트에 적합하며 소규모 프로덕션 배포에도 적합합니다.

참고

앞에서 설명한 것처럼 EF Core의 데이터베이스 공급자는 플러그형입니다. SQLite는 가볍고 플랫폼 간이므로 이 모듈에 적합한 선택입니다. 동일한 코드를 사용하여 SQL Server 및 PostgreSQL과 같은 다른 데이터베이스 엔진에서 작업할 수 있습니다. 동일한 앱에서 여러 데이터베이스 엔진을 사용할 수도 있습니다.

시작하기 전에 필요한 패키지를 추가해야 합니다.

  1. 터미널 창에서 다음 명령을 실행합니다.

    dotnet add package Microsoft.EntityFrameworkCore.Sqlite
    

    이 명령은 EF Core SQLite 데이터베이스 공급자 및 모든 종속성(일반적인 EF Core 서비스를 포함)이 포함된 NuGet 패키지를 추가합니다.

  2. 다음 명령을 실행합니다.

    dotnet add package Microsoft.EntityFrameworkCore.Design
    

    이 명령은 EF Core 도구에 필요한 패키지를 추가합니다.

  3. 완료하려면 다음 명령을 실행합니다.

    dotnet tool install --global dotnet-ef
    

    이 명령은 마이그레이션 및 스캐폴딩을 만드는 데 사용할 도구인 dotnet ef를 설치합니다.

    dotnet ef가 이미 설치되어 있는 경우 dotnet tool update --global dotnet-ef를 실행하여 업데이트할 수 있습니다.

스캐폴드 모델 및 DbContext

이제 DbContext 구현을 추가하고 구성합니다. DbContext는 데이터베이스와 상호 작용할 수 있는 게이트웨이입니다.

  1. ContosoPizza 디렉터리를 마우스 오른쪽 단추로 클릭하고 Data라는 새 폴더를 추가합니다.

  2. Data 폴더에서 PizzaContext.cs라는 새 파일을 만듭니다. 빈 파일에 다음 코드를 추가합니다.

    using Microsoft.EntityFrameworkCore;
    using ContosoPizza.Models;
    
    namespace ContosoPizza.Data;
    
    public class PizzaContext : DbContext
    {
        public PizzaContext (DbContextOptions<PizzaContext> options)
            : base(options)
        {
        }
    
        public DbSet<Pizza> Pizzas => Set<Pizza>();
        public DbSet<Topping> Toppings => Set<Topping>();
        public DbSet<Sauce> Sauces => Set<Sauce>();
    }
    

    위의 코드에서

    • 생성자는 DbContextOptions<PizzaContext> 형식의 매개 변수를 허용합니다. 생성자를 사용하면 외부 코드가 구성을 전달할 수 있으므로 동일한 DbContext를 테스트 코드와 프로덕션 코드 간에 공유하고 다른 공급자에서도 사용할 수 있습니다.
    • DbSet<T> 속성은 데이터베이스에 생성될 테이블에 해당합니다.
    • 테이블 이름은 PizzaContext 클래스의 DbSet<T> 속성 이름과 일치합니다. 필요한 경우 이 동작을 재정의할 수 있습니다.
    • 인스턴스화되면 PizzaContextPizzas, ToppingsSauces 속성을 공개합니다. 이런 속성에 의해 공개되는 컬렉션에 대한 변경 내용은 데이터베이스에 전파됩니다.
  3. Program.cs에서 // Add the PizzaContext를 다음 코드로 바꿉니다.

    builder.Services.AddSqlite<PizzaContext>("Data Source=ContosoPizza.db");
    

    앞의 코드가 하는 역할은 다음과 같습니다.

    • PizzaContext를 ASP.NET Core의 종속성 삽입 시스템에 등록합니다.
    • PizzaContext가 SQLite 데이터베이스 공급자를 사용하도록 지정합니다.
    • 로컬 파일 ContosoPizza.db를 가리키는 SQLite 연결 문자열을 정의합니다.

    참고

    SQLite는 로컬 데이터베이스 파일을 사용하므로 연결 문자열을 하드 코딩해도 좋습니다. PostgreSQL 또는 SQL Server와 같은 네트워크 데이터베이스의 경우 연결 문자열을 항상 안전하게 보관해야 합니다. 로컬 개발의 경우 비밀 관리자를 사용합니다. 프로덕션 배포의 경우 Azure Key Vault 같은 서비스를 사용하는 것이 좋습니다.

  4. 또한 Program.cs에서 // Additional using declarations를 다음 코드로 바꿉니다.

    using ContosoPizza.Data;
    

    위의 코드는 이전 단계의 종속성을 해결합니다.

  5. 변경 내용을 모두 저장합니다. Github Codespaces는 변경 내용을 자동으로 저장합니다.

  6. dotnet build를 실행하여 터미널에서 앱을 빌드합니다. 빌드는 경고나 오류 없이 성공해야 합니다.

마이그레이션 프로젝트 만들기 및 실행

다음으로, 초기 데이터베이스를 만드는 데 사용할 수 있는 마이그레이션을 만듭니다.

  1. 다음 명령을 실행하여 테이블을 만들기 위한 마이그레이션을 생성합니다.

    dotnet ef migrations add InitialCreate --context PizzaContext
    

    앞의 명령에서 다음을 확인할 수 있습니다.

    • 마이그레이션에 InitialCreate라는 이름이 부여됩니다.
    • --context 옵션은 DbContext에서 파생되는 ContosoPizza 프로젝트의 클래스 이름을 지정합니다.

    Migrations 디렉터리가 ContosoPizza 프로젝트 루트에 나타납니다. 디렉터리에 DDL(데이터 정의 언어) 변경 스크립트로 변환될 데이터베이스 변경 사항을 설명하는 <timestamp>_InitialCreate.cs 파일이 포함되어 있습니다.

  2. 다음 코드를 실행하여 InitialCreate 마이그레이션을 적용합니다.

    dotnet ef database update --context PizzaContext
    

    이 명령은 마이그레이션을 적용합니다. ContosoPizza.db는 존재하지 않으므로 프로젝트 디렉터리에 마이그레이션이 만들어집니다.

    dotnet ef 도구는 모든 플랫폼에서 지원됩니다. Windows의 Visual Studio에서는 통합 패키지 관리자 콘솔 창의 Add-MigrationUpdate-Database PowerShell cmdlet을 사용할 수도 있습니다.

데이터베이스 검사

EF Core가 앱을 위한 데이터베이스를 만들었습니다. 다음으로, SQLite 확장을 사용하여 데이터베이스 내부를 살펴보겠습니다.

  1. Explorer 창에서 ContosoPizza.db 파일을 마우스 오른쪽 단추로 클릭하고 데이터베이스 열기를 선택합니다.

    Visual Studio Code Explorer 창에서 데이터베이스 열기 메뉴 옵션을 보여 주는 스크린샷.

    Explorer 창에 SQLite Explorer 폴더가 나타납니다.

    Explorer 창에서 SQLite Explorer 폴더를 보여 주는 스크린샷.

  2. SQLite Explorer 폴더를 선택하여 노드와 모든 자식 노드를 확장합니다. ContosoPizza.db를 마우스 오른쪽 단추로 클릭하고 테이블 ‘sqlite_master’ 표시를 선택하여 마이그레이션에서 만든 전체 데이터베이스 스키마와 제약 조건을 확인합니다.

    Explorer 창에서 SQLite Explorer 폴더를 보여 주는 스크린샷.

    • 각 엔터티에 해당하는 테이블이 만들어졌습니다.
    • 테이블 이름은 DbSetPizzaContext에 있는 속성의 이름에서 가져온 것입니다.
    • Id라는 속성이 기본 키 필드를 자동으로 증가시키는 것으로 유추되었습니다.
    • EF Core 기본 키와 외래 키 제약 조건 명명 규칙은 각각 PK_<primary key property>FK_<dependent entity>_<principal entity>_<foreign key property>입니다. <dependent entity><principal entity> 자리 표시자는 엔터티 클래스 이름에 해당합니다.

    참고

    ASP.NET Core MVC와 마찬가지로 EF Core는 설정보다 관례 접근 방식을 사용합니다. EF Core 규칙은 개발자의 의도를 유추하여 개발 시간을 단축합니다. 예를 들어 Id 또는 <entity name>Id라는 속성은 생성된 테이블의 기본 키로 유추됩니다. 명명 규칙을 채택하지 않기로 선택한 경우 속성에 [Key] 특성으로 주석을 달거나 DbContextOnModelCreating 메서드의 키로 구성해야 합니다.

모델 변경 및 데이터베이스 스키마 업데이트

Contoso Pizza의 관리자가 새로운 요구 사항 몇 가지를 제시하여 엔터티 모델을 변경해야 합니다. 다음 단계에서는 데이터 주석이라고도 하는 매핑 특성을 사용하여 모델을 수정할 예정입니다.

  1. Models\Pizza.cs에서 다음과 같이 변경합니다.

    1. System.ComponentModel.DataAnnotations에 대한 using 지시문을 추가합니다.
    2. Name 속성 앞에 [Required] 특성을 추가하여 속성을 필수로 표시합니다.
    3. Name 속성 앞에 [MaxLength(100)] 특성을 추가하여 최대 문자열 길이 100을 지정합니다.
    using System.ComponentModel.DataAnnotations;
    
    namespace ContosoPizza.Models;
    
    public class Pizza
    {
        public int Id { get; set; }
    
        [Required]
        [MaxLength(100)]
        public string? Name { get; set; }
    
        public Sauce? Sauce { get; set; }
    
        public ICollection<Topping>? Toppings { get; set; }
    }
    
  2. Models\Sauce.cs에서 다음과 같이 변경합니다.

    1. System.ComponentModel.DataAnnotations에 대한 using 지시문을 추가합니다.
    2. Name 속성 앞에 [Required] 특성을 추가하여 속성을 필수로 표시합니다.
    3. Name 속성 앞에 [MaxLength(100)] 특성을 추가하여 최대 문자열 길이 100을 지정합니다.
    4. IsVegan이라는 bool 속성을 추가합니다.
    using System.ComponentModel.DataAnnotations;
    
    namespace ContosoPizza.Models;
    
    public class Sauce
    {
        public int Id { get; set; }
    
        [Required]
        [MaxLength(100)]
        public string? Name { get; set; }
    
        public bool IsVegan { get; set; }
    }
    
  3. Models\Topping.cs에서 다음과 같이 변경합니다.

    1. System.ComponentModel.DataAnnotationsSystem.Text.Json.Serialization에 대한 using 지시문을 추가합니다.
    2. Name 속성 앞에 [Required] 특성을 추가하여 속성을 필수로 표시합니다.
    3. Name 속성 앞에 [MaxLength(100)] 특성을 추가하여 최대 문자열 길이 100을 지정합니다.
    4. Name 속성 바로 뒤에 Calories라는 decimal 속성을 추가합니다.
    5. 유형 ICollection<Pizza>?Pizzas 속성을 추가하여 Pizza-Topping을 다 대 다 관계로 설정합니다.
    6. Pizzas 속성에 [JsonIgnore] 특성을 추가합니다.

    중요

    이 단계는 웹 API 코드가 응답을 JSON으로 직렬화할 때 Topping 엔터티가 Pizzas 속성을 포함하지 않도록 하기 위한 것입니다. 이런 변경 사항 없이는 직렬화된 토핑 컬렉션에는 해당 토핑을 사용하는 모든 피자의 컬렉션이 포함됩니다. 이 컬렉션의 각 피자에는 토핑 컬렉션이 포함되며, 각 피자에는 다시 피자 컬렉션이 포함됩니다. 이 유형의 무한 루프는 “순환 참조”라고 하며 직렬화할 수 없습니다.

    using System.ComponentModel.DataAnnotations;
    using System.Text.Json.Serialization;
    
    namespace ContosoPizza.Models;
    
    public class Topping
    {
        public int Id { get; set; }
    
        [Required]
        [MaxLength(100)]
        public string? Name { get; set; }
    
        public decimal Calories { get; set; }
    
        [JsonIgnore]
        public ICollection<Pizza>? Pizzas { get; set; }
    }
    
  4. 모든 변경 내용을 저장하고 dotnet build를 실행합니다.

  5. 다음 명령을 실행하여 테이블을 만들기 위한 마이그레이션을 생성합니다.

    dotnet ef migrations add ModelRevisions --context PizzaContext
    

    ModelRevisions라는 마이그레이션이 만들어집니다.

    참고

    데이터가 손실을 초래할 수 있는 작업이 스캐폴드되었습니다. 마이그레이션이 정확한지 검토하세요라는 메시지가 표시됩니다. 이 메시지가 나타나는 이유는 Pizza에서 Topping으로 관계를 일대다에서 다대다로 변경하여 기존 외래 키 열을 삭제해야 하기 때문입니다. 데이터베이스에 아직 데이터가 없으므로 이 변경 내용은 문제가 되지 않습니다. 그러나 일반적으로 이 경고가 표시되면 생성된 마이그레이션을 확인하여 마이그레이션에 의해 데이터가 삭제되거나 잘리지 않도록 하는 것이 좋습니다.

  6. 다음 코드를 실행하여 ModelRevisions 마이그레이션을 적용합니다.

    dotnet ef database update --context PizzaContext
    
  7. SQLite Explorer 창의 제목 표시줄에서 데이터베이스 새로 고침 단추를 선택합니다.

    SQLite Explorer의 제목 표시줄에 있는 데이터베이스 새로 고침 버튼을 보여 주는 스크린샷.

  8. SQLite Explorer 창에서 ContosoPizza.db를 마우스 오른쪽 단추로 클릭합니다. 테이블 'sqlite_master' 표시를 선택하여 전체 데이터베이스 스키마 및 제약 조건을 확인합니다.

    중요

    SQLite 확장은 열려 있는 SQLite 탭을 다시 사용합니다.

    • 피자와 토핑 간의 “다대다” 관계를 나타내기 위해 PizzaTopping 조인 테이블이 만들어졌습니다.
    • ToppingsSauces에 새 필드가 추가됩니다.
      • Calories는 SQLite에 일치하는 decimal 형식이 없으므로 text 열로 정의됩니다.
      • 마찬가지로 IsVeganinteger 열로 정의됩니다. SQLite는 bool 형식을 정의하지 않습니다.
      • 두 경우 모두 EF Core가 변환을 관리합니다.
    • 각 테이블의 Name 열이 not null로 표시되었지만 SQLite에는 MaxLength 제약 조건이 없습니다.

    EF Core 데이터베이스 공급자는 모델 스키마를 특정 데이터베이스의 기능에 매핑합니다. SQLite는 MaxLength에 해당하는 제약 조건을 구현하지 않지만 SQL Server 및 PostgreSQL처럼 다른 데이터베이스는 해당 제약 조건을 구현합니다.

  9. SQLite Explorer 창에서 _EFMigrationsHistory 테이블을 마우스 오른쪽 단추로 클릭하고 테이블 표시를 선택합니다. 테이블에는 데이터베이스에 적용된 모든 마이그레이션 목록이 포함되어 있습니다. 마이그레이션을 두 번 실행했으므로 InitialCreate 마이그레이션에 대한 항목과 ModelRevisions에 대한 항목이라는 두 가지 항목이 있습니다.

참고

이 연습에서는 매핑 특성(데이터 주석)을 사용하여 모델을 데이터베이스에 매핑했습니다. 매핑 특성 대신 ModelBuilder fluent API를 사용하여 모델을 구성할 수 있습니다. 두 방법 모두 유효하지만 일부 개발자는 다른 방법보다 한 가지 접근 방식을 선호합니다.

마이그레이션을 사용하여 데이터베이스 스키마를 정의하고 업데이트했습니다. 다음 단원에서는 PizzaService에서 데이터를 조작하는 메서드를 완료합니다.

지식 점검

1.

엔터티 클래스에서 기본 키에 대한 속성 명명 규칙은 무엇입니까?