CAFE

ASP.NET MVC

ASP.NET MVC : ViewData, ViewBag, TempData 사용법 및 차이점 알아보기

작성자심재운|작성시간13.11.12|조회수5,825 목록 댓글 2

 


ASP.NET MVC 어플리케이션으로 작업하는 동안, controller action 메소드에서 view 에 데이터를 전달할 필요가 있습니다. 여기에는 다양한 기술들을 제공해 주는데요. ViewData, ViewBag 그리고 TempDate 가 이것을 기술해 줄 수 있습니다. 이번 내용은 이 3가지 기술에 대해 사용법이나 차이점에 대해 알아보고자 합니다.

ASP.NET MVC 어플리케이션의 controller 는 쉽게 model 에서 view 로 아래와 같이 View() 메소드만으로 오버라이드 사용하여 쉽게 전달할 수 있습니다.

public ActionResult Index()
{
  List<Customer> model = GetCustomers();
  return View(model);
}


Index() 메소드를 보면, Customer 객체의 List 제네릭은 GetCustomers()  함수를 통해 데이터를 얻고, 이를 View() 메소드를 호출하여 Index view 에 이를 전달합니다. Index view 는 Model 속성을 사용하여 해당 model 에 접근할 수 있습니다.


model 또는 view model 클래스를 통해 하나의 view 로 데이터를 전송하는 방법에 대해 실질적으로 추천하는 방법입니다. 때때로 view 에 임의의 데이터를 전송할 필요가 있을 때도 있습니다. 예를 들어, 브라우저에서 렌더링 할 수 있도록 controller 에서 view 로 성공 또는 오류 메시지를 전달해야 할 수도 있습니다.

또는 view 에 어떤 계산 된 결과값를 가지고 보조 개체에 전달 하기를 필요할 지도 모릅니다. 

바로 이것이 ViewData, ViewBag , TempData 객체의 역할입니다. 

ViewData, ViewBag 그리고 TempData 는 controller 그리고 view 에 사용할 내장형 객체(inbuilt objects)입니다. 각각의 객체들은 특별한 기능을 스스로 가지고 있습니다. 이에 대해 더 자세히 알아보도록 하겠습니다.


controller 에서 view 로 데이터에 합리적인 데이터의 량을 전송할 필요가 있다면, 이는 model 부분에서 하는게 아니라,  view model 을 생성하여 이를 통해 주어야 합니다. 만약 view model 에 대해 어떤 사유로 사용할 수 없을 경우, 데이터 전송을 용이하게 하기 위해 ,여기에 설명 된 기술을 사용할 수 있습니다.


ViewData


먼저 ViewData 의 목적은  controller 에서 view 로 데이터를 가져오는 것입니다. ViewData 는 dictionary 객체이며, ViewDataDictionary 형식입니다. .net 에서 또 다른 dictionary 객체와 비슷하게 ViewData 도 key 와 value 를 저장할 수 있게끔 합니다. ViewData 객체에 저장된 Data 는 오직 현재 요청하는 동안에만 존재합니다. 다시 말씀드리면, view 가 브라우저에 랜더링 되자마자 ViewData 객체는 비워(empty) 지게 됩니다.

아래 코드는 action method 에서 개발자가 명시한 "message" 라는 ViewData 의 key 이름을 설정하는 것을 보여줍니다.

public ActionResult Index()
{
  Customer model = new Customer() { 
                   CustomerID = "ALFKI", 
                   CompanyName = "Company 1", 
                   ContactName = "Contact 1", 
                   Country = "Country 1" };
  ViewData["message"] = "This is a test message";
  return View(model);
}


Index() 의 action method 는 Customer 클래스에 대한 인스턴스를 생성합니다. 이렇게 생성 된 Customer 객체는 Index view 의 model 역할을 할 예정이다. 그리고 ViewData dictionary 에 message 키가 저장되고, value 에 "This is a test message" 라고 설정하게 됩니다. 최종적으로 action method 는 View() 메소드의 호출을 통해 Index view 에 반환하며, 이를 Customer model 에 전달됩니다.

@model ViewDataViewBagTempData.Models.Customer

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
    <h1>@Model.CustomerID</h1>
    <h3>@ViewData["message"]</h3>
    <h2>@ViewData.Model.CompanyName</h2>
</body>
</html>

 

@model 은 view 에서 ViewDataViewBagTempData.Models.Customer 에 데이타 모델을 설정합니다. 해당 data model 은 @Model.CustomerID 에 의해 보여준대로 Model 속성을 사용하여 접근이 가능합니다.

그리고 다음 코드는 dictionary 문법을 사용하여 ViewData key 의 message 값을 출력합니다.

마지막 라인은 view 와 연관되어 있는 model 에 접근하여 ViewData 객체의 Model 속성을 사용한 부분입니다. 그래서 model 은  Model property을  통해 view 에 직접 사용할 수 있기 때문에, 이 방식으로 거의 사용하지 않더라도 ViewData는 model 에 대한 포인터를 제공합니다.

요청과 응답에 대한 cycle 이 완료(complete) 되자마자 ViewData dictionary 는 empty 하게 됩니다.

앞에 예제에서 ViewData 에 문자값을 저장했습니다. 그리고 integer, boolean, object 와 같은 다른 데이터 타입에도 저장할 수 있습니다. 아래와 같이 ViewData 안에 CustomerInfo 객체를 저장하는 것을 보실 수 있습니다.

public ActionResult Index()
{
  Customer model = new Customer() { ... };
  CustomerInfo info = new CustomerInfo() { 
                      LastOrderID = 100, 
                      LastOrderDate = new DateTime(2013, 01, 20), 
                      LastOrderAmount = 12345.6M };
  ViewData["extrainfo"] = info;
  return View(model);
}

 위의 코드에서 Customer model 를 생성하고, 추가적으로 CustomerInfo 클래스를 인스턴트 생성했습니다. CustomerInfo 클래스는 3개의 속성 (LastOrderID, LastOrderDate, LastOrderAmount) 을 가지고 있습니다. ViewData 의 key 에 extrainfo 라는 이름을 설정하고 CustomerInfo 객체를 할당 하였습니다. 

ViewData 내부에는 제네릭 객체가 저장되었고, view 내부에서 이를 추출하는 동안 이를 형변환해야 합니다.

<h3>@{
  CustomerInfo info = (CustomerInfo)ViewData["extrainfo"];
  @info.LastOrderID
  @: |  
  @info.LastOrderDate
  @: |  
  @info.LastOrderAmount
}</h3>

 

위의 코드를 보면, ViewData 에서 extrainfo 이름을 호출하여 이를 CustomerInfo 클래스에 형변환하여 Index view 에 할당합니다. 그리고 CustomerInfo 클래스의 속성을 view 에 여러가지 값들을 보여줄 수 있습니다. 주의할 점은 ViewData 전체에 null 인지,  view 에서 null 값 체크를 또한 필요가 있습니다.

ViewBag

 

ViewBag 은 ViewData 를 래퍼(wrapper) 한 부분입니다. dictionary 객체들을 사용하는 key-value 보다 더 낳은 object-property 문법을 사용하여 값을 추출 및 저장하는 방법입니다. 이는   dynamic data type feature of .NET.  를 사용할 수 있습니다. 추가적으로 ViewBag 은 ViewData 객체를 가지고 형변환 작업을 사용할 수 있습니다.   

public ActionResult Index()
{
  Customer model = new Customer() { ... };
  CustomerInfo info = new CustomerInfo() { ... };
  ViewBag.ExtraInfo = info;
  return View(model);
}

 


ViewBag 객체에 CustomerInfo 객체를 저장하였으며, ExtraInfo 라는 속성을 명시하였고, 이제 내부 값을 확인하기 위해 QuickWatch 윈도우에서 ViewData 객체를 살펴보죠.


 

ExtraInfo 의 dynamic 속성은 ExtraInfo 의 ViewData key 처럼 실질적으로 저장되어 있습니다. 물론 해당 객체를 접근하기 위해서 지정된 syntax 에 맞게 기술해야 합니다. view 에 CustomerInfo 를 추출하기 위해서 아래와 같은 코드를 기술합니다.

<h3>@{
  @ViewBag.ExtraInfo.LastOrderID
  @: |  
  @ViewBag.ExtraInfo.LastOrderDate
  @: |  
  @ViewBag.ExtraInfo.LastOrderAmount
}</h3>

 

여기서는 ExtraInfo 에 대해 CustomerInfo 라는 형변환을 필요로 하지 않고 곧바로 속성에 접근할 수 있습니다. (그러나 기억할 점은 이러한 속성들은 아쉽게도 Visual Studio 의 인텔리센스에서 제공해 주지 않는다는 점..)

ViewData 또는 ViewBag 의 사용에 대해 어떤 것을 사용할지는 개발자의 선택과 형변환의 필요에 따라 고려 해 보시면 되겠습니다. ViewBag 은 단지 ViewData 의  wrapper 이며, ViewBag 내부에 무엇이든 저장하여, 현재 request 동안에만 오직 접근이 가능하다는 점을 숙지하시면 되겠습니다.


TempData 

 


TempData 는 ViewData 와 같이 dictionary 저장소입니다. 하지만 TempData 에 저장된 값은 몇몇의 view 에서 읽지 않은 것들이 존재합니다. 공통적으로 대부분 TempData 는 현재 request 와 다음 request 사이의 값을 전달하는데 사용되곤 합니다. 예를 들어, 하나의 레코드값을 데이터베이스로 부터 삭제하는 것을 고려한다고 하면, 두개의 메소드 즉 Index() 와 Delete() 메소드를 생성합니다. Index() 의 action 메소드는 레코드 셋을 가져오기도 하고, 테이블에 보여줄 Index view 에 전달하기로 합니다.  


Index view 는 모든 레코드를 보여주는 table 로 구성되며, 각각의 테이블 row 은 Delete 링크로 인해 Delete() 의 action method 를 지정하게 됩니다. Delete() 의 action method 는 레코드 삭제 작업을 하며, 즉시 하나의 레코드를 삭제하고 나서 Index() 의 action 메소드를 호출합니다. 삭제 성공 여부를 확인하기 위해 TempDate 에 "message" 이름을 지정하고, "Record deleted successfully!" 라는 값을 TempDate 에 문자열을 입력합니다. 그리고 Index() 의 action method 로 이동하기 위해 RedirectToAction() 함수를 사용하여 Index() 함수로 이동합니다.

public ActionResult Index()
{
  Customer model = new Customer() { ... };
  return View(model);
}

public ActionResult Delete(int id)
{
  //delete record here
  TempData["message"] = "Record deleted successfully!";

  //transfer control to Index(). This will be another request.
  return RedirectToAction("Index");
}

 

먼저 Delete() 가 요청되고, TempData 키 이름인 message 를 명명하여 값을 할당합니다. RedirectToAction() 호출로 인해 Index() 메소드의 control 로 가게 됩니다. 기억해야 할 것은, RedirectToActon() 은 명시된 action method 로 GET request 를 브라우저에서 생성하기 위해 브라우저에 HTTP 302 응답을 반환합니다. 이 시점에서 두 번째 요청이 이루어집니다.  두 번째 요청의 첫 번째 요청 중에  데이터에 액세스를 하기 위해서  TempData 를 사용하게 됩니다.

<body>
    <h1>Index View</h1>
    <h3>@TempData["message"]</h3>
</body>

So far so good. While the above example works as expected, you need to be aware of a tricky thing. If you don't read the TempData values in the second request, they are persisted and can be read in yet subsequent request. They exist unless you read them in some view at which point of time they are removed. Consider the following code that makes this behavior clear:


 위의 예에서 예상대로 작동하는 동안,  까다로운 것이 존재하는데요. 두 번째 요청에서 TempData에 값을 읽을 수없는 경우, 이를  유지하고 아직 후속 요청에서 읽을 수 있습니다. 


public ActionResult Index1()
{
  TempData["message"] = "Test message!";
  return View();
}

public ActionResult Index2()
{
  return View();
}

public ActionResult Index3()
{
  return View();
}

 


Index1 view 가 Index2 view 를 호출하고, Index2 view 가 Index3 view 를 호출한다고 가정합시다. 

Index1 과 Index2 view 에서 TempData 를 전혀 사용하지 않았다면, Index3 view 까지 사용 가능합니다. 

하지만 Index1 이나 Index2 둘 중 하나에서 TempDate 를 사용하면, 요청이 완료되자 마자 제거 됩니다. 

Index3 view 은 경우에 있어서 TempData 값을 읽을 수 없습니다.




 TempData 에는 Keep() 메소드가 존재하며,  읽은 후에도 값 (들)을 보존 할 방법을 유지하고있다. 이렇게하려면, 일단 당신이 읽은 값은 TempData.Keep () 또는 TempData.Keep (<key>)를 호출합니다. (Keep() 호출하면 다음 읽기 작업까지 유지를 위한 TempData에 키를 표시합니다.


 

TempData 값은 실질적으로 ASP.NET Session 에 저장되며, 자동적으로 읽은 다음에 삭제됩니다.  실제로 세션에 저장되어있는 것을 증명하기 위해,    web.config 에 아래와 같이  추가하여 전체 세션 상태를 해제합니다

<sessionState mode="Off"></sessionState>

 

해제하지마자, 오류가 발생되는 것을 볼 수 있습니다.



 

꼭 ViewData, TempData 은 항상 형변환이 이루어져야 하며, 값을 접근하기 전에 null 값을 체크 해주시기 바랍니다.


요약하자면, 

  • ViewData, ViewBag and TempData allow you to pass values from a controller to a view.
    (ViewData, ViewBag, TempData 는 controller 에서 view 로 값을 전달하는 역할을 한다.)
  • ViewData and TempData objects allow you to store values as key-value pairs.
    (ViewData, TempData 객체는 key-value 쌍으로 값을 저장 가능하다.)
  • ViewBag object allows you to store values using object-properties syntax.
    (ViewBag 객체는 object-properties 문법을 사용하여 값을 저장한다.)
  • ViewBag is a wrapper over ViewData.
    (ViewBag 은 ViewData 의 wrapper 이다.)
  • ViewData and ViewBag values are available only during current request.
    (ViewData 와 ViewBag 값들은 오직 현재 요청하는 동안에만 가능하며, 존재한다.)
  • TempData values are accessible in the current request and the subsequent requests. They are removed when a view reads them.
    (TempData 값은 현재 요청에서 다음 요청 시 접근 가능합니다. 그리고 읽게 되면 곧바로 삭제 됩니다.)
  • TempData internally uses Session to store its values. The values are removed once they are read.
    (TempData 는 내부적으로 Session 으로 값을 저장합니다. 값은 읽자마자 삭제 됩니다.)
  • ViewData and TempData require typecasting and null checking whereas ViewBag doesn't need such checking.
    (ViewData, TempData 는 형변환이 요구되며, null 체크를 해야 합니다. 대신 ViewBag 은 이런 작업을 할 필요가 없다.)

감사합니다. (번역 : 심재운 (shimaprk@zeospace.com))





다음검색
현재 게시글 추가 기능 열기

댓글

댓글 리스트
  • 작성자망고 | 작성시간 13.11.12 아직 공부단계라 TempData에 대해선 몰랐는데, 좋은 설명 감사합니다. 실습해보면서 차이점을 느껴봐야겠네요.
  • 답댓글 작성자심재운 작성자 본인 여부 작성자 | 작성시간 13.11.12 ^^
댓글 전체보기
맨위로

카페 검색

카페 검색어 입력폼