파트 6: 컨트롤러에서 모델 데이터에 접근하기

등록일시: 2015-05-18 08:00,  수정일시: 2015-05-20 23:11
조회수: 14,027
이 문서는 ASP.NET MVC 기술을 널리 알리고자 하는 개인적인 취지로 제공되는 번역문서입니다. 이 문서에 대한 모든 저작권은 마이크로소프트에 있으며 요청이 있을 경우 언제라도 게시가 중단될 수 있습니다. 번역 내용에 오역이 존재할 수 있고 주석은 번역자 개인의 의견일 뿐이며 마이크로소프트는 이에 관한 어떠한 보장도 하지 않습니다. 번역이 완료된 이후에도 대상 제품 및 기술이 개선되거나 변경됨에 따라 원문의 내용도 변경되거나 보완되었을 수 있으므로 주의하시기 바랍니다.

이번 단계에서는 새로운 MoviesController 클래스를 생성한 다음, 뷰 템플릿을 통해서 조회한 영화 데이터를 브라우저로 출력하는 코드를 작성해보겠습니다.

다만 본문의 과정을 진행하기 전에 우선 응용 프로그램을 빌드부터 하시기 바랍니다. 그렇지 않으면 컨트롤러를 추가할 때 오류가 발생할 수도 있습니다.

먼저 솔루션 탐색기(Solution Explorer)에서 마우스 오른쪽 버튼으로 Controllers 폴더를 클릭한 다음, 추가(Add)를 선택하고 다시 컨트롤러(Controller)를 선택합니다.

그러면 스캐폴드 추가(Add Scaffold) 대화 상자가 나타나는데, 목록에서 Entity Framework를 사용하며 뷰가 포함된 MVC 5 컨트롤러(MVC 5 Controller with views, using Entity Framework)를 선택하고 추가(Add) 버튼을 클릭합니다.

다시 컨트롤러 추가(Add Controller) 대화 상자가 나타나면 각각의 항목들을 다음과 같이 설정합니다.

  • 컨트롤러 이름(Controller name) 항목에는 MoviesController를 입력합니다.
  • 모델 클래스(Model class) 항목은 Movie (MvcMovie.Models)를 선택합니다.
  • 데이터 컨텍스트 클래스(Data context class) 항목은 MovieDBContext (MvcMovie.Models)를 선택합니다.

다음 그림은 이렇게 설정이 완료된 대화 상자를 보여줍니다.

역주: 업데이트 4까지 설치된 Visual Studio 2013에서는 이 대화 상자의 레이아웃이 일부 변경되어 제공됩니다.

마지막으로 추가(Add) 버튼을 클릭합니다. (만약 이때 오류가 발생한다면 컨트롤러를 추가하기 전에 응용 프로그램을 빌드하지 않았기 때문일 가능성이 높습니다.) 그러면 Visual Studio가 다음과 같은 파일들과 폴더들을 생성해줄 것입니다:

  • Controllers 폴더에 MoviesController.cs 파일이 생성됩니다.
  • Views\Movies 폴더가 생성됩니다.
  • 새로 생성된 Views\Movies 폴더에 Create.cshtml, Delete.cshtml, Details.cshtml, Edit.cshtml, Index.cshtml 파일들이 생성됩니다.

다시 말해서 Visual Studio가 여러분을 대신해서 자동으로 CRUD(Create, Read, Update, Delete) 액션 메서드들과 뷰들을 생성해주는 것입니다. (이렇게 CRUD 액션 메서드들과 뷰들을 자동으로 생성해주는 작업을 스캐폴딩(Scaffolding)이라고 합니다.) 결과적으로 영화 정보를 생성하고 조회하고 수정하고 삭제할 수 있는 완벽한 기능의 웹 응용 프로그램이 불과 몇 분의 짧은 시간만에 만들어진 셈입니다.

이제 응용 프로그램을 실행하고 MVC Movie 링크를 클릭해봅니다 (또는 브라우저 주소 표시줄에 나타나 있는 URL의 끝에 /Movies 를 추가해서 Movies 컨트롤러로 이동합니다). 이 예제 응용 프로그램은 기본 라우팅에 기반해서 (App_Start\RouteConfig.cs 파일에 정의된) 동작하므로 http://localhost:xxxxx/Movies 에 대한 브라우저의 요청은 Movies 컨트롤러의 Index 액션 메서드로 라우트됩니다. 즉 결론적으로 말해서 http://localhost:xxxxx/Movies 에 대한 요청과 http://localhost:xxxxx/Movies/Index 에 대한 브라우저 요청은 사실상 동일한 요청인 셈입니다. 물론 현재는 데이터베이스에 저장된 영화 정보가 전혀 없으므로 페이지에는 다음과 같이 빈 영화 목록만 나타나게 됩니다.

영화 정보 생성하기

이번에는 목록 페이지에서 Create New 링크를 클릭합니다. 그리고 적당한 영화 정보를 입력한 다음, 다시 Create 버튼을 클릭해서 저장합니다.

노트: 여러분의 환경에 따라 Price 항목에 소수점이나 쉼표(",")를 올바르게 입력할 수 없는 경우가 있습니다. 쉼표를 소수점으로 사용하는 비-영어권 로케일과 비 US-English 날짜 형식에 대해서 jQuery 유효성 검사 기능을 정상적으로 지원하기 위해서는 globalize.js 파일과 적합한 cultures/globalize.cultures.js 파일(https://github.com/jquery/globalize)을 참조해야 하고 Globalize.parseFloat 함수를 사용해서 약간의 JavaScript 코드도 작성해야 합니다. 그 구체적인 방법은 다음 단계에서 살펴보도록 하겠습니다. 이런 문제가 발생하는 경우 일단 지금은 간단히 10 같은 정수를 입력하시기 바랍니다.

그러면 Create 버튼을 클릭하는 순간 폼이 서버로 제출되어 영화 정보가 데이터베이스에 저장됩니다. 그런 다음 목록을 통해서 새로 생성된 영화 정보를 확인할 수 있도록 다시 /Movies URL로 재전송됩니다.

영화 정보를 몇 개 생성해보면서 테스트를 더 해보시기 바랍니다. 그리고 Edit, Details, Delete 링크를 사용해서 나머지 다른 기능들도 모두 테스트 해보시기 바랍니다.

생성된 코드 살펴보기

이번에는 Controllers\MoviesController.cs 파일을 열고 자동으로 생성된 Index 메서드의 코드를 자세히 살펴보도록 하겠습니다. 컨트롤러에 생성된 Index 메서드 부분의 코드는 다음과 같습니다.

public class MoviesController : Controller
{
    private MovieDBContext db = new MovieDBContext();

    // GET: /Movies/
    public ActionResult Index()
    {
        return View(db.Movies.ToList());
    }

브라우저로부터 Movies 컨트롤러에 대한 요청이 전달되면 이 Index 메서드가 Movies 테이블에 존재하는 모든 영화 정보 항목들을 가져온 다음, 그 결과를 Index 뷰에 전달해줍니다. 참고로 MoviesController 클래스에 작성된 다음 라인은 이전 과정에서 살펴봤던 영화 데이터베이스 컨텍스트의 인스턴스를 생성하는 코드입니다. 바로 이 영화 데이터베이스 컨텍스트를 이용해서 영화 정보를 조회하거나 수정하고 삭제할 수 있습니다.

private MovieDBContext db = new MovieDBContext();

강력한 형식의 모델과 @model 키워드

컨트롤러에서 ViewBag 개체를 이용해서 뷰 템플릿으로 데이터나 개체를 전달하는 방법에 관해서는 이미 이전 과정의 마지막 부분에서 살펴봤습니다. ViewBag 개체는 동적 개체로 뷰에 정보를 전달할 수 있는 편리한 런타임 바인딩(Late-Bound) 기능을 제공해줍니다.

그러나 MVC에서는 강력한 형식의 개체를 뷰 템플릿에 전달할 수도 있습니다. 강력한 형식에 기반한 이 접근 방식은 작성된 코드에 대한 보다 훌륭한 컴파일 시점 검사를 가능하게 해주고, Visual Studio 편집기에서 풍부한 인텔리센스 기능을 제공해줍니다. 가령 본문의 앞 부분에서 메서드들과 뷰들을 생성할 때, Visual Studio의 스캐폴딩 메커니즘이 바로 이 방식을 이용해서 (즉, 강력한 혁식의 모델을 전달하는 방식으로) MoviesController 클래스와 뷰 템플릿들을 생성한 것입니다.

그러면 이번에는 Controllers\MoviesController.cs 파일에 생성된 Details 메서드를 살펴보도록 하겠습니다. Details 메서드의 코드는 다음과 같습니다.

public ActionResult Details(int? id)
{
    if (id == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }

    Movie movie = db.Movies.Find(id);
    if (movie == null)
    {
        return HttpNotFound();
    }

    return View(movie);
}

일반적으로 Details 메서드의 id 매개변수에는 라우트 데이터가 전달되는데, 가령 http://localhost:1234/movies/details/1 URL의 경우 컨트롤러에는 Movies 컨트롤러가, 액션 메서드로는 details 메서드가, 그리고 id 라우트 변수에는 1이 설정됩니다. 그러나 다음과 같이 쿼리 문자열을 통해서 id 값을 전달할 수도 있습니다:

http://localhost:1234/movies/details?id=1

전달된 id에 대한 영화 정보가 발견되면 다음과 같이 그에 해당하는 Movie 모델의 인스턴스가 Details 뷰로 전달됩니다:

return View(movie);

계속해서 Views\Movies\Details.cshtml 파일의 내용을 살펴보겠습니다:

@model MvcMovie.Models.Movie

@{
    ViewBag.Title = "Details";
}

<h2>Details</h2>

<div>
    <h4>Movie</h4>
    <hr />
    <dl class="dl-horizontal">
        <dt>
            @Html.DisplayNameFor(model => model.Title)
        </dt>
        @*Markup omitted for clarity.*@       
    </dl>
</div>
<p>
    @Html.ActionLink("Edit", "Edit", new { id = Model.ID }) |
    @Html.ActionLink("Back to List", "Index")
</p>

뷰 템플릿 파일의 상단에 @model 구문을 추가하면 뷰가 기대하고 있는 개체의 형식을 지정할 수 있습니다. Visual Studio는 Movies 컨트롤러 클래스를 생성하면서 Details.cshtml 파일 상단에 다음과 같은 @model 구문을 자동으로 추가해줍니다:

@model MvcMovie.Models.Movie

이런 방식으로 @model 지시자를 작성함으로서 강력한 형식인 Model 개체를 이용해서 컨트롤러에서 뷰로 전달된 영화 정보에 접근할 수 있게 됩니다. Details.cshtml 뷰 템플릿의 코드도 이렇게 강력한 형식의 Model 개체를 이용해서 영화 정보 필드들을 DisplayNameFor 헬퍼 메서드와 DisplayFor 헬퍼 메서드에 전달합니다. 또한 Create 메서드와 Edit 메서드 및 관련 뷰 템플릿들 역시 영화 모델 개체를 전달하고 전달받습니다.

다시 Index.cshtml 뷰 템플릿과 MoviesController.cs 파일의 Index 메서드를 살펴보도록 하겠습니다. 특히 Index 액션 메서드에서 View 메서드를 호출할 때, List 개체를 생성하는 방식을 자세히 살펴보시기 바랍니다. 이 코드는 생성된 Movies 목록을 Index 액션 메서드에서 뷰로 전달하게 됩니다:

public ActionResult Index()
{
    return View(db.Movies.ToList());
}

그리고 Visual Studio는 Movies 컨트롤러 클래스를 생성하면서 Index.cshtml 파일의 상단에 자동으로 다음과 같은 @model 구문을 추가합니다:

@model IEnumerable<MvcMovie.Models.Movie> 

역시 이번에도 이 @model 지시자로 인해서 강력한 형식인 Model 개체를 통해서 컨트롤러에서 뷰로 전달된 영화 정보의 목록에 접근할 수 있게 됩니다. 가령 Index.cshtml 템플릿의 코드는 foreach 구문을 이용해서 강력한 형식인 Model 개체의 영화 정보 목록에 대해 반복 작업을 수행합니다:

@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>
        <th>
            @Html.DisplayFor(modelItem => item.Rating)
        </th>
        <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>
}

이 코드에서 Model 개체는 강력한 형식의 개체, 즉 IEnumerable<Movie> 개체이므로 루프 내부의 item 개체는 Movie 형식일 수 밖에 없습니다. 결과적으로 다른 여러 가지 장점들을 비롯해서 컴파일 시점 검사와 더불어 코드 편집기에서의 완벽한 인텔리센스 지원을 받을 수 있습니다.

SQL Server LocalDB를 이용해서 작업하기

Entity Framework Code First는 제공된 연결 문자열이 아직 존재하지 않는 Movies 데이터베이스를 지정하고 있다는 사실을 인식해서 자동으로 이 데이터베이스를 생성합니다. App_Data 폴더를 살펴보면 생성된 데이터베이스를 확인할 수 있습니다. 만약 Movies.mdf 파일이 보이지 않으면 솔루션 탐색기(Solution Explorer)의 툴바에서 모든 파일 표시(Show All Files) 버튼을 클릭한 다음, 새로 고침(Refresh) 버튼을 클릭하고 App_Data 폴더를 다시 살펴보십시오.

또한 Movies.mdf 파일을 마우스로 더블 클릭하면 서버 탐색기(Server Explorer) 창이 열리는데, 테이블(Tables) 폴더를 확장해서 Movies 테이블을 살펴보시기 바랍니다. 이때 ID 컬럼 옆에 키 아이콘이 표시된다는 점에 주의하시기 바랍니다. 그 이유는 Entity Framework가 기본적으로 ID라는 이름을 가진 속성을 기본 키로 설정하기 때문입니다. Entity Framework와 MVC에 관한 더 자세한 정보는 Tom Dykstra가 집필한 훌륭한 Creating an Entity Framework Data Model for an ASP.NET MVC Application 자습서를 참고하시기 바랍니다.

마우스 오른쪽 버튼으로 Movies 테이블을 클릭하고 테이블 데이터 표시(Show Table Data)를 선택하면 생성된 데이터들을 직접 확인할 수 있습니다.

그리고 마우스 오른쪽 버튼으로 Movies 테이블을 클릭한 다음, 테이블 정의 열기(Open Table Definition)를 선택하면 Entity Framework Code First가 자동으로 생성한 테이블 구조를 살펴볼 수 있습니다.

본 자습서의 이전 과정에서 작성했던 Movie 클래스와 Movies 테이블의 스키마가 어떻게 서로 매핑되는지 주의 깊게 살펴보시기 바랍니다. Entity Framework Code First는 Movie 클래스를 기반으로 이 스키마를 자동으로 생성합니다.

모든 작업을 마쳤으면 MovieDBContext 노드를 마우스 오른쪽 버튼으로 클릭한 다음, 연결 닫기(Close Connection)를 클릭하여 연결을 닫습니다.

역주: 원문에는 이렇게 연결을 닫지 않으면 다음 번에 프로젝트를 실행할 때 오류가 발생할 수도 있다고 경고하고 있지만, 개인적으로 오류가 발생하는 상황을 경험해본 적은 없습니다.

이제 데이터를 출력하고 편집하고 수정하고 삭제할 수 있는 페이지들과 데이터베이스가 만들어졌습니다. 계속해서 다음 단계에서는 다른 스캐폴딩 코드들을 살펴보도록 하겠습니다. 그리고 MVC에서 Entity Framework를 활용하는 방법에 대한 보다 자세한 정보는 Creating an Entity Framework Data Model for an ASP.NET MVC Application 자습서를 참고하시기 바랍니다.

이 기사는 2013년 10월 17일에 최초 작성되었습니다.