login register Sysop! about ME  
qrcode
    최초 작성일 :    2012년 07월 23일
  최종 수정일 :    2012년 07월 23일
  작성자 :    songgun
  편집자 :    songgun (송 원석)
  읽음수 :    26,567

강좌 목록으로 돌아가기

필자의 잡담~

본문은 ASP.net에서 제공되는 자습서 시리즈 중, 두 번째로 MVC Music Store 관련 자습서들을 번역하여 제공해드리는 것입니다. 원문은 총 10편으로 구성되어 있으며, 각각의 분량을 고려하여 한 편씩 제공되거나 두 편이 함께 제공됩니다.

먼저 편역이 마무리 된 ASP.NET MVC 4 자습서 - 만들면서 배우기 시리즈를 읽어보지 않으신 분들은, 우선 해당 자습서 시리즈부터 읽어보시는 것을 권해드립니다.
본문은 ASP.net의 공식 MVC 관련 자습서인 Part 3: Views and ViewModelsPart 4: Models and Data Access를 편역한 글입니다. 마이크로소프트의 공식 번역 문서가 아니며 태오 사이트 MS 컬럼 번역팀에서 번역한 내용입니다. 그렇기에, 일부 오역이나 오타가 존재할 수 있는 점 미리 양해를 드립니다. 원문에 대한 모든 저작권은 마이크로소프트에 있으며, 컬럼 내용과 관련한 질의 문답 역시 원문 사이트에 문의하시는 것을 추천드립니다.

파트 3: 뷰와 뷰 모델

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

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

지금까지 본 자습서에서는 컨트롤러 액션에서 단순 문자열만 반환해왔습니다. 컨트롤러의 동작 방식을 이해하기 위한 용도로는 이 정도만으로도 전혀 부족함이 없지만, 실제 웹 응용 프로그램을 구축할 때 조차도 이런 방식을 사용할 개발자는 없을 것입니다. 결론적으로, 사이트에 방문한 브라우저에 반환할 HTML을 생성하기 위한, 이런 단순한 방법을 대신할 수 있는 더 좋은 방법이 필요한데, 가령 템플릿 파일을 사용하면 반환할 HTML 콘텐트를 보다 손쉽게 처리할 수 있습니다. 그리고, 바로 이와 같은 작업이 뷰(View)가 수행하는 일입니다.

뷰 템플릿 추가하기

뷰 템플릿을 사용하기 위해서는, 먼저 다음 코드처럼 return View() 구문을 호출해서 ActionResult를 반환하도록 HomeController의 Index 메서드를 변경해야 합니다:

public class HomeController : Controller  
{  
    //  
    // GET: /Home/  
    public ActionResult Index()  
    {  
        return View();  
    }  
}

이 변경은 문자열 대신, "뷰(View)"를 사용해서 반환될 결과를 생성하고자 한다는 것을 나타냅니다.

계속해서 프로젝트에 적절한 뷰 템플릿을 추가해보도록 하겠습니다. 먼저, 커서를 Index 액션 메서드 내부에 위치시킨 다음, 마우스 오른쪽 버튼을 클릭하고, "Add View" 메뉴를 선택합니다. 그러면, 다음과 같은 "Add View" 대화 상자가 나타납니다:


출처 : http://i1.asp.net/asp.net/images/mvc/msv30/image026.jpg


출처 : http://i1.asp.net/asp.net/images/mvc/msv30/image027.png

이 "Add View" 대화 상자를 이용하면 쉽고 빠르게 뷰 템플릿 파일을 만들 수 있습니다. 기본적으로 "Add View" 대화 상자는 액션 메서드 이름과, 그에 대응하는 생성할 뷰 템플릿의 이름을 동일하게 지정합니다. 가령, 위의 그림을 보면, 이번 예제에서는 HomeController의 Index() 액션 메서드 내부에서 "Add View" 컨텍스트 메뉴를 선택했으므로, 기본적으로 "Add View" 대화 상자에 "Index"라는 뷰 이름이 지정되어 있는 것을 볼 수 있습니다. 일단 지금으로서는 대화 상자에서 변경할 옵션이 하나도 없으므로, 그냥 "Add" 버튼을 클릭합니다.

그러면, Visual Web Developer가 \Views\Home 디렉터리에 새로운 Index.cshtml 뷰 템플릿을 만들어주고, 만약 해당 폴더가 존재하지 않는다면 폴더까지 함께 만들어줍니다.


출처 : http://i1.asp.net/asp.net/images/mvc/msv30/image028.png

이렇게 ASP.NET MVC의 기본 명명규약에 따라 결정된 "Index.cshtml" 파일의 이름과 폴더 위치는 매우 중요합니다. 가령, 디렉터리의 이름인 \Views\Home는 컨트롤러의 이름인 HomeController가 반영된 결과입니다. 또한, 뷰 템플릿의 이름인 Index는 해당 뷰를 출력할 컨트롤러 액션 메서드의 이름이 반영된 결과입니다.

반환되는 뷰가 이 명명규약을 준수하기만 하면, 뷰의 템플릿 이름이나 경로를 코드에서 명시적으로 지정해주지 않더라도 자동으로 ASP.NET MVC가 알아서 처리해줍니다. 예를 들어서, 다음과 같이 작성된 HomeController의 코드는 \Views\Home\Index.cshtml 뷰 템플릿을 렌더하게 됩니다:

public class HomeController : Controller  
{  
    //  
    // GET: /Home/  
    public ActionResult Index()  
    {  
        return View();  
    }  
}

그리고, Visual Web Developer는 "Index.cshtml" 뷰 템플릿을 생성한 다음, 이 파일을 자동으로 편집기에서 열어주는데, 이 파일의 내용은 다음과 같습니다.

@{   
    ViewBag.Title = "Index";   
}

<h2>Index</h2>

이 코드에서 알 수 있는 것처럼, ASP.NET MVC 3의 뷰는 구버전의 ASP.NET MVC에서 사용하던 ASP.NET 웹폼의 웹폼 뷰 엔진에 비해서 훨씬 더 간결한 Razor 구문을 사용합니다. 물론, ASP.NET MVC 3에서도 웹폼 뷰 엔진을 계속 사용할 수는 있지만, Razor 뷰 엔진이 ASP.NET MVC 개발에 훨씬 잘 어울린다는 것을 많은 개발자들이 인식해나가고 있는 추세입니다.

이 페이지의 처음 세 라인의 코드는 ViewBag.Title을 이용해서 페이지의 타이틀을 설정합니다. 이 코드가 동작하는 방식에 대해서는 잠시 뒤에 직접 살펴보도록 하겠습니다. 일단 지금은 페이지의 제목을 변경해보고, 그 결과에 따라 어떻게 바뀌는지부터 확인해보도록 하겠습니다. 다음 코드와 같이 페이지의 <h2> 태그 내용을 "This is the Home Page"로 변경합니다.

@{   
    ViewBag.Title = "Index";   
}

<h2>This is the Home Page</h2>

그런 다음, 응용 프로그램을 실행시켜보면, 다음 그림과 같이 홈 페이지에 새로운 텍스트가 나타날 것입니다.


출처 : http://i1.asp.net/asp.net/images/mvc/msv30/image029.png

사이트 공통 요소를 위한 레이아웃 사용하기

대부분의 웹사이트에는 네비게이션, 풋터, 로고 이미지, 스타일시트 참조 등, 다수의 페이지에서 공유되는 다양한 요소들이 존재합니다. Razor 뷰 엔진에서는 /Views/Shared 폴더에 자동으로 생성되어 있는 _Layout.cshtml 파일을 통해서 이런 공통 요소들을 손쉽게 관리할 수 있습니다.


출처 : http://i1.asp.net/asp.net/images/mvc/msv30/image030.png

이 파일을 열어서 내용을 확인해보면 다음과 같습니다.

<!DOCTYPE html>  
<html>  
<head>  
    <meta charset="utf-8" />  
    <title>@ViewBag.Title</title>  
    <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />  
    <script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>  
    <script src="@Url.Content("~/Scripts/modernizr-1.7.min.js")" type="text/javascript"></script>  
</head>

<body>   
    @RenderBody()   
</body>  
</html>

각각의 뷰에 작성된 콘텐트들은 @RenderBody() 명령에 의해서 출력되며, 그 외부에 출력하고자 하는 모든 공통 콘텐트들은 _Layout.cshtml 마크업에 작성할 수 있습니다. MVC 뮤직 스토어 사이트에서는 모든 페이지 상단에 Home 페이지 및 Store 영역으로 이동할 수 있는 링크가 포함된 공통 헤더를 출력할 계획이므로, 이 템플릿의 @RenderBody() 구문 상단에 다음과 같은 내용들을 직접 작성합니다.

<!DOCTYPE html>  
<html>  
<head>  
    <title>@ViewBag.Title</title>  
    <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />  
    <script src="@Url.Content("~/Scripts/jquery-1.4.4.min.js")" type="text/javascript"></script>  
</head>    

<body>  
    <div id="header"> 
        <h1>ASP.NET MVC MUSIC STORE</h1>  
        <ul id="navlist">   
            <li class="first"><a href="/" id="current">Home</a></li>  
            <li><a href="/Store/">Store</a></li>  
        </ul>  
    </div>   
    @RenderBody()   
</body>  
</html>

스타일시트 수정하기

빈 프로젝트 템플릿에는 유효성 검사 메시지에 사용되는 스타일들이 정의된, 매우 단순한 CSS 파일만 포함되어 있습니다. 따라서, 디자이너가 멋진 룩앤필을 제공해주는 별도의 CSS와 이미지들을 제공해줬다고 가정하고, 이를 프로젝트에 추가하여 반영해보도록 하겠습니다.

본 자습서에서 사용되는 수정된 CSS 파일과 이미지들은 http://mvcmusicstore.codeplex.com/에서 다운로드 받을 수 있는 MvcMusicStore-Assets.zip 파일의 Content 디렉터리에 들어 있습니다. 다음과 같이, 윈도우 탐색기에서 이 파일과 폴더를 드래그한 다음, Visual Web Developer 솔루션 탐색기의 Content 폴더에 드랍시킵니다:


출처 : http://i1.asp.net/asp.net/images/mvc/msv30/image031.png

그러면, 기존의 Site.css 파일을 덮어 쓸 것인지 확인하는 메세지가 나타납니다. 물론, "Yes"를 클릭합니다.


출처 : http://i1.asp.net/asp.net/images/mvc/msv30/image032.png

그리고 나면, 응용 프로그램의 Content 폴더 모습이 다음과 비숫해질 것입니다:


출처 : http://i1.asp.net/asp.net/images/mvc/msv30/image033.png

이제 응용 프로그램을 실행시킨 다음, 변경 사항이 Home 페이지에 반영된 결과를 살펴봅니다.


출처 : http://i1.asp.net/asp.net/images/mvc/msv30/image034.png

지금까지 수행한 작업들을 다시 검토해보겠습니다:

  • HomeController의 Index 액션 메서드의 코드를, 단지 "return View()"라고만 작성해도 ASP.NET MVC가 자동으로 \Views\Home\Index.cshtml 뷰 템플릿을 찾아서 출력해줍니다. 그 이유는, 이 뷰 템플릿이 기본 명명규약을 따르고 있기 때문입니다.
  • Home 페이지는 \Views\Home\Index.cshtml 뷰 템플릿에 정의된 간단한 환영 메시지를 출력합니다.
  • Home 페이지는 _Layout.cshtml 템플릿을 이용하므로, 이 환영 메시지는 기본 사이트 HTML 레이아웃이 적용되어 출력됩니다.

모델을 이용한 뷰에 정보 전달하기

당연한 얘기겠지만, 하드코딩 된 HTML을 출력하는 뷰 템플릿만 사용해서는 그다지 흥미로운 웹 사이트를 만들어낼 수가 없습니다. 동적 웹 사이트를 만들려면, 컨트롤러 액션에서 뷰 템플릿으로 정보를 전달할 수 있어야만 합니다.

MVC(Model-View-Controller) 패턴에서 모델(Model)이라는 용어는 응용 프로그램의 데이터를 담고 있는 개체를 뜻합니다. 대부분 모델 개체와 데이터베이스의 테이블이 대응되지만, 반드시 그래야만 하는 것은 아닙니다.

지금까지 살펴본 것과 같은, ActionResult를 반환하는 컨트롤러 액션 메서드는 뷰에 모델 개체를 전달할 수 있습니다. 컨트롤러는 응답을 생성하기 위해 필요한 각종 정보들을 깔끔하게 포장한 다음, 이 정보를 뷰 템플릿에 전달해서 적절한 HTML 응답을 생성하기 위해 사용할 수 있습니다. 실제로 동작하는 과정을 살펴보는 것이 가장 이해가 빠르므로, 지금부터 직접 그 과정을 살펴보도록 하겠습니다.

먼저, MVC 뮤직 스토어에 사용될 장르와 앨범에 대한 모델 클래스부터 생성해보겠습니다. 우선, Genre 클래스부터 작성해봅니다. 프로젝트에서 "Models" 폴더를 마우스 오른쪽 버튼으로 클릭한 다음, "Add" > "Class" 옵션을 선택하고, 파일 이름을 "Genre.cs"로 지정합니다.


출처 : http://i1.asp.net/asp.net/images/mvc/msv30/image035.jpg


출처 : http://i1.asp.net/asp.net/images/mvc/msv30/image036.png

그리고, 이 클래스에 public string Name 속성을 추가합니다:

public class Genre  
{  
    public string Name { get; set; }  
}
노트: 이 예제 코드에 사용되는 { get; set; } 표기법은 C#의 자동-구현 속성 기능을 활용하고 있는 것입니다. 이 기능을 이용하면, 내부 필드를 생성하지 않고서도 속성의 여러 가지 이점들을 얻을 수 있습니다.

이번에는, 동일한 과정을 거쳐서 Album.cs 파일에 Title 속성과 Genre 속성을 갖고 있는 Album 클래스를 작성합니다:

public class Album  
{  
    public string Title { get; set; } 
    public Genre Genre { get; set; } 
}

이제 StoreController를 수정해서 모델을 통해서 동적 정보를 출력하는 뷰를 사용하기 위해 필요한 준비가 모두 마무리되었습니다. 일단 지금은 보여주는 것 자체가 목적이므로, 요청 ID로 음반 이름을 붙였다고 가정하고, 다음 뷰와 같은 형태로 정보를 출력해보도록 하겠습니다.


출처 : http://i1.asp.net/asp.net/images/mvc/msv30/image037.png

먼저, 뮤직 스토어의 Details 액션을 수정해서 특정 음반 정보를 보여주도록 해보겠습니다. StoreControllers 클래스 상단에 "using" 구문을 추가해서 MvcMusicStore.Models 네임스페이스를 포함시킵니다. 이 작업을 마치고 나면, 클래스의 "using" 영역의 모습은 다음과 비슷할 것입니다.

using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Web;  
using System.Web.Mvc;  
using MvcMusicStore.Models;

그런 다음, HomeController의 Index 메서드처럼, Details 컨트롤러 액션도 문자열 대신 ActionResult를 반환하도록 수정합니다.

public ActionResult Details(int id)

그러면, 이제 Album 개체를 뷰로 반환하도록 메서드의 로직을 변경할 수 있습니다. 추후에는 데이터베이스에서 데이터를 가져오도록 변경해 보겠지만, 일단 지금은 "가상 데이터"를 사용해서 이 작업을 처리합니다.

public ActionResult Details(int id)  
{  
    var album = new Album { Title = "Album " + id }; 
    return View(album);  
}
노트: 만약, C#에 익숙하지 않다면, var 키워드가 사용된 album 변수가 후기 바인딩(Late-Bound) 기법을 사용하고 있는 것으로 오해할 수도 있습니다. 그러나, 그렇지 않습니다. C# 컴파일러는 변수에 할당되는 값에 기반한 형식 추론(Type-Inference)에 따라 album 변수의 형식이 Album인 것을 인지하고, 지역 album 변수를 Album 형식으로 컴파일합니다. 따라서, 일반적인 코드처럼 컴파일 시점 검사와 Visual Studio 코드 편집기의 지원을 받을 수 있습니다.

계속해서 이번에는 HTML 응답을 만들어내기 위한, Album 클래스를 이용하는 뷰 템플릿을 작성해보도록 하겠습니다. 그러나, 먼저 프로젝트를 빌드해서, "Add View" 대화 상자가 새로 작성된 Album 클래스를 인식할 수 있도록 준비해야 합니다. 메뉴에서 "Debug" > "Build MvcMusicStore" 항목을 선택하면 프로젝트를 빌드할 수 있습니다. (Ctrl-Shift-B 단축키를 눌러도 프로젝트를 빌드할 수 있습니다.)


출처 : http://i1.asp.net/asp.net/images/mvc/msv30/image038.png

이제 클래스들이 준비되었으므로 뷰 템플릿을 만들 수 있습니다. 마우스 오른쪽 버튼으로 Details 메서드를 클릭한 다음, 컨텍스트 메뉴에서 "Add View..."를 선택합니다.


출처 : http://i1.asp.net/asp.net/images/mvc/msv30/image039.png

본 자습서의 이전 파트에서 HomeController를 대상으로 수행했던 것과 동일한 방식으로 새로운 뷰 템플릿을 생성해보도록 하겠습니다. 다만, 이번에는 StoreController의 Details 메서드에 대한 뷰를 생성할 것이므로, 기본적으로 \Views\Store\Details.cshtml 파일이 생성됩니다.

그리고, 이번에는 "Add View" 대화 상자의 "Create a strongly-typed view" 체크박스를 체크해야 합니다. 그런 다음, "Model class" 드롭다운 리스트에서 "Album" 클래스를 선택합니다. 그래야만, Album 개체가 전달될 것을 기대하는 뷰 템플릿이 생성됩니다.


출처 : http://i1.asp.net/asp.net/images/mvc/msv30/image040.png

마지막으로 "Add" 버튼을 클릭하면, 다음과 같은 코드가 포함된 \Views\Store\Details.cshtml 뷰 템플릿이 만들어지게 됩니다.

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

<h2>Details</h2>

이 파일의 첫 번째 라인의 코드에 주목하시기 바랍니다. 바로 이 라인이 이 뷰가 Album 클래스에 대한 강력한 형식의 뷰라는 것을 나타내는 코드입니다. Razor 뷰 엔진은 Album 개체가 전달된다는 것을 인식할 수 있으므로, 모델의 속성에 손쉽게 접근할 수 있으며, Visual Web Developer의 편집기에서 인텔리센스의 이점도 누릴 수 있습니다.

다음과 같이 이 코드의 <h2> 태그를 수정해서 음반의 Title 속성이 출력되도록 만듭니다.

<h2>Album: @Model.Title</h2>

그러면, 아마도 여러분이 @Model 키워드 뒤에 마침표를 입력하는 순간, 인텔리센스가 동작해서 Album 클래스가 지원하는 속성과 메서드들의 목록이 나타나는 것을 확인할 수 있을 것입니다.

다시 프로젝트를 실행시킨 다음, /Store/Details/5 URL로 이동해보시기 바랍니다. 그러면, 다음 그림과 같이 음반의 Details 페이지를 확인하실 수 있을 것입니다.


출처 : http://i1.asp.net/asp.net/images/mvc/msv30/image037.png

그러면 계속해서, StoreController 클래스의 Browse 액션 메서드도 비슷한 방식으로 수정해보겠습니다. 먼저, 메서드를 변경해서 ActionResult를 반환하도록 만든 다음, 메서드 로직을 변경하여 새로운 Genre 개체를 만들어서 뷰로 이 개체를 반환하게 만듭니다.

public ActionResult Browse(string genre)  
{  
    var genreModel = new Genre { Name = genre }; 
    return View(genreModel);  
}

그리고, 마우스 오른쪽 버튼으로 Browse 메서드를 클릭한 다음, 컨텍스트 메뉴에서 "Add View..."를 선택하고, 이전과 동일한 방식으로 Genre 클래스에 강력하게 형식화 된 뷰를 추가합니다.


출처 : http://i1.asp.net/asp.net/images/mvc/msv30/image042.png

그런 다음, 뷰 코드(/Views/Store/Browse.cshtml)의 <h2> 요소를 수정해서 장르 정보를 출력합니다.

@model MvcMusicStore.Models.Genre
@{
    ViewBag.Title = "Browse";   
}

<h2>Browsing Genre: @Model.Name</h2>

이제 프로젝트를 다시 실행시키고, /Store/Browse?Genre=Disco URL로 이동해봅니다. 그러면, 다음 그림과 비슷한 Browse 페이지를 확인할 수 있을 것입니다.


출처 : http://i1.asp.net/asp.net/images/mvc/msv30/image043.png

이제 마지막으로, Index 액션 메서드를 변경해서 뮤직 스토어의 모든 장르들을 출력할 수 있도록, 조금 더 복잡한 변경 작업을 수행해보겠습니다. 즉, 이번에는 단일 Genre 개체가 아니라 Genre 개체들이 담긴 List 개체를 모델 개체로 사용하고자 하는 것입니다.

public ActionResult Index()  
{  
    var genres = new List<Genre>  
    {  
        new Genre { Name = "Disco"},  
        new Genre { Name = "Jazz"},  
        new Genre { Name = "Rock"}  
    };  
    return View(genres);  
}

지금까지와 마찮가지로, Index 액션 메서드 내부를 마우스 오른쪽 버튼으로 클릭하고, 컨텍스트 메뉴에서 "Add View..."를 선택한 다음, 모델 클래스로 Genre를 선택하고 "Add" 버튼을 누릅니다.


출처 : http://i1.asp.net/asp.net/images/mvc/msv30/image044.png

그런 다음, 생성된 뷰 템플릿 파일을 열고 @model 선언이 작성된 첫 번째 라인의 코드를 다음과 같이 변경하여, 뷰가 단일 Genre 개체가 아닌 Genre 개체들의 목록이 전달되기를 기대하고 있다는 것을 나타냅니다. 이 작업을 마치고 나면 /Store/Index.cshtml 파일의 첫 번째 라인은 다음과 같은 모습일 것입니다:

@model IEnumerable<MvcMusicStore.Models.Genre>

이 코드는 이 뷰가 여러 개의 Genre 개체들을 담고 있는 모델 개체를 사용한다는 것을 Razor 뷰 엔진에게 전달해줍니다. 참고로, List<Genre> 개체 대신 IEnumerable<Genre> 개체를 사용하는 이유는, IEnumerable<Genre> 개체가 보다 범용적이므로, IEnumerable 인터페이스를 지원하는 모든 개체 형식들을 모델 형식으로 사용할 수 있기 때문입니다.

그런 다음, 다음과 같은 코드를 사용해서 모델에 담겨 있는 Genre 개체들을 대상으로 루프문을 수행합니다.

@model IEnumerable<MvcMusicStore.Models.Genre>
@{   
    ViewBag.Title = "Store";   
}

<h3>Browse Genres</h3>  

<p>   
    Select from @Model.Count() genres:</p>  
<ul>   
    @foreach (var genre in Model)   
    {   
        <li>@genre.Name</li> 
    } 
</ul>

그리고, 이 코드를 작성할 때도, 완벽하게 인텔리센스가 지원되어 "@Model."이라고 코드를 입력하면 Genre 개체들이 담겨 있는 IEnumerable 인터페이스가 지원해주는 메서드들과 속성들을 보면서 작업할 수 있습니다.


출처 : http://i1.asp.net/asp.net/images/mvc/msv30/image045.png

그뿐만 아니라, Visual Web Developer가 IEnumerable 인터페이스 목록의 각 항목들이 Genre 형식이라는 점까지 인식하고 있기 때문에, foreach 루프문 내부에서도 Genre 형식에 대한 인텔리센스 지원을 받을 수 있습니다.


출처 : http://i1.asp.net/asp.net/images/mvc/msv30/image046.png

그리고 참고로, Visual Web Developer가 제공해주는 스캐폴딩 기능을 이용하면, 해당 기능이 자체적으로 Genre 클래스를 분석한 다음, Name 속성의 존재를 파악해서, 루프문을 이용한 해당 속성의 출력 코드를 자동으로 만들어낼 수도 있습니다. 그뿐만 아니라, 각각의 항목들에 대한 Edit, Details, Delete 링크도 자동으로 생성할 수 있습니다. 본 자습서의 뒷 부분에서 MVC 뮤직 스토어의 관리자 기능을 구현할 때는 이 기능을 활용해 볼 예정입니다. 그러나, 일단 지금은 간단한 목록만 구현해보도록 하겠습니다.

다시 응용 프로그램을 실행시키고, /Store로 이동해보면, 장르들의 목록과 그 갯수가 출력되는 모습을 확인하실 수 있습니다.


출처 : http://i1.asp.net/asp.net/images/mvc/msv30/image047.png

페이지들 간의 링크 추가하기

지금 /Store URL에서 출력되는 장르 목록의 이름들은 그저 단순한 텍스트일뿐입니다. 이 점을 보완해서 장르 이름들을 단순 텍스트가 아닌 링크로 렌더하고, 이 링크들을 클릭하면 적절한 /Store/Browse URL로 이동하도록 변경해보겠습니다. 즉, "Disco" 같은 음반 장르를 클릭하면, /Store/Browse?genre=Disco URL로 이동하도록 만들어 볼 것입니다. 물론, 다음과 같은 코드를 사용해서 \Views\Store\Index.cshtml 뷰 템플릿을 변경해도 원하는 동작을 수행하는 링크를 만들어 낼 수는 있습니다 (이 코드를 실제로 작성하지는 마십시요. 잠시 후 바로 개선할 것입니다):

<ul>   
    @foreach (var genre in Model)   
    {   
        <li><a href="/Store/Browse?genre=@genre.Name">@genre.Name</a></li>   
    }   
</ul>

이런 방식도 동작이야 하겠지만, 이처럼 하드코딩 된 문자열에 의존하면 나중에라도 문제가 발생할 수 있습니다. 가령, 컨트롤러의 이름을 변경하기라도 하면, 모든 코드를 뒤져서 변경이 필요한 부분들을 찾아서 일일이 수정해줘야만 합니다.

권장되는 접근방식은 HTML 도우미 메서드의 이점을 활용하는 것입니다. ASP.NET MVC에는 뷰 템플릿 코드 내부에서 필요한, 이 예제와 유사한 여러 가지 일반적인 작업들을 처리해주는 HTML 도우미 메서드가 포함되어 있습니다. 그 중에서도 Html.ActionLink() 도우미 메서드는 특히 유용한데, HTML <a> 링크의 작성을 손쉽게 만들어 주고 URL 경로가 적절하게 인코드되었는지 확인하는 등 번거로운 세부 사항들을 대신 처리해줍니다.

Html.ActionLink() 도우미 메서드는 링크에 필요한 다양한 정보들을 지정할 수 있도록 여러 가지 다른 오버로드를 제공해줍니다. 가장 간단한 버전은 링크 텍스트와 클라이언트가 하이퍼링크를 클릭했을 때 이동할 액션 메서드만 지정해주면 됩니다. 가령, Store 영역의 Details 페이지에, 링크 텍스트가 "Go to the Store Index"고, 링크를 클릭했을 때 "/Store/" Index() 메서드로 이동하는 링크를 생성하려면 다음과 같이 호출하면 됩니다:

@Html.ActionLink("Go to the Store Index", "Index")
노트: 이 경우, 컨트롤러 이름을 지정할 필요가 없는데, 그 이유는 현재 뷰를 렌더하는 컨트롤러와 동일한 컨트롤러의 다른 액션 메서드로 링크되기 때문입니다.

그러나, 지금 작성하고자 하는 Browse 페이지에 대한 링크에는 전달될 매개변수가 필요합니다. 따라서, 다음과 같은 세 가지 매개변수를 전달 받는 Html.ActionLink 메서드의 또 다른 오버로드를 사용해야 합니다:

  1. 링크 텍스트, 즉 장르의 이름
  2. 컨트롤러 액션 이름, Browse
  3. 라우트 매개변수 값, 이름(Genre) 및 그 값(Genre 이름)

다음 코드는 이 매개변수들을 모두 지정하여 Index 뷰에 Browse 페이지에 대한 링크를 추가하는 코드입니다:

<ul>   
    @foreach (var genre in Model)   
    {   
        <li>@Html.ActionLink(genre.Name, "Browse", new { genre = genre.Name })</li>   
    }   
</ul>

이제 MVC 뮤직 스토어 응용 프로그램을 다시 실행시킨 다음, /Store/ URL로 이동해보면 장르들의 목록을 볼 수 있습니다. 그리고, 각각의 장르 이름은 하이퍼링크이므로, 이 링크들을 클릭하면 /Store/Browse?genre=[genre] URL로 이동하게 됩니다.


출처 : http://i1.asp.net/asp.net/images/mvc/msv30/image048.jpg

장르 목록의 HTML을 살펴보면 다음과 비슷한 형태일 것입니다:

<ul>  
    <li><a href="/Store/Browse?genre=Disco">Disco</a></li>  
    <li><a href="/Store/Browse?genre=Jazz">Jazz</a></li>  
    <li><a href="/Store/Browse?genre=Rock">Rock</a></li>  
</ul>

파트 4: 모델 및 데이터 접근

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

본 자습서 시리즈에서는 ASP.NET MVC 뮤직 스토어 응용 프로그램을 구축하기 위해서 필요한 모든 단계들을 자세하게 살펴볼 것입니다. 이번 파트 4에서는 모델 및 데이터 접근에 관해서 살펴봅니다.

지금까지는 컨트롤러에서 뷰 템플릿으로 "가상 데이터"만 전달했었습니다. 이제는 실제 데이터를 연결시킬 준비가 되었습니다. 본 자습서에서는 데이터베이스 엔진으로 Server Compact Edition을 (SQL CE라고도 합니다) 사용하는 방법을 살펴볼 것입니다. SQL CE는 내장된 파일 기반의 무료 데이터베이스로, 설치나 구성이 필요 없으며, 로컬 개발에 매우 편리합니다.

Entity Framework Code-First를 이용한 데이터 접근

본 자습서에서는 데이터베이스를 질의하거나 수정하기 위한 방식으로 ASP.NET MVC 3 프로젝트에 내장되어 있는 Entity Framework (EF) 지원을 사용해보려고 합니다. Entity Framework는 유연한 객체 관계 매핑(ORM, Object Relational Mapping) 데이터 API로, 개체 지향적인 방식으로 데이터베이스에 저장되어 있는 데이터들을 질의하거나 수정할 수 있습니다.

Entity Framework 버전 4에서는 Code-First라는 이름의 개발 패러다임이 지원됩니다. Code-First를 사용하면 간단한 클래스("plain-Old CLR Objects"의 약자인 POCO로 알려진)로 모델 개체를 생성할 수 있으며, 이 클래스들로부터 실시간으로 데이터베이스를 생성할 수도 있습니다.

모델 클래스 변경하기

본 자습서에서는 Entity Framework의 데이터베이스 생성 기능을 이용해보려고 합니다. 그러나, 그 전에 먼저 모델 클래스를 약간 수정해서 추후에 이용하게 될 부분들을 추가해줘야 합니다.

Artist 모델 클래스 추가하기

이전 파트에서 작성했던 Albums 모델은 앞으로 아티스트 정보와 연결될 것이므로, 아티스트를 표현할 간단한 모델 클래스를 추가해야 합니다. 따라서, Models 폴더에 Artist.cs라는 이름의 새로운 클래스를 추가하고, 다음과 같은 코드를 작성합니다.

namespace MvcMusicStore.Models  
{  
    public class Artist  
    {  
        public int ArtistId { get; set; }  
        public string Name { get; set; }  
    }  
}

기존 모델 클래스 수정하기

먼저, Album 클래스를 다음과 같이 수정합니다.

namespace MvcMusicStore.Models   
{   
    public class Album   
    {   
        public int AlbumId { get; set; }   
        public int GenreId { get; set; }   
        public int ArtistId { get; set; }   
        public string Title { get; set; }   
        public decimal Price { get; set; }   
        public string AlbumArtUrl { get; set; }   
        public Genre Genre { get; set; }   
        public Artist Artist { get; set; }   
    }   
}

그런 다음, Genre 클래스를 다음과 같이 수정합니다.

using System.Collections.Generic;  

namespace MvcMusicStore.Models  
{  
    public partial class Genre  
    {  
        public int GenreId { get; set; }  
        public string Name { get; set; }  
        public string Description { get; set; }  
        public List<Album> Albums { get; set; }  
    }  
}

App_Data 폴더 추가하기

그러면 이번에는 프로젝트에 SQL Server Express 데이터베이스 파일을 담기 위한 App_Data 폴더를 추가해보도록 하겠습니다. App_Data 폴더는 ASP.NET의 특수 디렉터리로, 데이터베이스에 접근하기 위해서 필요한 적절한 보안 접근 권한을 기본적으로 갖고 있습니다. "Project" 메뉴에서 "Add ASP.NET Folder" > "App_Data"를 선택합니다.


출처 : http://i1.asp.net/asp.net/images/mvc/msv30/image049.png

web.config 파일에 연결 문자열 생성하기

Entity Framework가 데이터베이스에 연결할 수 있는 방법을 인식할 수 있도록 웹사이트의 구성 파일에 설정을 몇 라인 추가해보겠습니다. 프로젝트의 루트에 위치한 Web.config 파일을 마우스로 더블 클릭해서 엽니다.


출처 : http://i1.asp.net/asp.net/images/mvc/msv30/image050.png

파일 하단으로 스크롤해서 내린 다음, 가장 마지막 라인 바로 위에 다음과 같은 <connectionStrings> 섹션을 추가합니다.

  <connectionStrings>  
    <add name="MusicStoreEntities"  
         connectionString="Data Source=DataDirectoryMvcMusicStore.sdf"  
         providerName="System.Data.SqlServerCe.4.0"/>  
  </connectionStrings>  
</configuration>

컨텍스트 클래스 추가하기

마우스 오른쪽 버튼으로 Models 폴더를 클릭해서 MusicStoreEntities.cs라는 새로운 클래스를 추가합니다.


출처 : http://i1.asp.net/asp.net/images/mvc/msv30/image051.png

이 클래스는 Entity Framework 데이터베이스 컨텍스트를 나타내며, 생성, 조회, 수정, 삭제 작업을 처리해줍니다. 이 클래스의 코드는 다음과 같습니다.

using System.Data.Entity;  

namespace MvcMusicStore.Models  
{  
    public class MusicStoreEntities : DbContext  
    {  
        public DbSet<Album> Albums { get; set; }  
        public DbSet<Genre> Genres { get; set; }  
    }  
}

이것이 필요한 작업의 전부입니다. 별도의 구성이나 특별한 인터페이스 등을 구현한 필요도 전혀 없습니다. 그저, DbContext 기본 클래스를 확장하기만 하면, MusicStoreEntities 클래스가 데이터베이스 작업을 대신 처리해줍니다. 이제 데이터베이스를 연결했으므로, 모델 클래스에 몇 가지 속성을 추가해서 데이터베이스로부터 일부 추가적인 정보들의 이점을 얻도록 하겠습니다.

뮤직 스토어 카탈로그 데이터 추가하기

본 자습서에서는 새롭게 생성되는 데이터베이스에 "초기(Seed)" 데이터를 추가해주는 Entity Framework의 기능을 활용해보고자 합니다. 이 기능을 활용해서 뮤직 스토어 카탈로그에 장르들의 목록, 아티스트, 음반 정보들을 미리 채워넣게 될 것입니다. 본 자습서의 이전 파트에서 사이트의 디자인 관련 파일들을 가져오기 위해서 다운로드 받았던 MvcMusicStore-Assets.zip 파일의 Code 폴더 하위에 이 초기 데이터를 추가해주는 클래스가 포함되어 있습니다.

이 파일에 포함된 Code/Models 폴더 하위에 위치한 SampleData.cs 파일을 다음 그림과 같이 드래그해서 MVC 뮤직 스토어 프로젝트의 Models 폴더에 드랍시킵니다.


출처 : http://i1.asp.net/asp.net/images/mvc/msv30/image052.png

그런 다음, Entity Framework가 이 SampleData 클래스를 인식할 수 있도록 코드를 한 라인 추가해줘야 합니다. 프로젝트의 루트에서 Global.asax 파일을 더블 클릭해서 연 다음, Application_Start 메서드의 상단에 다음과 같은 코드를 추가합니다.

protected void Application_Start()  
{  
    System.Data.Entity.Database.SetInitializer(new MvcMusicStore.Models.SampleData());  

    AreaRegistration.RegisterAllAreas();

    RegisterGlobalFilters(GlobalFilters.Filters);  
    RegisterRoutes(RouteTable.Routes);  
}

이제, 프로젝트에서 Entity Framework를 구성하기 위해 필요한 모든 작업들이 마무리 되었습니다.

데이터베이스 질의하기

이제, "가상 데이터"를 사용하는 대신, 모든 정보들을 데이터베이스 호출을 통해서 질의하도록 StoreController 클래스를 변경해보겠습니다. 먼저, 다음 코드와 같이 StoreController 클래스에 MusicStoreEntities 클래스의 인스턴스를 담을 storeDB라는 이름의 필드를 선언합니다:

public class StoreController : Controller  
{  
    MusicStoreEntities storeDB = new MusicStoreEntities();

데이터베이스에 질의하도록 Store 컨트롤러의 Index 메서드 변경하기

이 MusicStoreEntities 클래스는 Entity Framework에 의해서 관리되며, 데이터베이스의 테이블들에 대한 컬렉션 속성들을 노출해줍니다. 데이터베이스에서 모든 장르들의 목록을 가져오도록 StoreController 클래스의 Index 메서드를 수정해보겠습니다. 기존에는 하드코딩 된 문자열 데이터를 이용해서 이 작업을 처리했지만, 지금부터는 Entity Framework 컨텍스트의 Generes 컬렉션을 대신 사용할 수 있습니다:

public ActionResult Index()  
{  
    var genres = storeDB.Genres.ToList();  
    return View(genres);  
}

뷰 템플릿은 변경할 필요가 전혀 없는데, 지금까지 반환해왔던 동일한 뷰 모델을 앞으로도 계속 반환할 것이기 때문입니다. 그저 데이터베이스에서 가져온 실제 데이터를 반환하기만 하면 됩니다.

프로젝트를 다시 실행시킨 다음, "/Store" URL로 이동해보면, 이제 데이터베이스에서 가져온 장르들의 목록이 나타나는 것을 볼 수 있을 것입니다:


출처 : http://i1.asp.net/asp.net/images/mvc/msv30/image053.jpg

실제 데이터를 사용하도록 Store 컨트롤러의 Browse 메서드와 Details 메서드 변경하기

MVC 뮤직 스토어에서는 이름을 기준으로 특정 장르를 검색하기 위해서 /Store/Browse?genre=[특정 장르] 액션 메서드를 이용하고 있습니다. 동일한 장르 이름을 가진 항목이 여러 개 존재할 수는 없으므로, 단 하나의 장르만 반환되어야 하는데, 다음 코드와 같이 LINQ의 .Single() 메서드를 사용하면 적절한 Genre 개체를 조회할 수 있습니다. (아직 이 코드를 작성하지는 마십시요.):

var example = storeDB.Genres.Single(g => g.Name == Disco”);

이 예제 코드에 사용된 Single 메서드는 람다식을 매개변수로 받아서, 지정된 값과 일치하는 이름을 갖고 있는 단일 Genre 개체를 반환합니다. 결국, 이 코드는 Name 속성값이 Disco인 단일 Genre 개체를 로딩하게 됩니다.

그리고, Genre 개체를 가져올 때, 함께 로드할 다른 관련 엔티티들을 지정할 수 있는 Entity Framework의 기능을 활용해보겠습니다. 이 기능은 Query Result Shaping이라고 부르며, 필요한 모든 정보들을 가져오기 위해서 데이터베이스에 접근해야 될 횟수를 줄여줍니다. 가령, 이 예제에서는 조회한 장르에 대한 음반 정보들까지 미리 가져오고자 하므로, Genres.Include("Albums")가 포함되도록 질의를 변경해서 관련된 음반 정보들까지 가져오도록 만듭니다. 장르와 음반의 데이터를 단일 데이터베이스 요청만으로 모두 가져오므로, 이 방법이 훨씬 효과적입니다.

장황한 설명은 일단 생략하도록 하겠습니다. 다음은 수정된 Browse 컨트롤러 액션의 모습입니다:

public ActionResult Browse(string genre)  
{  
    // Retrieve Genre and its Associated Albums from database  
    var genreModel = storeDB.Genres.Include("Albums")  
                            .Single(g => g.Name == genre);  
    
    return View(genreModel);  
}

이제 각 장르에 포함된 음반들을 출력하도록 Store 영역의 Browse 뷰를 변경할 수 있습니다. 뷰 템플릿을 연 다음 (/Views/Store/Browse.cshtml), 다음과 같이 음반들의 목록을 출력하기 위한 코드를 추가합니다.

@model MvcMusicStore.Models.Genre
@{   
    ViewBag.Title = "Browse";   
}  

<h2>Browsing Genre: @Model.Name</h2>  

<ul>   
    @foreach (var album in Model.Albums)   
    {   
        <li>   
            @album.Title   
        </li>   
    }   
</ul>

다시 응용 프로그램을 시작시킨 다음, /Store/Browse?genre=Jazz로 이동해보면, 데이터베이스로부터 결과가 조회되어, 선택된 장르에 해당하는 모든 앨범들이 나타날 것입니다.


출처 : http://i1.asp.net/asp.net/images/mvc/msv30/image054.jpg

지금까지 처리한 작업과 동일한 작업을 /Store/Details/[id] URL을 대상으로도 수행해서, 가상 데이터를 매개변수 값과 일치하는 음반 정보를 로드하는 데이터베이스 질의로 대체합니다.

public ActionResult Details(int id)  
{  
    var album = storeDB.Albums.Find(id);  
    
    return View(album);  
}

그리고, 응용 프로그램을 실행시킨 다음, /Store/Details/1로 이동해보면 데이터베이스로부터 조회해 온 결과가 나타날 것입니다.


출처 : http://i1.asp.net/asp.net/images/mvc/msv30/image054.png

이제, 음반의 ID를 기준으로 음반 정보를 출력할 수 있도록 Store 영역의 Details 페이지의 준비가 마무리되었으므로, Browse 뷰를 수정해서 Details 뷰에 링크를 걸 수 있게 만듭니다. 이전 파트의 마지막 부분에서, Index 페이지에서 Browse 페이지로 링크를 걸 때 사용했던 바로 그 Html.ActionLink 메서드를 다시 사용합니다. 변경이 마무리된 Browse 뷰의 완전한 소스는 다음과 같습니다.

@model MvcMusicStore.Models.Genre
@{   
    ViewBag.Title = "Browse";   
} 

<h2>Browsing Genre: @Model.Name</h2>  

<ul>   
    @foreach (var album in Model.Albums)   
    {   
        <li>   
            @Html.ActionLink(album.Title, "Details", new { id = album.AlbumId })   
        </li>   
    }   
</ul>

이제 MVC 뮤직 스토어의 메인 페이지에서, 특정 장르의 음반 목록을 제공해주는 장르 페이지로 이동할 수 있고, 다시 이 페이지에서 음반 이름을 클릭하여 특정 음반의 상세 정보를 살펴볼 수 있는 상세 페이지로 이동할 수 있습니다.


출처 : http://i1.asp.net/asp.net/images/mvc/msv30/image056.png

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

다음 강좌에서 계속 이어집니다...


authored by

  lsy5718
  2012-07-24(17:58)
캐릭 이미지
매번 도움만 받네요 ㅋ 감사합니다~

 
 
.NET과 Java 동영상 기반의 교육사이트

로딩 중입니다...

서버 프레임워크 지원 : NeoDEEX
based on ASP.NET 3.5
Creative Commons License
{5}
{2} 읽음   :{3} ({4})