PD 수첩같은 프로그램이 있다는 것은 아직 우리나라가 완전히 희망이 없지않다는 증거이다.
아파트라는 민감한 문제를 건드리는 오늘의 방송이 어떨지 매우 기대가된다. 용산에 있었던 비극도 역시 관심이 있긴 하지만 그래도 아파트 이야기에 계속해서 더 관심이 가는것은 내가 어쩔수없는 속물이라서 그런것일까. 용산에서 있었던 참극에 나는 관련이 없다고 생각이 들어서일까.
가슴한편이 먹먹해지는 기분이든다. 두번다시 저런 비극이 되풀이 되지않기를...
사용자 가입을 하는 Form의 경우에는 일반적인 Wizard Form을 취하게 된다. Wizard Form을 통해서, 사용자 정보 수집과 각 정보에 대한 Validation check를 하게 되는데, 이 때에 너무나 많은 질문으로 인하여 당황하지 않게 하는 점진적인 표현(progressive disclosure)이라는 사용자 원칙을 따른다. 모든 질문들은 전부 서로 관련이 있는 것이 아니므로 각 단계별로 소량의 질문들을 나타내는 것이다.
여러단계를 통한 이동을 하게 되는 경우, 고려되어야지 되는 것은 Next 를 통한 입력한 정보의 전달, 그리고 Back으로 돌아갔을 때 사용자의 정보의 유지가 필요하게 된다. 이를 Session으로 처리해주는 방법도 ASP .NET MVC에서는 TempData와 데이터 Serialization을 통해서 이를 쉽게 구현 가능하다.
먼저, 사용자 데이터를 LogFormatter로 Serialization시켜주기 위한 Help method와 사용자 데이터 class를 만들어준다.
[Serializable]
public class RegistrationData
{
public string Name { get; set; }
public string Email { get; set; }
public int? Age { get; set; }
public string Hobbies { get; set; }
}
public static class SerializationUtils
{
public static string Serialize(object obj)
{
using(StringWriter sw = new StringWriter())
{
new LosFormatter().Serialize(sw, obj);
return sw.ToString();
}
}
public static object Deserialize(string data)
{
if(data == null)
{
return null;
}
else
{
return (new LosFormatter()).Deserialize(data);
}
}
}
그리고, Controller의 OnActionExecuting과 OnActionExecuted 함수를 다음과 같이 재 정의 한다. 이제 Controller의 모든 Action은 TempData를 검사하고, TempData에서 데이터가 있는 경우에는 Deserialization을 통해서 사용자 데이터를 가지고 온다.
public RegistrationData RegData { get; set; }
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
//Action이 call 될 때, RegData 값을 update 시켜준다.
RegData = (SerializationUtils.Deserialize(Request.Form["regData"]) ?? TempData["regData"] ??
new RegistrationData()) as RegistrationData;
TryUpdateModel(RegData);
}
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
if(filterContext.Result is RedirectToRouteResult)
{
//Back이나 Next로 RedirectToAction이 호출이 되었을때, TempData에 regData값을 넣어준다.
TempData["regData"] = RegData;
}
}
그것은 VIEWSTATE를 이용해서 Wizard Form을 구성했기 때문인데, VIEWSTATE는 ASP .NET WEBFORM기술에서 악명이 높은 기술이다. 그렇지만, 이와 같이 구성이 된다면 서버의 Session상태에 구애받지 않고, 만약에 사용자가 밤새 웹브라우저를 열어놓은 일이 있더라도 값을 보장받을 수 있는 강력한 방법이 된다.
단, 주의할점이 하나 있다. VIEWSTATE 값은 Base64로 Formatting된 값이다. 이 값의 변조는 악의적인 사용자가 가능한 정보이기 때문에, 특별한 Encoding을 통하던, 아니면 다른 방법을 통해서 암호화를 하는 것이 용의하다.
Data Binding : 규약을 이용하여 데이터 Entiry를 다루는 MVC Framework의 기능.
Model Binding
: HTML form의 submit시에 응용프로그램은 key/value pair로 폼의 데이터를 담고 있는 HTTP response를 받게 된다.
이때에, Http 요청 데이터를 Action method의 매개변수 및 사용자 정의 .NET 객체와 직접적으로 mapping하기 위한 알고리즘을 제공한다.
Model Binding 시의 데이터 적용 순서
1) Form(POST 매개변수 : FormCollection)
2) RouteData
3) QueryString
사용자 정의 형식에 대한 Model Binding : 기본적으로 DefaultModelBinder는 {parameterName.PropertyName}으로 검색을 한다. (* 대소문자는 가리지 않는다.)
다음과 같은 View가 존재한다고 할때에, 대응되는 ActionMethod는 다음과 같다.
BindAttribute의 이용
Bind Attribute는 Action Method의 매개변수 이름이 아닌 다른 이름으로 Binding을 원하거나, 특정 속성들이 모델 바인딩의 대상이 되어야하는지를 엄밀하게 제어할 필요가 있을 때 사용된다.
///
/// 다음 ActionMethod는 productA.*, productB.* 로 prefix된 view의 id를 이용해서
/// 값을 Binding한다.
///
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult New([Bind(Prefix = "productA")] Product product1,
[Bind(Prefix = "productB")]Product product2)
{
}
///
/// 다음 ActionMethod는 Name과 Price를 Binding에 포함한다.
///
///
///
public ActionResult RegisterMember([Bind(Include = "Name, Price")] Product product)
{
}
///
/// 다음 ActionMethod는 DateOfBirthDay를 Binding하지 않는다.
///
///
///
public ActionResult DeregisterMember([Bind(Exclude = "DateOfBirthDay")] Product product)
{
}
[Bind(Include="Name")]
public class Product
{
public string Name { get; set; }
public string Price { get; set; }
public string Description { get; set; }
}
Array, Collection, Dictionary에 대한 Model Binding
: 동일한 이름을 갖는 여러개의 Textbox를 Render하는 View의 Controller는 다음과 같이 사용될 수 있다.
<%=Html.TextBox("movies") %>
<%=Html.TextBox("movies") %>
<%=Html.TextBox("movies") %>
public ActionResult DoSomething(IList movies)
{
throw new NotImplementedException();
}
사용자 정의 Entry의 Group에 대해서는 다른 방법이 필요하게 되는데, DefaultModelBinder는 ActionMethod의 사용에 C#의 문법과 동일한 명명 규약을 따르기를 요구한다. 다음과 같은 ActionMethod가 존재할 때에, 사용되는 View는 다음과 같다.
public ActionResult DoSomething(IList products)
{
foreach(Product product in products)
{
System.Diagnostics.Debug.WriteLine(product.Name);
}
throw new NotImplementedException();
}
<%using(Html.BeginForm("DoSomething", "Products")) { %>
<%for(int i = 0 ; i < 10 ; i++) { %>
사용자 지정 Model Binder의 지정
: 특정한 데이터에 대하여 사용자 지정 Model Binder를 사용하고 싶은 경우에는 IModelBinder interface를 상속한 사용자 지정 ModelBinder를 만들어주면 된다.
Model Binder가 사용되도록 구성하기 위해서 3가지 방법중 하나를 사용할 수 있다.
1) Model에 ModelBinderAttribute의 적용
[ModelBinder(typeof(XDocumentBinder))]
public class XDocument
{
//...
}
2) ModelBinders.Binders.Add를 이용, 전역 ModelBinder에 등록하기
ModelBinders.Binders.Add(typeof(XDocument), new XDocumentBinder());
3) 매개변수를 Binding할때, Parameter attribute를 넣어주기
public ActionResult DoSomething([ModelBinder(typeof(XDocumentBinder))] XDocument xml)
{
//...
}
.NET MVC Framework에서 ModelBinder Select 순위
1) Binding시에 명시적으로 지정된 Binder
2) 대상 형식을 위해 ModelBinders.Binders에 등록된 Binder
3) 대상 형식에 ModelBinderAttribute를 사용하여 할당된 Binder
4) DefaultModelBinder