파트 5: 폼 편집하기 및 템플릿 사용하기

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

MVC 뮤직 스토어 응용 프로그램은 ASP.NET MVC와 Visual Studio for Web Development를 소개하고, 그 사용법을 단계별로 살펴보기 위한 자습용 응용 프로그램으로, 온라인 음반 판매 기능을 비롯하여, 기초적인 사이트 관리, 사용자 로그인, 장바구니 기능 등이 구현된 간단한 전자상거래 사이트 예제입니다.

본 자습서 시리즈에서는 ASP.NET MVC 뮤직 스토어 응용 프로그램을 구축하기 위해서 필요한 모든 단계들을 자세하게 살펴볼 것입니다. 이번 파트 5에서는 폼 편집하기 및 템플릿 적용에 관해서 살펴봅니다.

이전 파트에서는 데이터베이스에서 가져온 데이터를 출력하는 방법을 살펴봤습니다. 계속해서 이번에는 데이터베이스의 데이터를 수정하는 방법을 살펴보도록 하겠습니다.

StoreManagerController 생성하기

먼저, StoreManagerController라는 이름의 새로운 컨트롤러를 만들어보도록 하겠습니다. 다만 이번에는 컨트롤러를 생성할 때, ASP.NET MVC 3 도구 업데이트에서 제공해주는 스캐폴딩 기능을 활용해보려고 합니다. 그러므로, Add Controller 대화 상자의 옵션들을 다음과 같이 설정합니다.

그런 다음, Add 버튼을 클릭해보면, ASP.NET MVC 3의 스캐폴딩 메커니즘이 여러분을 대신해서 다음과 같은 제법 많은 양의 작업들을 처리해주는 것을 확인할 수 있을 것입니다:

  • Entity Framework에 대한 지역 변수가 포함된 새로운 StoreManagerController를 생성해줍니다.
  • 프로젝트의 Views 폴더에 StoreManager 폴더를 추가해줍니다.
  • Album 클래스에 대한 강력한 형식인 Create.cshtml, Delete.cshtml, Details.cshtml, Edit.cshtml, 그리고 Index.cshtml 뷰를 추가해줍니다.

이렇게 만들어진 새로운 StoreManager 컨트롤러 클래스에는, Album 모델 클래스의 사용 방법을 인식하고 있는, Entity Framework 컨텍스트를 이용해서 데이터베이스에 접근하는, CRUD (Create, Read, Update, Delete) 컨트롤러 액션들이 포함되어 있습니다.

역주: 본 자습서를 지금까지 충실하게 따라해왔으며, 지금 막 설명한 단계를 그대로 수행했다면, 아마도 새로 만들어진 StoreManagerController 클래스에 오류가 존재할 것입니다. 즉, db.Artist라는 코드가 사용되는 모든 부분들에 빨간색 밑줄이 표시되어 있을 것입니다. 이 오류를 해결하려면 이번 컨트롤러를 생성하면서 자동으로 MusicStoreEntities 클래스에 추가된 Artist 속성을 Artists로 수정해줘야 합니다. 아마도, 원문의 편집 과정 등에서 내용이 누락된 것으로 보입니다.

스캐폴드로 생성된 뷰 수정하기

그런데, 한 가지 분명하게 집고 넘어가야 할 점은, 비록 이런 방식으로 코드를 자동생성하기는 했지만, 이 생성된 코드들은 지금까지 본 자습서에서 작성해왔던 다른 코드들과 마찮가지로 아주 기본적인 ASP.NET MVC 코드일 뿐이라는 것입니다. 기초적인 컨트롤러 코드를 직접 작성하거나, 강력한 형식의 뷰를 반복적으로 작성하는데 걸리는 시간들을 최소화시키기 위한 것일뿐, 코드를 변경하면 안된다거나 하는 심각해보이는 경고를 남발하기 위한 의도로 제시되는 코드가 아니라는 뜻입니다. 이 코드의 주인은 당연히 여러분이며, 각자 의도하는대로 수정할 수 있어야만 합니다.

먼저, StoreManager의 Index 뷰를 (/Views/StoreManager/Index.cshtml) 조금 변경해보도록 하겠습니다. 이 뷰는 MVC 뮤직 스토어에 존재하는 음반들의 목록을 보여주는데, 이 목록에서는 각각의 음반(Album 클래스)들의 public 속성들과 Edit / Details / Delete 링크들이 제공됩니다. 그 중에서 AlbumArtUrl 필드는 그다지 중요하지 않으므로 제거해보도록 하겠습니다. 뷰 코드에서 다음에 강조되어 있는, 즉 <table> 영역의 AlbumArtUrl 참조를 둘러싸고 있는, <th>와 <td> 요소를 제거합니다:

<table> 
    <tr> 
        <th>  
            Genre 
        </th> 
        <th> 
            Artist 
        </th> 
        <th> 
            Title 
        </th> 
        <th> 
            Price 
        </th> 
        <th> 
            AlbumArtUrl 
        </th> 
        <th></th> 
    </tr>  
@foreach (var item in Model) {  
    <tr> 
        <td> 
            @Html.DisplayFor(modelItem => item.Genre.Name)  
        </td> 
        <td> 
            @Html.DisplayFor(modelItem => item.Artist.Name)  
        </td> 
        <td> 
            @Html.DisplayFor(modelItem => item.Title)  
        </td> 
        <td> 
            @Html.DisplayFor(modelItem => item.Price)  
        </td> 
        <td> 
            @Html.DisplayFor(modelItem => item.AlbumArtUrl) 
        </td> 
        <td>  
            @Html.ActionLink("Edit", "Edit", new { id=item.AlbumId }) |  
            @Html.ActionLink("Details", "Details", new { id=item.AlbumId }) |  
            @Html.ActionLink("Delete", "Delete", new { id=item.AlbumId })  
        </td> 
    </tr> 
} 
</table>

모든 작업이 완료된 뷰의 코드는 다음과 같은 모습일 것입니다:

@model IEnumerable<MvcMusicStore.Models.Album>  
@{  
    ViewBag.Title = "Index";  
}

<h2>Index</h2> 
<p> 
    @Html.ActionLink("Create New", "Create") 
</p> 
<table> 
    <tr> 
        <th> 
            Genre 
        </th> 
        <th> 
            Artist 
        </th> 
        <th> 
            Title 
        </th> 
        <th> 
            Price 
        </th> 
        <th></th> 
    </tr>  
@foreach (var item in Model) { 
    <tr> 
        <td> 
            @Html.DisplayFor(modelItem => item.Genre.Name)  
        </td> 
        <td> 
            @Html.DisplayFor(modelItem => item.Artist.Name) 
        </td> 
        <td> 
            @Html.DisplayFor(modelItem => item.Title) 
        </td> 
        <td>  
            @Html.DisplayFor(modelItem => item.Price)  
        </td> 
        <td>  
            @Html.ActionLink("Edit", "Edit", new { id=item.AlbumId }) | 
            @Html.ActionLink("Details", "Details", new { id=item.AlbumId }) | 
            @Html.ActionLink("Delete", "Delete", new { id=item.AlbumId }) 
        </td> 
    </tr> 
} 
</table>

뮤직 스토어 관리자의 최초 모습

MVC 뮤직 스토어 응용 프로그램을 시작한 다음, /StoreManager/로 이동해봅니다. 그러면, 방금 수정한 뮤직 스토어 관리자의 Index 페이지가 나타나는데, 이 페이지에는 뮤직 스토어에 존재하는 모든 음반들의 목록이 Edit, Details, 그리고 Delete 링크와 더불어 제공됩니다.

목록에서 임의의 Edit 링크를 클릭해보면 Album 클래스의 필드들에 대한 편집 폼이 나타나는데, 그 중 Genre 속성과 Artist 속성은 드롭다운 형태로 제공됩니다.

역주: 만약, 이 Edit 페이지에 접근할 때, jQuery와 관련된 스크립트 오류가 발생한다면, _Layout.cshtml 파일에 참조된 jQuery 라이브러리의 버전 및 파일명과 프로젝트의 Scripts 폴더에 실제로 존재하는 jQuery 라이브러리의 파일명을 점검해보시기 바랍니다.

다시 페이지 하단의 "Back to List" 링크를 클릭해서 목록으로 이동한 다음, 특정 음반에 대한 Details 링크를 클릭해봅니다. 그러면, 다음과 같이 해당 음반에 대한 상세 정보가 나타납니다.

이번에도 역시 페이지 하단의 "Back to List" 링크를 클릭해서 목록으로 이동한 다음, 특정 음반에 대한 Delete 링크를 클릭해봅니다. 그러면, 해당 음반에 대한 상세 정보와 함께, 정말로 해당 음반 정보를 삭제할 것인지를 묻는 확인 페이지가 나타납니다.

페이지 하단의 Delete 버튼을 클릭하면 해당 음반의 정보가 삭제되고, 음반이 삭제된 것을 확인할 수 있는 Index 페이지로 되돌아가게 됩니다.

비록, 뮤직 스토어 관리자가 완성된 것은 아니지만, CRUD 작업을 개발하기 위한, 실제로 동작하는 기초적인 컨트롤러와 뷰 코드는 준비가 마무리된 셈입니다.

뮤직 스토어 관리자 코드 살펴보기

자동으로 생성된 StoreManagerController에는 이미 상당량의 코드가 작성되어 있습니다. 이 코드들을 가장 상단에서부터 마지막 부분까지 하나 씩 살펴보도록 하겠습니다. 이 컨트롤러에는 MVC 컨트롤러에 필요한 기본적인 몇 가지 네임스페이스들을 비롯해서, Models 네임스페이스에 대한 참조가 포함되어 있습니다. 그리고, 각 컨트롤러 액션에서 데이터 접근을 위해 사용되는, MusicStoreEntities 클래스의 private 인스턴스가 존재합니다.

using System; 
using System.Collections.Generic; 
using System.Data; 
using System.Data.Entity; 
using System.Linq; 
using System.Web; 
using System.Web.Mvc;
using MvcMusicStore.Models; 
   
namespace MvcMusicStore.Controllers 
{ 
    public class StoreManagerController : Controller 
    { 
        private MusicStoreEntities db = new MusicStoreEntities();

뮤직 스토어 관리자 Index 액션 및 Details 액션

Index 뷰에서는 Store 영역의 Browse 메서드에서 살펴봤던 것과 비슷한 방식으로, 각 음반들의 장르 및 아티스트 이름이 포함된 음반 목록을 출력해줍니다. 그런데, 음반들의 장르 및 아티스트 이름을 출력할 때, 관련 개체들의 참조를 사용하기 때문에, Index 컨트롤러 액션에서도 다음과 같이 원래의 요청에 더해서 이 정보들을 효율적으로 질의하고 있는 것을 확인할 수 있습니다.

// 
// GET: /StoreManager/ 
public ViewResult Index() 
{ 
    var albums = db.Albums.Include(a => a.Genre).Include(a => a.Artist); 
    return View(albums.ToList()); 
}

그리고, Details 컨트롤러 액션은 이전 파트에서 작성했던 Store 컨트롤러의 Details 액션과 정확하게 같은 방식으로 동작합니다. 즉, Find() 메서드를 이용해서 ID로 음반(Album 클래스)을 질의한 다음, 이를 뷰로 반환하는 것입니다.

// 
// GET: /StoreManager/Details/5 
public ViewResult Details(int id) 
{ 
    Album album = db.Albums.Find(id); 
    return View(album); 
}

Create 액션 메서드

이번에 살펴볼 Create 액션 메서드는 사용자의 입력을 처리해야 하기 때문에, 지금까지 살펴본 다른 액션 메서드들과는 약간 다른 점들이 존재하게 됩니다. 사용자가 /StoreManager/Create/를 처음 방문하면 비어 있는 폼을 보게 됩니다. 이 HTML 페이지에는 <form> 요소가 포함되어 있으며, <form> 요소에는 음반의 상세 정보를 입력할 수 있는 드롭다운 및 텍스트박스 입력 요소들이 포함되어 있습니다.

폼에 음반의 상세 정보를 입력한 다음, "Save" 버튼을 눌러서 변경된 내용들을 응용 프로그램에 제출하고 데이터베이스에 저장할 수 있습니다. 바로 이 때, <form> 요소는 /StoreManager/Create/ URL로 HTTP-POST 전송을 수행하게 되며, <form> 요소의 값들은 이 HTTP-POST 전송의 일부로 제출됩니다.

그리고, ASP.NET MVC에서는 StoreManagerController 클래스에 두 개의 "Create" 액션 메서드를 구현하는 방식으로, 지금과 같은 두 가지 URL 호출 시나리오 로직을 분리할 수 있습니다. 즉, /StoreManager/Create/ URL에 대한 최초의 HTTP-GET 이동을 처리해주는 액션 메서드와, HTTP-POST 전송으로 제출된 변경 사항들을 처리해주는 액션 메서드를 별도로 구현할 수가 있는 것입니다.

뷰백(ViewBag)을 이용하여 뷰에 정보 전달하기

본 자습서의 이전 파트에서 이미 뷰백을 사용해보기는 했지만, 지금까지 그다지 자세하게 설명하지는 않았었습니다. 뷰백을 사용하면 강력한 형식의 모델 개체를 사용하지 않고도 뷰로 정보를 전달할 수 있습니다. 이번 예제의 경우, Create HTTP-GET 컨트롤러 액션에서는 장르들의 목록과 아티스트들의 목록을 드롭다운에 적용하기 위해 뷰로 전달해야하는데, 가장 간단한 방법이 바로 뷰백 항목으로 이 목록들을 전달하는 것입니다.

뷰백은 동적 개체이므로, 속성을 정의하는 코드를 작성하지 않아도 ViewBag.Foo나 ViewBag.YourNameHere 등의 코드를 필요에 따라 마음대로 사용할 수 있습니다. StoreManager 컨트롤러의 HTTP-GET Create 메서드 코드에서는 ViewBag.GenreId와 ViewBag.ArtistId를 통해서 폼과 함께 제출될 GenreId 및 ArtistId 드롭다운 값들을 설정하는데, 이 값들은 나중에 Album 개체의 속성에 설정되게 됩니다.

이 드롭다운 값들은, 바로 이런 용도를 위해서 준비된 내장 SelectList 개체를 통해서 폼에 반환됩니다. 이 부분의 코드는 다음과 같습니다:

ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name");
ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name");  

이 액션 메서드 코드에서 확인할 수 있는 것처럼, SelectList 개체를 생성하기 위해서는 세 가지 매개변수가 사용됩니다:

  • 첫 번째 매개변수는 드롭다운에 출력될 항목들의 목록입니다. 단지 문자열이 아니라, 장르들의 목록 자체를 전달하고 있다는 점에 주의하십시요.
  • 두 번째 매개변수는 드롭다운 항목들의 값으로 사용될 속성입니다.
  • 세 번째 매개변수는 드롭다운 항목들의 텍스트로 출력될 속성입니다. 이 예제 코드에서는 Genre.Name 속성이 사용되고 있으므로, 이 속성의 값이 사용자들에게 보여지게 됩니다.

이런 사항들을 감안하고 코드를 살펴보면 HTTP-GET Create 액션은 매우 단순합니다. 두 개의 SelectList만 뷰백에 추가될 뿐, 폼에 전달될 모델 개체는 존재하지 않습니다. (지금 단계에서는 아직 모델 개체에 담을 음반 자체가 존재하지 않는 상태니까요.)

// 
// GET: /StoreManager/Create 
public ActionResult Create() 
{ 
    ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name"); 
    ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name"); 
    return View(); 
}

Create 뷰에서 드롭다운을 출력해주는 HTML 도우미

지금까지 드롭다운 값들을 뷰로 전달해주는 방법을 살펴봤으므로, 이번에는 이 값들을 출력하는 방법을 살펴보기 위해서 뷰를 검토해보도록 하겠습니다. 뷰 코드(/Views/StoreManager/Create.cshtml)를 살펴보면 장르의 드롭다운을 출력해주는 다음과 같은 메서드 호출을 찾아볼 수 있을 것입니다.

@Html.DropDownList("GenreId", String.Empty) 

이 메서드는 HTML 도우미로 알려져 있는, 일반적인 뷰 작업을 수행해주는 유틸리티 메서드들 중 하나입니다. 이런 HTML 도우미 메서드를 활용하면 뷰 코드를 매우 간결하고 읽기 쉽게 작성할 수 있습니다. 그리고, 이 예제 코드에 사용된 Html.DropDownList 도우미는 ASP.NET MVC에서 제공되는 내장 메서드지만, 나중에 살펴보게 될 것처럼 응용 프로그램에서 재사용하고자 하는 뷰 코드들을 직접 작성할 수도 있습니다.

이 Html.DropDownList 도우미 메서드를 호출하려면 두 가지 정보를 제공해줘야 합니다. 바로, 출력하려는 목록을 어디에서 가져올 것인지에 대한 정보와, (필요한 경우) 최초에 선택될 초기값 정보가 바로 그것입니다. 이 예제 코드에서 첫 번째 매개변수는 GenreId로, DropDownList 도우미에게 모델이나 뷰백에서 GenreId라는 이름을 가진 값을 찾도록 지시합니다. 그리고, 이 폼은 Create 폼이기 때문에, 드롭다운 목록의 초기값을 지정하기 위해 사용되는 두 번째 매개변수는 미리 선택할 값이 존재하지 않으므로 String.Empty를 지정합니다.

전송된 폼 값들을 처리하기

이미 앞에서 설명했던 것처럼, 대부분의 폼들은 각각 두 가지 액션 메서드와 연결되어 있습니다. 그 중, 첫 번째 메서드는 HTTP-GET 요청을 처리하고 폼을 출력해줍니다. 그리고, 두 번째 메서드는 제출된 폼 값들을 포함하고 있는 HTTP-POST 요청을 처리합니다. StoreManager 컨트롤러 코드 중에서 HTTP-POST 요청에 대해서만 응답하도록 ASP.NET MVC에게 지시하는 [HttpPost] 어트리뷰트가 적용되어 있는 Create 컨트롤러 액션을 주의해서 살펴보시기 바랍나다.

// 
// POST: /StoreManager/Create 
[HttpPost] 
public ActionResult Create(Album album) 
{ 
    if (ModelState.IsValid) 
    { 
        db.Albums.Add(album); 
        db.SaveChanges(); 
        return RedirectToAction("Index"); 
    } 
    ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name", album.GenreId); 
    ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name", album.ArtistId); 
    return View(album); 
}

이 컨트롤러 액션은 다음과 같은 네 가지 작업을 처리합니다:

  1. 폼의 값들을 읽습니다.
  2. 폼의 값들이 모든 유효성 검사 규칙을 만족하는지 점검합니다.
  3. 만약, 제출된 폼 값들이 유효하다면, 데이터를 저장하고 갱신된 목록을 출력해줍니다.
  4. 만약, 제출된 폼 값들이 유효하지 않다면, 유효성 검사 오류 메시지가 포함된 폼을 다시 출력합니다.

모델 바인딩을 이용해서 폼 값들을 읽기

이 컨트롤러 액션은 GenreId와 ArtistId의 드롭다운 값들과 Title과 Price, 그리고 AlbumArtUrl의 텍스트박스 값들이 포함된 폼 제출을 처리해줍니다. 물론, 컨트롤러 액션에서 이런 폼 값들에 보다 직접적인 방식으로 접근할 수도 있겠지만, ASP.NET MVC에 내장된 모델 바인딩(Model Binding) 기능을 이용하는 편이 더 좋습니다. 모델 형식이 컨트롤러 액션에 매개변수로 전달되는 경우, ASP.NET MVC는 폼의 입력값들(을 비롯한 라우트 및 쿼리스트링의 값들)을 이용해서 해당 형식의 개체를 채우려고 시도합니다. 이 작업은 모델 개체의 속성들과 이름이 일치하는 값들을 찾는 과정을 통해서 이루어지게 되는데, 가령 새로운 Album 개체의 GenreId 속성값을 설정하고자 하는 경우, 이름이 GenreId인 입력값을 찾으려고 시도하는 식입니다. 뷰 템플릿을 생성할 때, ASP.NET MVC의 표준 방식을 사용하면, 폼의 입력 필드 이름이 항상 모델 속성 이름으로 렌더되므로, 입력 필드의 이름과 해당 필드의 이름이 자연스럽게 일치하게 됩니다.

모델 유효성 검사하기

모델의 유효성 검증은 간단히 ModelState.IsValid를 호출함으로서 이루어집니다. 물론, 아직까지 Album 클래스에 어떠한 유효성 검사 규칙도 추가하지 않았기 때문에 (잠시 뒤에, 이 작업을 처리해보게 될 것입니다), 현재 이 과정 중에 그다지 많은 작업들이 처리되지는 않습니다. 다만, 중요한 점은 이 ModelStat.IsValid 검사가 모델에 적용되어 있는 모든 유효성 검사 규칙들을 알아서 처리해주기 때문에, 나중에 유효성 검사 규칙을 변경하더라도, 컨트롤러 액션의 코드는 전혀 변경할 필요가 없다는 점입니다.

제출된 값들을 저장하기

만약, 전송된 폼의 값들이 유효성 검사를 통과했다면, 이번에는 데이터베이스에 그 값들을 저장할 차례입니다. Entity Framework를 사용하는 경우, 이 작업을 수행하려면 다음 코드와 같이 모델을 Albums 컬렉션에 추가한 다음, SaveChanges 메서드를 호출하기만 하면 됩니다.

db.Albums.Add(album); 
db.SaveChanges();

그러면, Entity Framework가 적절한 SQL 명령을 생성하여 값들을 영속화시켜줍니다. 그리고, 데이터를 저장한 뒤에는 사용자를 음반 목록 페이지로 다시 재전송하여 수정된 내용을 확인할 수 있도록 합니다. 이 작업은 출력하고자 하는 컨트롤러 액션의 이름과 함께 RedirectToAction을 반환하여 이루어지는데, 여기에서는 Index 메서드가 바로 그 액션입니다.

유효성 검사 오류 메시지와 함께 유효하지 않은 폼 제출 출력하기

폼의 입력값들이 유효하지 않은 경우에는, 드롭다운 값들을 다시 뷰백에 추가하고 (HTTP-GET 메서드에서와 동일하게) 전체 모델 값을 한번 더 뷰로 전달하여 출력합니다. 이 때, 뷰에서 @Html.ValidationMessageFor HTML 도우미를 사용하면 유효성 검사 오류 메시지들이 자동으로 출력됩니다.

Create 폼 테스트하기

지금까지 살펴본 내용들을 테스트해보려면, 응용 프로그램을 실행한 다음, /StoreManager/Create/로 이동합니다. 그러면, StoreController의 Create HTTP-GET 메서드에 의해 반환된 빈 폼이 나타나게 됩니다.

이 폼에 임의의 값들을 입력한 다음, "Create" 버튼을 클릭하면 폼이 제출됩니다.

Edit 처리하기

Edit 액션 메서드들은 (HTTP-GET 및 HTTP-POST) 방금 살펴본 Create 액션 메서드의 경우와 매우 비슷합니다. 다만, 편집 시나리오에서는 기존 음반을 대상으로 작업이 수행되기 때문에, Edit HTTP-GET 메서드에서 라우트를 통해서 전달된 "id" 매개변수를 기반으로 음반 정보(Album 클래스의 인스턴스)를 로드하게 됩니다. AlbumId를 이용해서 음반 정보를 가져오는 이 코드는, 앞에서 Details 컨트롤러 액션에서 살펴봤던 코드와 매우 비슷합니다. 그리고, Create HTTP-GET 메서드에서처럼 뷰백을 이용해서 드롭다운 값들이 반환됩니다. 이런 방식을 통해서 뷰에 Album 클래스를 모델 개체로 전달하면서도 (Edit 뷰는 Album 클래스에 대한 강력한 형식의 뷰입니다), 그와 동시에 뷰백을 통해서 추가적인 데이터들을 (장르들의 목록 등) 함께 전달할 수 있습니다.

// 
// GET: /StoreManager/Edit/5 
public ActionResult Edit(int id) 
{ 
    Album album = db.Albums.Find(id); 
    ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name", album.GenreId); 
    ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name", album.ArtistId); 
    return View(album); 
}

그리고, Edit HTTP-POST 액션은 Create HTTP-POST 액션과 거의 똑같습니다. 유일한 차이점은 새로운 음반 정보를 db.Albums 컬랙션에 추가하는 대신, db.Entry(album)를 이용해서 Album의 현재 인스턴스를 찾은 다음, 그 상태를 Modified로 설정한다는 점 뿐입니다. 그러면, Entity Framework가 새로운 음반 정보를 만드는 대신, 기존 음반 정보를 수정하게 됩니다.

// 
// POST: /StoreManager/Edit/5 
[HttpPost] 
public ActionResult Edit(Album album) 
{ 
    if (ModelState.IsValid) 
    { 
        db.Entry(album).State = EntityState.Modified; 
        db.SaveChanges(); 
        return RedirectToAction("Index"); 
    } 
    ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name", album.GenreId); 
    ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name", album.ArtistId); 
    return View(album); 
}

테스트를 해보려면 응용 프로그램을 실행한 다음, /StoreManger/로 이동해서 특정 음반의 Edit 링크를 클릭해봅니다.

그러면, Edit HTTP-GET 메서드에 의해 반환된 Edit 폼이 나타납니다. 폼의 기존 값들을 임의의 값들로 변경한 다음, Save 버튼을 클릭합니다.

그러면 폼이 제출되고, 값들이 저장된 다음, 음반 목록에서 변경된 값들을 확인하실 수 있습니다.

Delete 처리하기

Delete 처리도 Edit 처리나 Create 처리와 동일한 형태를 따르고 있습니다. 다시 말해서, 첫 번째 컨트롤러 액션은 확인 폼을 출력해주고, 두 번째 컨트롤러 액션은 폼 전송(즉, 실제 삭제 작업)을 처리해줍니다.

HTTP-GET Delete 컨트롤러 액션의 코드는 앞에서 살펴본 StoreManager 컨트롤러의 Details 컨트롤러 액션의 코드와 똑같습니다.

// 
// GET: /StoreManager/Delete/5 
public ActionResult Delete(int id) 
{ 
    Album album = db.Albums.Find(id); 
    return View(album); 
}

그리고, Delete 뷰 컨텐트 템플릿을 이용해서 Album 형식에 강력한 형식인 폼을 출력합니다.

이 Delete 뷰 템플릿은 모델에 존재하는 모든 필드들을 출력하고 있지만, 본문의 예제에서는 그 내용을 상당히 간단하게 줄여도 무방합니다. /Views/StoreManager/Delete.cshtml 뷰 코드를 다음과 같이 변경합니다.

@model MvcMusicStore.Models.Album  
@{ 
    ViewBag.Title = "Delete";  
}

<h2>Delete Confirmation</h2> 
<p>Are you sure you want to delete the album titled 
    <strong>@Model.Title</strong>? 
</p>  
@using (Html.BeginForm()) {  
    <p> 
        <input type="submit" value="Delete" /> 
    </p> 
    <p>  
        @Html.ActionLink("Back to List", "Index")  
    </p>  
}

그러면, 다음과 같이 단순해진 삭제 확인 화면이 나타나게 됩니다.

이 화면 하단의 Delete 버튼을 클릭하면 폼이 서버로 재전송되고 DeleteConfirmed 액션이 실행됩니다.

// 
// POST: /StoreManager/Delete/5 
[HttpPost, ActionName("Delete")] 
public ActionResult DeleteConfirmed(int id) 
{ 
    Album album = db.Albums.Find(id);  
    db.Albums.Remove(album); 
    db.SaveChanges(); 
    return RedirectToAction("Index"); 
}

이 HTTP-POST Delete 컨트롤러 액션은 다음과 같은 작업들을 처리하게 됩니다:

  1. ID에 해당하는 음반(Album의 인스턴스)을 로드합니다.
  2. 해당 음반을 제거하고 변경사항을 저장합니다.
  3. Index 페이지로 재전송하여 목록에서 해당 음반 정보가 사라진 것을 확인할 수 있게 해줍니다.

그 과정을 테스트를 해보려면 응용 프로그램을 시작한 다음, /StoreManager로 이동합니다. 그리고, 목록에서 특정 음반의 Delete 링크를 클릭합니다.

그러면, 다음과 같은 삭제 확인 화면이 나타납니다.

이 화면 하단의 Delete 버튼을 클릭하면, 해당 음반이 삭제되고, 다시 StoreManager 컨트롤러의 Index 페이지로 반환되어 음반이 삭제된 것을 확인할 수 있습니다.

사용자 정의 HTML 도우미를 사용하여 텍스트 잘라내기

뮤직 스토어 관리자의 Index 페이지에는 한 가지 잠재적인 문제점이 존재합니다. 음반 타이틀이나 아티스트 이름의 속성값이 너무 긴 경우, 표 구조가 깨질 수도 있습니다. 그러므로, 사용자 정의 HTML 도우미를 작성해서 뷰에서 이 속성들이나 다른 속성들의 값을 손쉽게 잘라낼 수 있도록 만들어 보겠습니다.

Razor의 @helper 구문을 이용하면 뷰에서 사용할 자신만의 도우미 함수를 손쉽게 작성할 수 있습니다. 다음 코드를 /Views/StoreManager/Index.cshtml 뷰의 @model 라인 바로 아래에 추가합니다.

@helper Truncate(string input, int length)  
{  
    if (input.Length <= length) {  
        @input  
    } else {  
        @input.Substring(0, length)<text>...</text>  
    }  
}

이 도우미 메서드는 매개변수로 원본 문자열과 허용 가능한 최대 길이를 받습니다. 만약, 전달된 텍스트가 지정된 길이보다 짧다면, 도우미는 원본 텍스트를 그대로 반환합니다. 그러나 만약 더 길다면, 텍스트를 잘라내고 텍스트 뒤에 "..."를 추가하여 렌더합니다.

이제 방금 작성한 Truncate 도우미를 사용해서 음반 타이틀이나 아티스트 이름의 속성값을 25자 이하로 잘라낼 수 있습니다. 이 새로운 Truncate 도우미를 적용한 뷰의 완전한 코드는 다음과 같습니다.

@model IEnumerable<MvcMusicStore.Models.Album>  
@helper Truncate(string input, int length)  
{  
    if (input.Length <= length) {  
        @input  
    } else {  
        @input.Substring(0, length)<text>...</text>  
    }  
}  
@{  
    ViewBag.Title = "Index";  
}

<h2>Index</h2> 
<p>  
    @Html.ActionLink("Create New", "Create")  
</p> 
<table> 
    <tr> 
        <th>  
            Genre  
        </th> 
        <th> 
            Artist  
        </th> 
        <th> 
            Title  
        </th> 
        <th> 
            Price 
        </th> 
        <th></th> 
    </tr> 
@foreach (var item in Model) { 
    <tr> 
        <td>  
            @Html.DisplayFor(modelItem => item.Genre.Name)  
        </td> 
        <td> 
            @Truncate(item.Artist.Name, 25)  
        </td> 
        <td> 
            @Truncate(item.Title, 25)  
        </td> 
        <td> 
            @Html.DisplayFor(modelItem => item.Price)  
        </td> 
        <td> 
            @Html.ActionLink("Edit", "Edit", new { id=item.AlbumId })   
            @Html.ActionLink("Details", "Details", new { id=item.AlbumId })   
            @Html.ActionLink("Delete", "Delete", new { id=item.AlbumId })  
        </td> 
    </tr> 
} 
</table>

다시 /StoreManager/ URL로 이동해보면, 아티스트 이름과 타이틀이 최대 길이 이하로 유지되는 것을 확인할 수 있습니다.

노트: 이 예제는 단일 뷰에서 도우미를 작성하고 사용하는 간단한 사례를 다루고 있습니다. 사이트 전체에서 사용할 수 있는 도우미를 작성하는 방법을 살펴보려면, 다음 블로그 포스트를 참고하시기 바랍니다: http://bit.ly/mvc3-helper-options

질문이나 의견이 있으시면 http://mvcmusicstore.codeplex.com/을 방문해주시기 바랍니다.