잊지 않겠습니다.

트위터 시작

시끌벅적 2010. 1. 21. 00:03
트위터를 시작한 다음 거의 블로그레 안들어오게 되었다 보는 재미 말하는 재미....결국이것이 소통하는 재미가 아닐까
소통이라는 것이 별거있나 진심으로 듣고 진심을 가지고 이야기하고 바로 이런것이 소통인거지 뭐

iPhone 에서 작성된 글입니다.
Posted by Y2K
,

pd 수첩

시끌벅적 2010. 1. 12. 23:23
PD 수첩같은 프로그램이 있다는 것은 아직 우리나라가 완전히 희망이 없지않다는 증거이다.
아파트라는 민감한 문제를 건드리는 오늘의 방송이 어떨지 매우 기대가된다. 용산에 있었던 비극도 역시 관심이 있긴 하지만 그래도 아파트 이야기에 계속해서 더 관심이 가는것은 내가 어쩔수없는 속물이라서 그런것일까. 용산에서 있었던 참극에 나는 관련이 없다고 생각이 들어서일까.
가슴한편이 먹먹해지는 기분이든다. 두번다시 저런 비극이 되풀이 되지않기를...

iPhone 에서 작성된 글입니다.
Posted by Y2K
,
사용자 가입을 하는 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;
    }
}

View에서 LogFormatter를 이용해서 데이터의 ViewState를 적어준다.


    <%using(Html.BeginForm()){ %>        
        <%=Html.ValidationSummary() %>
        <%=Html.Hidden("regData", SerializationUtils.Serialize(Model)) %>
        

Name:<%=Html.TextBox("name") %>

E-Mail:<%=Html.TextBox("email")%>

<%} %>

이와 같이 구성 이후에, Html을 보면 색다른 결과가 나온다.


    

Name:

E-Mail:


그것은 VIEWSTATE를 이용해서 Wizard Form을 구성했기 때문인데, VIEWSTATE는 ASP .NET WEBFORM기술에서 악명이 높은 기술이다. 그렇지만, 이와 같이 구성이 된다면 서버의 Session상태에 구애받지 않고, 만약에 사용자가 밤새 웹브라우저를 열어놓은 일이 있더라도 값을 보장받을 수 있는 강력한 방법이 된다.

단, 주의할점이 하나 있다. VIEWSTATE 값은 Base64로 Formatting된 값이다. 이 값의 변조는 악의적인 사용자가 가능한 정보이기 때문에, 특별한 Encoding을 통하던, 아니면 다른 방법을 통해서 암호화를 하는 것이 용의하다.
Posted by Y2K
,
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는 다음과 같다.

New Product

<%=Html.ValidationSummary() %> <%using(Html.BeginForm()) { %> Name : <%=Html.TextBox("productValue.name") %>
Price : <%=Html.TextBox("productValue.price")%>
Description : <%=Html.TextBox("productValue.description")%>
<%} %>

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult New(Product productValue)
{
    try
    {
        productValue.CheckValidation();
    }
    catch(ProductException ex)
    {
        foreach(string key in ex.Keys)
        {
            ModelState.AddModelError(key, ex[key]);
        }                
        return View();
    }

    return RedirectToAction("NewResult",
        new { product = productValue });
}

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++) { %>
        

<%=string.Format("Product : {0}", i) %>

<%--C#에서 사용되는 배열 문법과 동일하다.(products[0].Name, products[0].Price)--%> Name : <%=Html.TextBox("products["+i.ToString()+"].Name") %>
Price : <%=Html.TextBox("products["+i.ToString()+"].Price") %>
Description : <%=Html.TextBox("products["+i.ToString()+"].Description") %>
<%} %> <%} %>

사용자 지정 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


Posted by Y2K
,
사고나서 이틀동안 가지고 노느니라고 정신이 없는 주말을 보내고 있다
동영상 인코딩,음악,악세사리 여러가질보고있는데 필름을 해야되나 고민이다 일단 케이스는 하나 했는데 액정은 내일 회사에서 좀 물어봐야할듯
바로 사진도 블로그에 올릴수있는 티스토리 프로그램 꽤나 괜찮은듯

iPhone 에서 작성된 글입니다.
Posted by Y2K
,
아이폰 구매
사고싶어서 안달이났던 물건이라서 행복하당
재미있는 장난감이 생긴듯 ㅋ ㅋ

iPhone 에서 작성된 글입니다.
Posted by Y2K
,
Javascript가 동작하지 않는 브라우져나 서버단에 치명적인 오류가 들어갈 수 있는 값의 경우에는 Server단에서의 Validation Check가 반드시 필요하게 된다. 전에 사용했던 IDataError interface의 경우에는 데이터 모델들의 처리가 되는데. 이는 일반적인 C#에서의 에러처리가 아닌 ASP .NET MVC에서의 Validateion 처리라고 조금은 한정지어서 생각할 수 있다.

생각해보면 C# 언어에 가장 맞는 Validation Check는 try~catch 를 이용한 방법이다. 이러한 방법을 이용해야지만이 만약 Entity를 다른 환경, 예를 들어 SilverLight에서 Entity를 표현하거나 WinForm으로 Entity를 표현할 때에 정확한 Validation Check가 가능하게 된다.

다음은 Product에 대한 Validation check code이다.

public class Product
{        
    public string Name { get; set; }
    public string Price { get; set; }
    public string Description { get; set; }

    public void CheckValidation()
    {
        ProductException exception = new ProductException();
        if(string.IsNullOrWhiteSpace(Name))
        {
            exception.Add("name", "Name is empty");
        }

        if(string.IsNullOrWhiteSpace(Description))
        {
            exception.Add("description", "Description is empty");
        }

        if(string.IsNullOrEmpty(Price))
        {
            exception.Add("Price", "Price is empty");
        }

        if(!exception.IsValid)
        {
            throw exception;
        }
    }
}

public class ProductException : Exception, IDictionary
{
    public bool IsValid { get { return _exceptMessages.Count == 0; } }
    private Dictionary _exceptMessages;
    public ProductException()            
    {
        _exceptMessages = new Dictionary();
    }

    #region IDictionary Members

    public void Add(string key, string value)
    {
        _exceptMessages.Add(key, value);
    }

    public bool ContainsKey(string key)
    {
        return _exceptMessages.ContainsKey(key);
    }

    public ICollection Keys
    {
        get { return _exceptMessages.Keys; }
    }

    public bool Remove(string key)
    {
        return _exceptMessages.Remove(key);
    }

    public bool TryGetValue(string key, out string value)
    {
        return _exceptMessages.TryGetValue(key, out value);
    }

    public ICollection Values
    {
        get { return _exceptMessages.Values; }
    }

    public string this[string key]
    {
        get
        {
            return _exceptMessages[key];
        }
        set
        {
            _exceptMessages[key] = value;
        }
    }

    #endregion

    #region ICollection> Members

    public void Add(KeyValuePair item)
    {
        _exceptMessages.Add(item.Key, item.Value);
    }

    public void Clear()
    {
        _exceptMessages.Clear();
    }

    public bool Contains(KeyValuePair item)
    {
        return _exceptMessages.Contains(item);
    }

    public void CopyTo(KeyValuePair[] array, int arrayIndex)
    {
        throw new NotImplementedException();
    }

    public int Count
    {
        get { return _exceptMessages.Count; }
    }

    public bool IsReadOnly
    {
        get { throw new NotImplementedException(); }
    }

    public bool Remove(KeyValuePair item)
    {
        return _exceptMessages.Remove(item.Key);
    }

    #endregion

    #region IEnumerable> Members

    public IEnumerator> GetEnumerator()
    {
        return _exceptMessages.GetEnumerator();
    }

    #endregion

    #region IEnumerable Members

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return _exceptMessages.GetEnumerator();
    }

    #endregion
}
Product을 사용하는 Controller의 예
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult New(Product productValue)
{
    try
    {
        productValue.CheckValidation();
    }
    catch(ProductException ex)
    {
        foreach(string key in ex.Keys)
        {
            ModelState.AddModelError(key, ex[key]);
        }                
        return View();
    }

    return RedirectToAction("NewResult",
        new { product = productValue });
}

Posted by Y2K
,
View는 WebApplication의 Request가 Response로 바뀌는 BlockBox가 된다.

MVC에서 View는 완전한 출력을 담당하는 책임을 가지고 있다. 단지 Controller의 출력만을 담당하며, 간결한 표현 로직을 사용하여 그 출력을 최종 HTML로 Render하는 임무를 가진다. 그렇지만, 표현로직과 업무로직사이의 경계는 매우 모호하다. 업무로직과 표현 로직에 대한 것은 조금은 주관적이라고 생각이 되기 때문에.. 최대한 Controller에서 동작을 하고 표현 부분에 대해서는 로직의 최대한도로 줄이는 것을 목표로 하는 것이 가장 좋다.
또한, View의 문제는 Test에 있다. Html을 테스트하는 것은 GUI 프로그램에서 사용자 액션을 테스트하는 것과 동일하다. 테스트 할 방법도 마땅하지 않으며, 표시된 HTML을 TEXT단위로 테스트 하는 것은 공백 문제부터 시작해서 많은 어려움이 있기 때문에, 많은 책들의 저자와 비슷하게 VIEW는 봐서 잘 되는 것이 최고다. 라는 생각이 든다. 딱히 테스트 할 방법도 안보이는 것이 사실이다.;

WebForm View Engine
: MVC Framework는 WebFormViewEngine이라는 class로 구성된 View Engine을 사용하고 있다. 이 ViewEngine은 기존의 ASP .NET WebForm Engine을 기반으로 구축되어있다. 그러나 WebFormViewEngine에서 View는 단순한 HTML Template이다. 이는 Controller에서 받은 특정 데이터인 ViewData와 강력히 형식화된 Model 객체를 기반으로 만들어진 HTML 문자를 표현하는 기능만을 가지게 된다. 또한 내부적으로 사용되는 기반 기술은 ASP .NET WebForm 서버 페이지 기술과 동일하다.만약에 자체적인 View Engine을 만들고 싶다면, IViewEngine과 IView interface를 상속받아 구현하면 된다.

View의 데이터 전송
: ViewData라는 Dictionary<string, object> 형식의 데이터 또는 강력한 형식의 Model을 이용하는 방법이 있다. 두 방법의 차이는 Controller에서는 전혀 알 필요가 없다. 단지 Controller에서 데이터 형식의 Model을 전송해서 View로 전달 될때, 강력한 Model 형식의 View의 경우에는 형변환이 가능한지만을 알아보게 된다. 따라서 이 방법은 매우 유용한 것이 된다.

ViewData.Eval()의 이용
: ViewData.Eval()은 대소문자를 가리지 않고, 값이 없는 경우에 String.Empty를 리턴하는 사용하기 편한 함수이다. 또한 Eval함수는 .으로 구분되는 토큰 명명법을 사용하고 있기 때문에 Model의 속성또한 표현할 수 있다. ViewData에 key를 Name으로 넣었는지, name으로 넣었는지. 아니면 값이 없는 경우에 NULL처리를 해주는 것을 빼먹지 않았는지 신경쓰지 않고 사용하기에 매우 좋은 함수이다. 또한 Html Helper method에서 Html.TextBox("comment")를 사용하는 경우, 이 코드는 다음과 같다. <input name="comment" id="comment" type="text" value="<%=Html.Encode(ViewData.Eval("comment"))%>"/>. 편하게 작성도 가능하고, 값이 없는 경우에 나오는 모든 에러처리를 해주기 때문에 간결한 코드 작성이 가능하다.
Posted by Y2K
,
.NET으로 COM+ 객체를 만드는 예제.

.NET으로 COM+객체를 만들때는 먼저 Project Property에 Comvisible(true)로 설정이 되어 있어야지 된다. 그리고 assembly:Guid 속성에 component guid를 등록시켜야지만 com 객체를 만드는 가장 기초적인 설정이 끝이 난다. 이와같은 설정을 하면 작성된 dll이나 exe를 com+ 객체로 등록할 수 있다.

// Setting ComVisible to false makes the types in this assembly not visible 
// to COM components.  If you need to access a type in this assembly from 
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(true)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("3308202e-a355-4c3b-805d-b527d1ef5fa3")]

그러나, dll을 등록한다고해서 library안의 class들이 모두 com객체로 사용할 수 있는 것이 아니다. dll은 여러개의 com객체를 담을 수 있는 상자이고, 그 상자안에 채워야지 될 com객체들은 각각 작성되어야지 된다. com을 이용하는 것은 com 객체 뿐 아니라, com 객체를 구성하고 있는 interface역시 사용할 수 있어야지 되기 때문에, com으로 외부에서 노출될 모든 interface와 class들은 Guid Class Property를 갖고, 각 Guid 값을 가져야지 된다.

com으로 사용되기 위해서, class와 interface가 가져야지 될 속성은 각각 다른데, interface는 ComInterfaceType Property와 Guid를 가져야지 되며, class는 Com으로 제공될 ComSourceInterfaces, ClassInterface, Transaction, Guid, Com 객체 사용에서 Pool과 CreateTimeOut을 제한하는 ObjectPooling attribute를 가져야지 된다.

com interface 구현 예제
#region Interfaces

[Description("ICSSimpleObject description")]
[Guid("2A59630E-4232-4582-AE47-706E2B648579")]
// The public interface describing the COM interface of the coclass 
public interface ICSSimpleObject
{
    #region Properties

    float FloatProperty { get; set; }

    #endregion

    #region Methods

    [Description("HelloWorld description")]
    string HelloWorld();

    void GetProcessThreadID(out uint processId, out uint threadId);

    [Description("DoTransaction description")]
    void DoTransaction();

    #endregion
}

[Guid("A06C000A-7A85-42dc-BA6D-BE736B6EB97A")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
// The public interface describing the events the coclass can sink
public interface ICSSimpleObjectEvents
{
    #region Events

    [DispId(1)]
    void FloatPropertyChanging(float NewValue, ref bool Cancel);

    #endregion
}
#endregion

com class 구현 예제
// The ObjectPooling attribute is used to configure the component's object 
// pooling. It can enable or disables object pooling and set the min. or  
// max. pool size and object creation timeout. 
[ObjectPooling(MinPoolSize=2, MaxPoolSize=10, CreationTimeout=20)]
// Creates the component with a new transaction, regardless of the state 
// of the current context.
[Transaction(TransactionOption.Required)]
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(ICSSimpleObjectEvents))]
[Guid("14EAD156-F55E-4d9b-A3C5-658D4BB56D30")]
public class CSSimpleObject : ServicedComponent, ICSSimpleObject
{
    #region Properties

    /// 
    /// The private members don't make it into the type-library and are 
    /// hidden from the COM clients.
    /// 
    private float fField = 0;

    /// 
    /// A public property with both get and set accessor methods.
    /// 
    public float FloatProperty
    {
        get { return this.fField; }
        set
        {
            bool cancel = false;
            // Raise the event FloatPropertyChanging
            if (null != FloatPropertyChanging)
                FloatPropertyChanging(value, ref cancel);
            if (!cancel)
                this.fField = value;
        }
    }

    #endregion

    #region Methods

    /// 
    /// A public method that returns a string "HelloWorld".
    /// 
    /// "HelloWorld"
    public string HelloWorld()
    {
        return "HelloWorld";
    }

    /// 
    /// A public method with two outputs: the current process Id and the
    /// current thread Id.
    /// 
    /// [out] The current process Id
    /// [out] The current thread Id
    public void GetProcessThreadID(out uint processId, out uint threadId)
    {
        processId = NativeMethod.GetCurrentProcessId();
        threadId = NativeMethod.GetCurrentThreadId();
    }

    public void DoTransaction()
    {
        try
        {
            // Operate on the resource managers like DBMS
            // ...
	
            ContextUtil.SetComplete();  // Commit
        }
        catch (Exception ex)
        {
            ContextUtil.SetAbort();     // Rollback
            throw ex;
        }
    }

    #endregion

    #region Events

    [ComVisible(false)]
    public delegate void FloatPropertyChangingEventHandler(float NewValue, ref bool Cancel);

    /// 
    /// A public event that is fired before new value is set to the
    /// FloatProperty property. The Cancel parameter allows the client 
    /// to cancel the change of FloatProperty.
    /// 
    public event FloatPropertyChangingEventHandler FloatPropertyChanging;

    #endregion
}
Posted by Y2K
,
모든 Controller는 IController interface를 구현한다.

* Mvc.Controller의 특징
1. ActionMethods
  : Controller의 동작이 여러개의 method로 분할된다. 각 Action method는 서로다른 URL로 노출되며,
  들어오는 요청에서 추출된 매개변수를 가지고 있다.
2. ActionResult
  : Action의 의도된 결과를 나타내는 개체를 선택해서 반환할 수 있다. 결과를 지정하는 것과 실행하는
  것이 분리되기 때문에 자동화된 테스트가 상당히 간단하게 구성될 수 있다.
3. Filter
  : 재사용 가능한 동작들을 Filter로 캡슐화할 수 있다.

* Controller의 입력 처리
Mvc.Controller에서 사용 가능한 속성들
1. Request.QueryString : 요청과 함께 전송된 GET 변수들
2. Request.Form : 요청과 함께 전송된 POST 변수들
3. Request.Cookie : 요청과 함께 전송된 Cookie
4. Request.HttpMethod : 요청을 위해 사용된 Http method
5. Request.Headers : 요청과 함께 전송된 HTTP Header
6. Request.Url : 요청된 Url
7. Request.UserHostAddress : 요청을 보내온 사용자의 Ip Address
8. RouteData.Route : 요청에 대해 선택된 RouteTable.Routes Entry
9. RouteData.Values : 현재의 Route 매개 변수(URL에서 추출된 값이거나 기본 값)
10. HttpContext.Application : Application state 저장소
11. HttpContext.Cache : Application cache 저장소
12. HttpContext.Items : 현재 요청에 대한 상태저장소
13. User : 로그인 한 사용자의 인증정보
14. TempData : Session 내에서 이전 HTTP 요청을 처리하는 동안에 저장된 임시 Data

* Controller의 출력 처리
a. View를 Rendering하는 것으로 HTML 반환
b. HTTP 재전송 호출(311, 312 code 반환)
c. 응답의 출력 스트림에 다른 데이터 전송(xml, json, file)

* Action method 안에서의 직접적으로 Response를 다루는 일은 절대로 하지 않아야지 된다. 대신에 특정한 응답을 나타낼 수 있는 ActionResult를 상속한 객체를 반환하도록 한다.

* ActionResult 형식들
1. ViewResult : 지정된 View Template이나 기본 View Template을 Rendering 한다. [Mvc.Controller.View() method]
2. PartialViewResult : 지정된 Partial View Template이나 기본 Partial View Template을 Rendering한다.(Mvc.Controller.PartialView()]
3. RedirectToRouteResult : Routing 구성 설정에 따라 URL을 생성하면서 302 재전송을 하게 한다.[Mvc.Controller.RedirectToAction()]
4. RedirectResult : 임의의 URL로 HTTP 302 재전송을 하게 한다. RedirectToRouteResult와 다른 점은 RedirectResult는 외부의 URL을 사용가능하다는 차이점을 가지고 있다.
5. ContentResult : 브라우저로 text 데이터를 반환할 수 있다. 선택적으로 Content Header를 지정 가능하다.
6. FileResult : 이진 데이터를 브라우저로 직접 전달한다.
7. Jsonresult : .NET 객체를 Json 포멧으로 직렬화하고 이를 응답으로 전송한다.
8. JavascriptResult : 브라우저에 의해 실행될 수 있는 javascript 소스코드의 일부를 전송한다.
9. HttpUnauthorizedResult : Http 응답코드를 401로 설정한다.
10. EmptyResult : 아무 일도 하지 않는다.

사용자 지정 ActionResult를 만들고 싶은 경우(Image를 가공해서 반환한다던지 등의 Action이 가능하다)에는 ActionResult를 상속받아서 ExecuteResult를 재정의해서 사용하면 된다. 다음은 그 예제 코드이다.


public class WatermarkedImageResult : ActionResult
{
    public string ImageFileName { get; private set; }
    public string WatermarkText { get; private set; }

    public WatermarkedImageResult(string imageFileName, string watermarkText)
    {
        ImageFileName = imageFileName;
        WatermarkText = watermarkText;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        using(var image = Image.FromFile(ImageFileName))
        using(var graphics = Graphics.FromImage(image))
        using(var font = new Font("Arial", 10))
        using(var memoryStream = new MemoryStream())
        {
            var textSize = graphics.MeasureString(WatermarkText, font);
            graphics.DrawString(WatermarkText, font, Brushes.White, 10, image.Height - 10 - textSize.Height);

            image.Save(memoryStream, ImageFormat.Png);
            var response = context.RequestContext.HttpContext.Response;
            response.ContentType = "image/png";
            response.BinaryWrite(memoryStream.GetBuffer());
        }
    }
}

* TempData와 Session과의 비교
기본적으로 TempData의 저장소는 Session이 된다. 그렇기 때문에 TempData를 사용하고 싶으면 반드시 Session을 enable 시켜야지 된다. 하지만 TempData의 고유한 특징은 이것이 매우 작은 데이터라는 것이다. 각 entry는 오직 한번의 이전 요청을 저장하고 난 다음 없어진다. 처리 후에 자동으로 청소가 되니 RedirectToAction()에 걸쳐 개체를 유지하는데 적합하다.

* 재 사용가능한 Filter 처리하기
Filter를 이용해서 Controller와 ActionMethod에 추가적인 동작을 지정할 수 있다.
Filter는 요청 처리 PipeLine에 별도의 단계를 추가하는 .NET Attribute로 Action method의 실행 전후에, ActionResult가
실행 전후에 추가적인 로직을 삽입할 수 있다.

Action method의 실행 전후는 IController.Execute method의 실행 전, 후를 의미하며,
ActionResult의 실행 전후는 ActionResult.ExecuteResult method의 실행 전, 후를 의미한다.
따라서 실행 순서는 OnActionExecuting, OnActionExecuted, OnResultExecuting, OnResultExecuted 순서로 실행된다.

ex) HandleError : HandleErrorAttribute는 Controller의 Action method가 실행될 때에 예외가 발생되면 HandleError에 지정된 View가 Render된다.이때에 web.config에 customError mode가 on이 되어있어야지 된다. 이때 지정된 View는 HandleErrorInfo data model이 전달되며, 이는 불려진 Controller와 ActionName, Exception의 정보를 담게 된다. 이 상태는 Error를 처리된 상태로 만들게 되며 따라서 Http 응답코드는 에러가 아닌 200이전달되게 된다.


Controller Test Code
* Controller의 Unit Code는 의미있는 단위 테스트를 작성하기 위해서 많은 사람들이 AAA Pattern을 따른다. Arrange-Act-Assert로 이루어지는
이 방법은 대부분의 Controller 테스트에 유용하다.

[TestClass]
public class HomeControllerTest
{
    [TestMethod]
    public void CheckViewName()
    {
        HomeController controller = new HomeController();
        ViewResult viewResult = controller.Index();

        Assert.IsNotNull(viewResult);
        Assert.AreEqual(viewResult.ViewName, "index");
    }

    [TestMethod]
    public void CheckViewData()
    {
        HomeController controller = new HomeController();
        ViewResult viewResult = controller.Index();

        Assert.IsNotNull(viewResult);
        Assert.AreEqual(6, viewResult.ViewData["age"]);
    }

    [TestMethod]
    public void CheckRedirection()
    {
        HomeController controller = new HomeController();
        RedirectToRouteResult result = controller.Index();

        Assert.IsNotNull(result);
        Assert.AreEqual("RedirectActionName", result.RouteValues["action"]);
    }
}
Posted by Y2K
,