본문 바로가기

IT 살이/04. 기술 - 프로그래밍

개발 프레임워크 만들기 대장정 33 - Spring.NET의 Result Mapping

▶ Result Mapping 이란.


Result Mapping ! 이것이 뭐냐면 "애플리케이션의 흐름"을 제어하는 방법중의 하나다. 애플리케이션 흐름? Response.Redirect, Server.Transfer 등이 바로 애플리케이션의 흐름을 제어하는 메소드들이다.  여기서 말하는 애플리케이션의 흐름은 페이지의 수행 결과에 따라서 이 페이지 저 페이지로 리다이렉트되는 것을 말하고 있다. Spring.NET에서는 이렇게 결과에 따라서 적절한 페이지로 리다이렉트될 수 있도록 사전에 매핑을 설정할 수 있는 방법이 있다.

페이지 수행 결과 리다이렉트될 대상 페이지
"SUCCESS" OK.aspx
"FAIL" Sorry.aspx

Spring.NET에서는 이런 설정을 configuration에 포함시킬 수 있다는 것이다. 그리고 실제의 aspx.cs에서는 Response.Redirect, Server.Transfer 메소드를 없앤다. Spring.NET이 지원하는 Result Mapping 방법을 이용하면 Controller가 직접 다른 View를 참조할 필요가 없다. 예를 들어 만약 수행 결과가 "SUCCESS" 인 경우 "success.aspx" 페이지로 리다이렉트되어야 한다면 OK.apsx로 설정되어 있는 내용을 success.aspx로 변경만 하면 된다. success.aspx로 넘겨야 하는 파라미터가 있다면 그 값도 configuration 할 수 있다.


▶ 기존 방법의 MVC 패턴 위배


aspx.cs에서 이런 메소드를 사용해서 직접 개발자가 페이지를 대상 페이지로 리다이렉트시키는 방법은 앞에서 말한 MVC 패턴에도 맞지 않는 부분이 있다. 앞에서 말한 MVC 패턴에서는 개발 작업에서 UI를 비즈니스 로직에서 분리시키는 것이 중요한 목표였다. 그러나  Controller 즉 Page 객체에서 직접 Redirect, Transfer 메소드를 사용하면 다음과 같은 상태가 되고 만다.

이런 상태가 되면 애플리케이션의 흐름이 변경된 경우 재 컴파일이 필요하고 필요하면 테스트, 배포 작업도 다시 해야 할 수 있다. 


▶ 수행 결과 추상화 -  Result  클래스


Spring.NET에서는 페이지의 수행 결과를 Spring.Web.Support 네임스페이스의 Result 타입으로 추상화하고 있다. 예를 들어 페이지 수행 결과 "SUCCESS", "FAIL"을 표현할 수 있는 객체이다.

다음과 같은 시나리오를 생각해보자. 사용자 등록 페이지 UserRegistration.aspx가 있다고 하자. 이 페이지에서 저장 버튼을 클릭하면 사용자 정보를 저장하고 나서 홈 페이지 Default.aspx로 리다이렉트되고 그리고 사용자 등록 페이지에서 취소 버튼을 클릭하면 로그인 페이지 login.aspx로 이동한다고 하자.  여기서 "홈 페이지로 이동"하고 "로그인 페이지로 이동"하는 것을 Spring.Web.Result로 표현하면 아래에서 첫번째, 두번째 <object> 설정과 같다. 

<object id="homePageResult" type="Spring.Web.Result, Spring.Web">

  <property name="TargetPage" value="~/Default.aspx"/>

  <property name="Mode" value="Transfer" />

  <property name="Parameters">

    <dictionary>

      <entry key="literal" value="My Text"/>

      <entry key="name" value="%{UserInfo.FullName}" />

      <entry key="host" value="%{Request.UserHostName}"/>

    </dictionary>

  </property>

</object>


<object id="loginPageResult" type="Spring.Web.Result, Spring.Web">

  <property name="TargetPage" value="Login.aspx"/>

  <property name="Mode" value="Transfer" />

</object>


<object type="UserRegistration.aspx" parent="basePage">

  <property name="UserManager" ref="userManager"/>

  <property name="Results">

    <dictionary>

      <entry key="SUCCESS" value-ref="homePageResult"/>

      <entry key="CANCEL" value-ref="loginPageResult"/>

    </dictionary>

  </property>

</object>

각 Result 타입에는 해당 결과로 인해 리다이렉트될 대상 페이지를 나타내는 TargetPage 속성이 있다.  위 설정에서 "homePageResult"라는 결과가 사용될때는 "~/Default.aspx"로 리다이렉트되고 "loginPageResult" 결과가 사용될때는 "Login.aspx"로 이동한다. 그리고 Result 타입에는 Mode 속성이 있다. 이 속성은 리다이렉트 방법을 나타낸다. 이 속성의 값으로는 "Transfer", "TransferNoPreserve", "Redirect"가 있는데 설정하지 않으면 "Transfer"가 사용된다. "TransferNoPreserve"는 Tranfer 메소드에서 "preserveForm=false"와 같다.

public void Transfer(
   string path,
   bool preserveForm
);

preserveForm 인자가 false이면 QueryStringForm 컬렉션 데이터는 지워진다.

세번째 <object/> 요소에서는 "UserRegistration.aspx" 페이지에서 이 Result 객체를 참조해서 사용하고 있다는 것을 설정하고 있다. 즉 UserRegistration.aspx의 실행 결과가 "SUCCESS"로 설정되는 경우는 "homePageResult"에서 정의한대로 리다이렉트로되고 결과가 "CANCEL"로 설정되는 경우는 "loginPageResult"에서 설정한대로 리다이렉트된다.

만약 리다이렉트될 대상 페이지에 파라미터를 넘기고 싶다면, Result 타입의 딕션너리 속성 Parameters을 사용한다. 이 속성에 <entry/> 요소를 파라미터가 필요한대로 포함시킨다. homePageResult 결과 설정예를 보면, "literal", "name", "host" 파라미터가 추가되어 있다. "literal" 속성처럼 리터럴 문자열이 파라미터 값으로 사용될 수도 있지만, 호출하는 페이지의 속성 값을 동적으로 설정할 수도 있다. "name", "host" 파라미터에는 현재 호출하는 페이지 즉 UserRegisteration 페이지의 UserInfo 속성의 FullName 속성값이 설정된다. 현재 호출하는 페이지를 나타내는 Page 객체에 FullName 속성을 갖는 UserInfo 속성이 public 속성으로 노출되어야 한다는 것을 말한다. 참고로 이때  "%{....}" 내부의 문자열을 해석하는 expression evaluation 프레임워크가 사용된다.

이렇게 추가된 파라미터들은 Result의 속성 Mode에 따라서 대상 페이지로 다르게 전달된다. Mode를 redirect로 설정하면 모든 파라미터들은 문자열로 변환되어 쿼리 문자열에 추가된다. 반면 transfer로 설정되면 HttpContext.items에 추가되어 대상 페이지로 전달된다.

UserRegistration.aspx 페이지에서 결과를 설정하는 코드 예를 보면 다음과 유사하게 된다.

protected override void OnInit(EventArgs e)

{

    //...

    this.saveButton.Click += new EventHandler(this.SaveUser);

    this.cancelButton.Click += new EventHandler( this.Cancel );

    //...

}


private void SaveUser(Object sender, EventArgs e)

{

    UserManager.SaveUser(UserInfo);

    SetResult("SUCCESS");

}


private void Cancel(Object sender, EventArgs e)

{

    SetResult("CANCEL");

}

Spring.Web.UI.Page에서 제공하는 SetResult 메소드를 사용하면 현재 페이지의 결과를 지정할 수 있다. 이제 이렇게 지정된 결과와 설정에 따라서 적절한 페이지로 리다이렉트된다.


정리를 하자면, Response.Redirect("~/Default.aspx") 대신에 SetResult("SUCCESS")를 사용한다는 것이다. 그래서 음....UI와 비즈니스 로직 분리,  MVC 패턴 준수 뭐 그렇다는 것이다.

이상!