파트 7: Movie 모델과 테이블에 새로운 필드 추가하기
- 본 번역문서의 원문은 Adding a New Field to the Movie Model and Table www.asp.net 입니다.
- 본 번역문서는 ASP.NET MVC 4 자습서 - 만들면서 배우기 (IV, 완결) www.taeyo.net 에서도 함께 제공됩니다.
이번에는 모델 클래스를 일부 변경한 다음, 그 결과를 데이터베이스 스키마에 반영하는 방법을 살펴보도록 하겠습니다.
Movie 모델에 Rating 속성 추가하기
먼저, 기존 Movie
클래스에 새로운 Rating
속성을 추가하겠습니다.
Models\Movie.cs 파일을 연 다음, 다음과 같은 Rating
속성을 추가합니다:
public string Rating { get; set; }
이 작업을 마치고 나면 Movie
클래스의 모습은 다음과 비슷할 것입니다:
public class Movie { public int ID { get; set; } public string Title { get; set; } public DateTime ReleaseDate { get; set; } public string Genre { get; set; } public decimal Price { get; set; } public string Rating { get; set; } }
그런 다음, Build > Build Movie 메뉴를 선택해서 응용 프로그램을 다시 컴파일합니다.
이제, Model
클래스를 변경했으므로 \Views\Movies\Index.cshtml 뷰 템플릿과 \Views\Movies\Create.cshtml 뷰 템플릿을 변경해서 추가된 Rating
속성을 브라우저에 출력해야 합니다.
먼저, \Views\Movies\Index.cshtml 파일을 열고, Price 컬럼 헤더 뒷 부분에 <th>Rating</th>
컬럼 헤더를 추가합니다.
그리고, @item.Rating
값을 렌더할 <td>
컬럼을 템플릿 끝 부분에 추가합니다.
다음은 변경을 마친 Index.cshtml 뷰 템플릿의 모습을 보여주고 있습니다:
@model IEnumerable<MvcMovie.Models.Movie> @{ ViewBag.Title = "Index"; } <h2>Index</h2> <p> @Html.ActionLink("Create New", "Create") </p> <table> <tr> <th> @Html.DisplayNameFor(model => model.Title) </th> <th> @Html.DisplayNameFor(model => model.ReleaseDate) </th> <th> @Html.DisplayNameFor(model => model.Genre) </th> <th> @Html.DisplayNameFor(model => model.Price) </th> <th> @Html.DisplayNameFor(model => model.Rating) </th> <th></th> </tr> @foreach (var item in Model) { <tr> <td> @Html.DisplayFor(modelItem => item.Title) </td> <td> @Html.DisplayFor(modelItem => item.ReleaseDate) </td> <td> @Html.DisplayFor(modelItem => item.Genre) </td> <td> @Html.DisplayFor(modelItem => item.Price) </td> <td> @Html.DisplayFor(modelItem => item.Rating) </td> <td> @Html.ActionLink("Edit", "Edit", new { id=item.ID }) | @Html.ActionLink("Details", "Details", new { id=item.ID }) | @Html.ActionLink("Delete", "Delete", new { id=item.ID }) </td> </tr> } </table>
이번에는 \Views\Movies\Create.cshtml 파일을 열고, 폼의 마지막 부분에 다음과 같은 마크업을 추가합니다. 이 마크업은 텍스트 박스를 렌더해주는데, 새로운 영화 정보를 생성할 때, 이 텍스트 박스를 사용해서 등급을 입력할 수 있습니다.
<div class="editor-label"> @Html.LabelFor(model => model.Rating) </div> <div class="editor-field"> @Html.EditorFor(model => model.Rating) @Html.ValidationMessageFor(model => model.Rating) </div>
모델과 데이터베이스 스키마 간의 차이점 관리하기
지금까지 새로운 Rating
속성 지원을 위해서 응용 프로그램의 코드를 수정했습니다.
다시 응용 프로그램을 실행한 다음, /Movies URL로 이동해보겠습니다. 그러면, 다음과 같은 두 가지 오류 중 한 가지가 발생하는 것을 보게 될 것입니다:
이런 오류들이 발생하는 이유는, 지금 막 변경한 응용 프로그램의 Movie
모델 클래스와 데이터베이스의 Movie
테이블 스키마가 서로 일치하지 않기 때문입니다.
(데이터베이스의 테이블에는 아직 Rating
속성에 해당하는 컬럼이 존재하지 않습니다.)
이전 강좌에서 살펴본 것처럼, Entity Framework Code First를 이용해서 데이터베이스를 자동생성하면, 생성된 데이터베이스의 스키마와 그에 대응하는 모델 클래스 사이에 동기화가 필요한지 여부를 지속적으로 판단하기 위한 테이블이 데이터베이스에 기본적으로 함께 추가됩니다. 그리고, 만약 동기화가 되어 있지 않은 것이 발견되면, 지금처럼 Entity Framework가 오류를 발생시키게 됩니다. 결과적으로 뒤늦게 런타임에 가서야 발견되었을 지도 모를 (모호한 오류로 인한) 문제점을 개발 시점에 손쉽게 추적할 수 있는 것입니다. 바로, 이런 동기화 점검(Sync-Checking) 기능으로 인해서 방금 살펴본 오류 메시지가 출력된 것입니다.
이 오류를 해결하기 위한 접근방법은 두 가지가 존재합니다:
- Entity Framework가 자동으로 데이터베이스를 드랍시킨 다음, 새로운 모델 클래스 스키마를 기준으로 데이터베이스를 재생성하도록 만드는 방법입니다. 이 방법은 모델과 데이터베이스를 신속하게 변경할 수 있으므로, 테스트 데이터베이스에서 개발이 한창 진행 중일 때 매우 편리합니다. 그러나, 단점은 데이터베이스에 존재하던 기존 데이터들이 사라지게 되므로, 절대로 운영 데이터베이스에서는 사용하면 안된다는 것입니다!
- 명시적으로 직접 데이터베이스의 스키마를 변경하여 모델 클래스와 일치시키는 방법입니다. 이 방법의 장점은 기존 데이터를 유지할 수 있다는 점입니다. 직접 데이터베이스를 변경하거나, 데이터베이스 변경 스크립트를 작성해서 수행할 수 있습니다.
본 자습서에서는 첫 번째 방법을 사용해서, 모델 클래스가 변경될 때마다 Entity Framework Code First가 자동으로 데이터베이스를 재생성하도록 만들어 보겠습니다.
변경된 모델에 따라 자동으로 데이터베이스 재생성하기
그러면 지금부터 응용 프로그램의 모델이 변경될 때마다, Code First가 자동으로 데이터베이스를 드랍시키고 재생성할 수 있도록 응용 프로그램을 변경해보겠습니다.
디버거를 중지하고, Solution Explorer에서 Models 폴더를 마우스 오른쪽 버튼으로 클릭한 다음, Add를 클릭하고, 다시 New Item을 선택합니다.
잠시 후 Add New Item 대화 상자가 나타나면, Class를 선택한 다음, 클래스의 이름을 "MovieInitializer"로 지정합니다.
그리고, 다음의 코드로 MovieInitializer
클래스의 내용을 대체합니다:
using System; using System.Collections.Generic; using System.Data.Entity; namespace MvcMovie.Models { public class MovieInitializer : DropCreateDatabaseIfModelChanges<MovieDBContext> { protected override void Seed(MovieDBContext context) { var movies = new List<Movie> { new Movie { Title = "When Harry Met Sally", ReleaseDate=DateTime.Parse("1989-1-11"), Genre="Romantic Comedy", Rating="R", Price=7.99M }, new Movie { Title = "Ghostbusters", ReleaseDate=DateTime.Parse("1984-3-13"), Genre="Comedy", Rating="R", Price=8.99M }, new Movie { Title = "Ghostbusters 2", ReleaseDate=DateTime.Parse("1986-2-23"), Genre="Comedy", Rating="R", Price=9.99M }, new Movie { Title = "Rio Bravo", ReleaseDate=DateTime.Parse("1959-4-15"), Genre="Western", Rating="R", Price=3.99M }, }; movies.ForEach(d => context.Movies.Add(d)); } } }
이 MovieInitializer
클래스는 모델 클래스가 변경되면, 모델이 사용하는 데이터베이스를 자동으로 드랍시키고 재생성하도록 지시합니다.
이 코드에서처럼 DropCreateDatabaseIfModelChanges 이니셜라이져를 상속 받으면 스키마가 변경된 경우에만 데이터베이스가 재생성됩니다.
또는, DropCreateDatabaseAlways 이니셜라이저를 상속 받아서, 응용 프로그램 도메인에서 컨텍스트가 처음 사용될 때마다 항상 데이터베이스를 다시 생성하고 데이터를 초기화시킬 수도 있습니다.
DropCreateDatabaseAlways 이니셜라이저 방식은 특정 통합 테스트 시나리오에서 아주 유용합니다.
그리고, 이 MovieInitializer
클래스에 작성된 Seed
메서드는, 데이터베이스가 생성(또는 재생성) 될 때마다 자동으로 데이터베이스에 추가될 약간의 기본 데이터들을 지정합니다.
이 메서드를 작성해 놓으면, 모델이 변경될 때마다 매번 수작업으로 데이터를 입력할 필요 없이 원하는 테스트 데이터로 편리하게 데이터베이스를 채울 수 있습니다.
클래스 정의를 마쳤으므로, 이제 매번 응용 프로그램이 실행될 때마다 모델 클래스와 데이터베이스의 스키마가 다른지 검토할 수 있도록 MovieInitializer
클래스를 구성할 차례입니다.
검토를 해 본 결과, 동기화가 필요한 상태라면 이니셜라이저를 실행해서 데이터베이스를 모델과 일치하도록 재생성하고, 기본 데이터를 입력하게 됩니다.
먼저, Global.asax 파일을 엽니다:
이 Global.asax 파일에는 프로젝트의 응용 프로그램 전체를 정의하는 클래스 정의와, 응용 프로그램이 처음 시작될 때 실행되는 Application_Start
이벤트 핸들러가 들어 있습니다.
다음과 같이 Application_Start
메서드의 시작 부분에 Database.SetInitializer
호출 코드를 추가합니다:
protected void Application_Start() { Database.SetInitializer<MovieDBContext>(new MovieInitializer()); AreaRegistration.RegisterAllAreas(); // Use LocalDB for Entity Framework by default Database.DefaultConnectionFactory = new SqlConnectionFactory("Data Source=(localdb)\v11.0; Integrated Security=True; MultipleActiveResultSets=True"); RegisterGlobalFilters(GlobalFilters.Filters); RegisterRoutes(RouteTable.Routes); BundleTable.Bundles.RegisterTemplateBundles(); }
그리고, 빨간색 밑줄이 쳐진 부분을 (MovieDBContext
나 MovieInitializer
) 마우스 오른쪽 버튼으로 클릭한 다음, Resolve > using MvcMovie.Models; 선택합니다.
또는, 파일 상단에 직접 다음과 같은 using 문을 추가해도 됩니다.
다만 이 using 문은 MovieInitializer
클래스가 실제로 위치한 네임스페이스를 참조해야 합니다:
using MvcMovie.Models; // MovieInitializer
이렇게 구성을 해놓으면, 응용 프로그램이 시작될 때, 지금 막 추가한 Database.SetInitializer
구문으로 인해서 MovieDBContext
인스턴스와 관련된 데이터베이스의 스키마와 모델 클래스가 일치하지 않는 경우, 자동으로 데이터베이스가 삭제되었다가 재생성되게 됩니다.
그리고, 앞에서 설명한 것처럼 그 과정 중에 데이터베이스가 MovieInitializer
클래스에 지정된 기본 데이터로 채워지게 됩니다.
이제 Global.asax 파일을 닫습니다.
응용 프로그램을 다시 시작한 다음, /Movies URL로 이동해봅니다. 그러면, 응용 프로그램이 시작될 때, 모델의 구조와 데이터베이스 스키마가 더 이상 일치하지 않는다는 사실이 감지될 것입니다. 그에 따라, 새로운 모델 구조와 일치하도록 데이터베이스가 재생성되고 데이터베이스가 기본 영화 데이터로 채워지게 됩니다:
이번에는 Create New 링크를 클릭해서 새로운 영화 정보를 추가해보십시요. 이제는 등급도 지정할 수 있다는 점에 주목하시기 바랍니다.
적절한 정보들을 입력한 다음, Create를 클릭하면, 등급 정보가 포함된 새로운 영화 정보가 목록에 나타나는 것을 확인할 수 있을 것입니다:
마지막으로 Edit 뷰 템플릿에도 Rating
필드를 추가해줍니다.
지금까지 모델 개체를 변경하는 방법과, 모델의 해당 변경 사항을 데이터베이스에 반영하는 방법을 살펴봤습니다. 그리고, 특정 시나리오에서 새로 생성된 데이터베이스에 기본 데이터를 채워 넣을 수 있는 방법도 살펴봤습니다. 계속해서 모델 클래스에 다채로운 유효성 검사 로직을 추가하는 방법과 특정 업무 로직을 강제하는 방법을 살펴보도록 하겠습니다.
- 파트 1: ASP.NET MVC 4 및 Visual Studio 2011 소개 2012-06-08 10:47
- 파트 2: 컨트롤러 추가하기 2012-06-08 10:54
- 파트 3: 뷰 추가하기 2012-06-13 09:48
- 파트 4: 모델 추가하기 2012-06-13 09:48
- 파트 5: 컨트롤러에서 모델의 데이터에 접근하기 2012-06-19 11:48
- 파트 6: Edit 메서드와 Edit 뷰 살펴보기 2012-06-19 11:48
- 파트 7: Movie 모델과 테이블에 새로운 필드 추가하기 2012-07-03 10:49
- 파트 8: 모델에 유효성 검사 추가하기 2012-07-03 11:04
- 파트 9: Details 메서드 및 Delete 메서드 살펴보기 2012-07-03 11:07