다음을 통해 공유


ViewData 사용 및 ViewModel 클래스 구현

작성자: Microsoft

PDF 다운로드

ASP.NET MVC 1을 사용하여 작지만 완전한 웹 애플리케이션을 빌드하는 방법을 안내하는 무료 "NerdDinner" 애플리케이션 자습서 의 6단계입니다.

6단계에서는 보다 풍부한 양식 편집 시나리오를 지원하는 방법을 보여 줍니다. 또한 컨트롤러에서 뷰로 데이터를 전달하는 데 사용할 수 있는 두 가지 방법인 ViewData 및 ViewModel에 대해서도 설명합니다.

ASP.NET MVC 3을 사용하는 경우 MVC 3 또는 MVC Music Store 시작 따라가는 것이 좋습니다.

NerdDinner 6단계: ViewData 및 ViewModel

다양한 양식 게시 시나리오를 다루며 CRUD(만들기, 업데이트 및 삭제) 지원을 구현하는 방법에 대해 설명했습니다. 이제 DinnersController 구현을 더 자세히 살펴보고 더 풍부한 양식 편집 시나리오를 지원할 예정입니다. 이 작업을 수행하는 동안 컨트롤러에서 뷰로 데이터를 전달하는 데 사용할 수 있는 두 가지 방법인 ViewData 및 ViewModel에 대해 설명합니다.

컨트롤러에서 View-Templates 데이터 전달

MVC 패턴의 정의 특성 중 하나는 애플리케이션의 여러 구성 요소 간에 적용하는 데 도움이 되는 엄격한 "문제 분리"입니다. 모델, 컨트롤러 및 뷰는 각각 잘 정의된 역할과 책임을 가지고 있으며 잘 정의된 방식으로 서로 통신합니다. 이렇게 하면 테스트 가능성 및 코드 재사용을 촉진할 수 있습니다.

Controller 클래스가 HTML 응답을 클라이언트에 다시 렌더링하기로 결정하면 응답을 렌더링하는 데 필요한 모든 데이터를 뷰 템플릿에 명시적으로 전달해야 합니다. 보기 템플릿은 데이터 검색 또는 애플리케이션 논리를 수행해서는 안 되며, 대신 컨트롤러가 전달한 모델/데이터에서 구동되는 렌더링 코드만 갖도록 제한해야 합니다.

현재 DinnersController 클래스에서 뷰 템플릿에 전달하는 모델 데이터는 간단하고 직선입니다. Index()의 경우 Dinner 개체 목록과 Details(), Edit(), Create() 및 Delete()의 경우 단일 Dinner 개체입니다. 애플리케이션에 더 많은 UI 기능을 추가할 때 보기 템플릿 내에서 HTML 응답을 렌더링하기 위해 이 데이터 이상을 전달해야 하는 경우가 많습니다. 예를 들어 편집 및 만들기 보기 내의 "국가" 필드를 HTML 텍스트 상자에서 드롭다운 목록으로 변경할 수 있습니다. 보기 템플릿에서 국가 및 지역 이름의 드롭다운 목록을 하드 코딩하는 대신 동적으로 채우는 지원되는 국가 및 지역 목록에서 생성할 수 있습니다. Dinner 개체와 지원되는 국가 지역 목록을 컨트롤러에서 뷰 템플릿으로 전달하는 방법이 필요합니다.

이 작업을 수행할 수 있는 두 가지 방법을 살펴보겠습니다.

ViewData 사전 사용

Controller 기본 클래스는 컨트롤러에서 뷰로 추가 데이터 항목을 전달하는 데 사용할 수 있는 "ViewData" 사전 속성을 노출합니다.

예를 들어 편집 보기 내의 "국가" 텍스트 상자를 HTML 텍스트 상자에서 드롭다운 목록으로 변경하려는 시나리오를 지원하기 위해 Edit() 작업 메서드를 업데이트하여 "국가" 드롭다운 목록의 모델로 사용할 수 있는 SelectList 개체(Dinner 개체 외에도)를 전달할 수 있습니다.

//
// GET: /Dinners/Edit/5

[Authorize]
public ActionResult Edit(int id) {

    Dinner dinner = dinnerRepository.GetDinner(id);

    ViewData["Countries"] = new SelectList(PhoneValidator.AllCountries, dinner.Country);

    return View(dinner);
}

위의 SelectList 생성자는 현재 선택한 값뿐만 아니라 드롭다운 목록을 채울 국가 및 지역 목록을 허용합니다.

그런 다음, 이전에 사용한 Html.TextBox() 도우미 메서드 대신 Html.DropDownList() 도우미 메서드를 사용하도록 Edit.aspx 뷰 템플릿을 업데이트할 수 있습니다.

<%= Html.DropDownList("Country", ViewData["Countries"] as SelectList) %>

위의 Html.DropDownList() 도우미 메서드는 두 개의 매개 변수를 사용합니다. 첫 번째는 출력할 HTML 양식 요소의 이름입니다. 두 번째는 ViewData 사전을 통해 전달한 "SelectList" 모델입니다. C# "as" 키워드(keyword) 사용하여 사전 내에서 형식을 SelectList로 캐스팅합니다.

이제 애플리케이션을 실행하고 브라우저 내에서 /Dinners/Edit/1 URL에 액세스할 때 텍스트 상자 대신 국가 및 지역의 드롭다운 목록을 표시하도록 편집 UI가 업데이트된 것을 볼 수 있습니다.

빨간색 화살표로 강조 표시된 국가 및 지역의 드롭다운 목록이 있는 사용자 인터페이스 편집 스크린샷

또한 HTTP-POST 편집 메서드에서 보기 편집 템플릿을 렌더링하기 때문에(오류가 발생하는 시나리오의 경우) 오류 시나리오에서 뷰 템플릿이 렌더링될 때 SelectList를 ViewData에 추가하도록 이 메서드도 업데이트해야 합니다.

//
// POST: /Dinners/Edit/5

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection collection) {

    Dinner dinner = dinnerRepository.GetDinner(id);

    try {
    
        UpdateModel(dinner);

        dinnerRepository.Save();

        return RedirectToAction("Details", new { id=dinner.DinnerID });
    }
    catch {
    
        ModelState.AddModelErrors(dinner.GetRuleViolations());

        ViewData["countries"] = new SelectList(PhoneValidator.AllCountries, dinner.Country);

        return View(dinner);
    }
}

이제 DinnersController 편집 시나리오에서 DropDownList를 지원합니다.

ViewModel 패턴 사용

ViewData 사전 접근 방식은 매우 빠르고 쉽게 구현할 수 있다는 이점이 있습니다. 그러나 일부 개발자는 오타가 컴파일 타임에 잡히지 않는 오류로 이어질 수 있으므로 문자열 기반 사전을 사용하는 것을 좋아하지 않습니다. 형식화되지 않은 ViewData 사전에서는 뷰 템플릿에서 C#와 같은 강력한 형식의 언어를 사용할 때 "as" 연산자를 사용하거나 캐스팅해야 합니다.

사용할 수 있는 다른 방법은 "ViewModel" 패턴이라고도 합니다. 이 패턴을 사용하는 경우 특정 보기 시나리오에 최적화되고 보기 템플릿에 필요한 동적 값/콘텐츠에 대한 속성을 노출하는 강력한 형식의 클래스를 만듭니다. 그런 다음 컨트롤러 클래스를 채우고 이러한 보기 최적화 클래스를 사용할 보기 템플릿에 전달할 수 있습니다. 이렇게 하면 보기 템플릿 내에서 형식 안전성, 컴파일 시간 검사 및 편집기 intellisense를 사용할 수 있습니다.

예를 들어 저녁 식사 양식 편집 시나리오를 사용하도록 설정하려면 두 개의 강력한 형식의 속성인 Dinner 개체와 "국가" 드롭다운 목록을 채우는 데 필요한 SelectList 모델을 노출하는 아래와 같은 "DinnerFormViewModel" 클래스를 만들 수 있습니다.

public class DinnerFormViewModel {

    // Properties
    public Dinner     Dinner    { get; private set; }
    public SelectList Countries { get; private set; }

    // Constructor
    public DinnerFormViewModel(Dinner dinner) {
        Dinner = dinner;
        Countries = new SelectList(PhoneValidator.AllCountries, dinner.Country);
    }
}

그런 다음, 편집() 작업 메서드를 업데이트하여 리포지토리에서 검색한 Dinner 개체를 사용하여 DinnerFormViewModel을 만든 다음 보기 템플릿에 전달할 수 있습니다.

//
// GET: /Dinners/Edit/5

[Authorize]
public ActionResult Edit(int id) {

    Dinner dinner = dinnerRepository.GetDinner(id);
    
    return View(new DinnerFormViewModel(dinner));
}

그런 다음 다음과 같이 edit.aspx 페이지의 맨 위에 있는 "상속" 특성을 변경하여 "Dinner" 개체 대신 "DinnerFormViewModel"이 예상되도록 보기 템플릿을 업데이트합니다.

Inherits="System.Web.Mvc.ViewPage<NerdDinner.Controllers.DinnerFormViewModel>

이렇게 하면 뷰 템플릿 내의 "Model" 속성의 intellisense가 전달되는 DinnerFormViewModel 형식의 개체 모델을 반영하도록 업데이트됩니다.

드롭다운 목록과 저녁 식사 목록 항목이 파란색 사각형으로 강조 표시된 코드 편집기 창의 스크린샷

드롭다운 목록과 회색 점선 사각형이 강조 표시된 주소 목록 항목이 있는 코드 편집기 창의 스크린샷

그런 다음 보기 코드를 업데이트하여 작동할 수 있습니다. 아래에서는 만드는 입력 요소의 이름을 변경하지 않고(양식 요소는 여전히 "Title", "Country"로 지정됨) DinnerFormViewModel 클래스를 사용하여 값을 검색하도록 HTML 도우미 메서드를 업데이트하고 있습니다.

<p>
    <label for="Title">Dinner Title:</label>
    <%= Html.TextBox("Title", Model.Dinner.Title) %>
    <%=Html.ValidationMessage("Title", "*") %>
</p>

<p>
    <label for="Country">Country:</label>
    <%= Html.DropDownList("Country", Model.Countries) %>                
    <%=Html.ValidationMessage("Country", "*") %>
</p>

또한 오류를 렌더링할 때 DinnerFormViewModel 클래스를 사용하도록 Edit post 메서드를 업데이트합니다.

//
// POST: /Dinners/Edit/5

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection collection) {

    Dinner dinner = dinnerRepository.GetDinner(id);

    try {
        UpdateModel(dinner);

        dinnerRepository.Save();

        return RedirectToAction("Details", new { id=dinner.DinnerID });
    }
    catch {
        ModelState.AddModelErrors(dinner.GetRuleViolations());

        return View(new DinnerFormViewModel(dinner));
    }
}

또한 Create() 작업 메서드를 업데이트하여 정확히 동일한 DinnerFormViewModel 클래스를 다시 사용하여 해당 클래스 내에서 "국가" DropDownList를 사용하도록 설정할 수도 있습니다. HTTP-GET 구현은 다음과 같습니다.

//
// GET: /Dinners/Create

public ActionResult Create() {

    Dinner dinner = new Dinner() {
        EventDate = DateTime.Now.AddDays(7)
    };

    return View(new DinnerFormViewModel(dinner));
}

HTTP-POST Create 메서드의 구현은 다음과 같습니다.

//
// POST: /Dinners/Create

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Dinner dinner) {

    if (ModelState.IsValid) {

        try {
            dinner.HostedBy = "SomeUser";

            dinnerRepository.Add(dinner);
            dinnerRepository.Save();

            return RedirectToAction("Details", new { id=dinner.DinnerID });
        }
        catch {
            ModelState.AddModelErrors(dinner.GetRuleViolations());
        }
    }

    return View(new DinnerFormViewModel(dinner));
}

이제 편집 및 만들기 화면 모두 국가 또는 지역을 선택하기 위한 드롭다운 목록을 지원합니다.

사용자 지정 모양의 ViewModel 클래스

위의 시나리오에서 DinnerFormViewModel 클래스는 Dinner 모델 개체를 지원 SelectList 모델 속성과 함께 속성으로 직접 노출합니다. 이 방법은 보기 템플릿 내에서 만들려는 HTML UI가 도메인 모델 개체와 비교적 밀접하게 일치하는 시나리오에서 잘 작동합니다.

그렇지 않은 시나리오의 경우 사용할 수 있는 한 가지 옵션은 개체 모델이 뷰에 의해 소비에 더 최적화되어 있고 기본 도메인 모델 개체와 완전히 다르게 보일 수 있는 사용자 지정 모양의 ViewModel 클래스를 만드는 것입니다. 예를 들어 여러 모델 개체에서 수집된 다른 속성 이름 및/또는 집계 속성을 노출할 수 있습니다.

사용자 지정 모양의 ViewModel 클래스를 사용하여 컨트롤러에서 렌더링할 뷰로 데이터를 전달하고 컨트롤러의 작업 메서드에 다시 게시된 양식 데이터를 처리할 수 있습니다. 이 이후 시나리오에서는 작업 메서드가 ViewModel 개체를 양식에 게시된 데이터로 업데이트한 다음 ViewModel instance 사용하여 실제 도메인 모델 개체를 매핑하거나 검색할 수 있습니다.

사용자 지정 모양의 ViewModel 클래스는 많은 유연성을 제공할 수 있으며 보기 템플릿 내에서 렌더링 코드 또는 작업 메서드 내의 양식 게시 코드가 너무 복잡해지기 시작할 때마다 조사할 수 있습니다. 이는 도메인 모델이 생성 중인 UI와 완전히 일치하지 않으며 중간 사용자 지정 모양의 ViewModel 클래스가 도움이 될 수 있다는 신호입니다.

다음 단계

이제 부분 및 master 페이지를 사용하여 애플리케이션 전체에서 UI를 다시 사용하고 공유하는 방법을 살펴보겠습니다.