빼꼼...

안녕하세요 웹지니입니다! 흐헤헤 -ㅅ-;;;
어제 있었던 북한의 도발 때문에 많이들 불안하셨을 것으로 생각합니다.
저로서는 조금 더 부지런히, 열심히 살아야겠다고 다시 한 번 추스르게 된 계기가 되었어요.
뭐... 이번 포스트를 꼭 그래서 올리는 건 아니구요...-ㅅ-;;

예전에 블로그를 통해 Custom HtmlHelper 메서드를 구현하는 것과 관련된 글을 올린 적이 있었어요. 벌써 1년도 넘게 지난 시점이군요. 당시 HtmlHelper 클래스의 확장 메서드를 구현하면서 최종적으로 태그를 생성하는 과정에서 ASP.NET MVC가 제공하는 TagBuilder 클래스를 사용했었는데요.

오늘 포스트는 요즘 유행(?)하는 Fluent Interface 패턴 형태로 TagBuilder 클래스를 손쉽게 활용할 수 있도록 확장하는 내용을 담아봅니다. Fluent Interface 패턴을 활용하면 매우 읽기 쉬운 코드를 작성할 수 있다는 장점이 있지요. 해서 한 번 만들어 봤습니다. 이름하야 FluentTagBuilder!!



Implementing FluentTagBuilder

아시는 분은 아시겠지만 Fluent Interface의 핵심은 메서드가 자기 자신 혹은 관련된 다른 객체의 인스턴스를 리턴하는 것입니다. 즉, 메서드 호출 시 스스로를 계속 리턴하여 연속적으로 메서드를 호출할 수 있도록 구현하는 것이지요. 실질적으로 HTML 태그를 생성하는 기능은 TagBuilder가 이미 훌륭하게 구현하고 있으니 그냥 맡겨버리고 우리는 이 TagBuilder 클래스의 인스턴스를 계속해서 리턴하는 Wrapper 메서드만을 구현해주면 됩니다. 일단 코드를 좀 살펴볼까요?

public class FluentTagBuilder : TagBuilder
{
	public FluentTagBuilder(string tagName)
		: base(tagName)
	{ }

	public FluentTagBuilder Append(FluentTagBuilder innerTag)
	{
		base.InnerHtml += innerTag.ToString();
		return this;
	}

	public FluentTagBuilder AppendHtml(string html)
	{
		base.InnerHtml += html;
		return this;
	}

	public FluentTagBuilder AppendText(string html) {
		  base.InnerHtml += HttpUtility.HtmlEncode(html);
		  return this;
	}

	public FluentTagBuilder With(FluentTagBuilder tagBuilder)
	{
		base.InnerHtml = tagBuilder.ToString();
		return this;
	}

	public FluentTagBuilder With(string attribute, string attributeValue)
	{
		base.Attributes.Add(attribute, attributeValue);
		return this;
	}

	public FluentTagBuilder With(IDictionary attributes)
	{
		base.MergeAttributes(attributes);
		return this;
	}

	public FluentTagBuilder And(string attribute, string attributeValue)
	{
		base.Attributes.Add(attribute, attributeValue);
		return this;
	}

	public FluentTagBuilder AndIf(bool condition, string attribute, string attributeValue)
	{
		if (condition)
		{
			base.Attributes.Add(attribute, attributeValue);
		}
		return this;
	}
}
예제 코드에서 크게 어렵거나 이해가 안 가는 부분은 없으실거라 생각해요. 그러면 실제로 어떻게 사용하는지를 알아볼까요? 예전 포스트에서 구현했던 이미지를 포함하는 링크를 생성하는 ImageLink 메서드를 FluentTagBuilder 클래스를 이용하면 아래와 같이 구현할 수 있습니다.

public static MvcHtmlString ImageLink(
	this HtmlHelper helper, 
	string imageUrl, 
	string actionName, 
	string controllerName, 
	RouteValueDictionary routeValues, 
	IDictionary imgAttributes, 
	IDictionary htmlAttributes)
{
	UrlHelper urlHelper = new UrlHelper(helper.ViewContext.RequestContext);
	string resolvedImageUrl = urlHelper.Content(imageUrl);

	string url = UrlHelper.GenerateUrl(
		null, actionName, controllerName, routeValues, helper.RouteCollection,
		helper.ViewContext.RequestContext, true
	);
	
	FluentTagBuilder imageTagBuilder = new FluentTagBuilder("img")
		.With(imgAttributes)
		.With("src", resolvedImageUrl);

	return MvcHtmlString.Create(new FluentTagBuilder("a")
		.Append(imageTagBuilder)
		.With("href", url)
		.With(htmlAttributes)
		.ToString()
	);
}
18번 라인부터 27번 라인까지의 코드가 바로 FluentTagBuilder 클래스를 이용하여 태그를 생성하는 부분입니다. 예전 포스트와 비교할 때 코드가 확연히 줄어들었을 뿐 아니라 코드를 이해하기도 더 쉬워진 것 같죠?

아직 어딘가에 살아있음을 보여주기 위한 짧은 포스트는 여기서 마치고 이만 물러갑니다.
멋진 하루 보내시기 바랍니다!
Posted by 웹지니 트랙백 0 : 댓글 6
안녕하세요? 웹지니입니다.

주말 동안에 구슬이 횽아가 멋진 소식을 전했네요. 뭐 거두 절미하고 번역 들어갑니다. ㅋ 원문은 여기를 클릭하세요.
(제가 태우는 담배가 레종 - Raison - 인데 발음이 비슷해서 그런가 왠지 친근한 느낌이... 켈룩 -ㅅ-;;)

달랑 점심 시간 1시간을 투자해서 번역한 글이라 다소 투박하거나 원문과 일치하지 않을 수 있음을 양해해 주시기 바랍니다.

UPDATED: ASP.NET Web Pages라는 이름으로 Razor Syntax를 지원하는 VS 2010 애드온의 베타 1버전이 발표 되었어요. 아쉽게도 현재로서는 .NET 4만 지원하는 것 같습니다. 정식 릴리즈때는 3.5도 지원해주면 좋겠네요. 다운로드는 여기로!!

ASP.NET의 새로운 뷰 엔진 - Razor

우리 팀에서 진행하는 작업 중 하나는 ASP.NET을 위한 새로운 뷰 엔진 옵션을 구현하는 것이었습니다.

ASP.NET MVC는 "뷰 엔진"이라는 개념을 지원하는데 이 뷰 엔진이란 서로 다른 템플릿 문법을 구현하는 교체 가능한 모듈입니다. ASP.NET MVC가 사용하는 기본 뷰 엔진은 ASP.NET 웹 폼의 템플릿과 마찬가지로.aspx/.ascx/.master 파일들을 사용합니다. 그 외에도 ASP.NET MVC 뷰 엔진으로 SparkNHaml이 많이 사용되고 있습니다.

우리가 구현하는 새로운 뷰 엔진은 코드 중심의 템플릿 접근법을 사용한 HTML의 생성에 최적화되어 있습니다. 이 새로운 뷰 엔진의 코드명은 "Razor"로 조만간 첫 번째 공식 베타 버전을 릴리즈할 예정입니다.

디자인 목표

우리는 "Razor"를 구현하면서 다음과 같은 디자인 목표를 수립했습니다.
  • 간결하고 유기적이며 풍부한 표현: Razor는 파일을 작성하는데 필요한 문자와 입력 수를 최소화하여 빠르고 유기적으로 워크플로우를 코딩할 수 있습니다. 다른 템플릿 문법들과 달리 Razor는 HTML 코드 내에 서버 측 코드를 명시적으로 표시할 필요가 없습니다. Razor 파서는 여러분의 코드 내에서 서버 측 코드를 알아서 유추해 냅니다. 따라서 정말 간결하며 풍부한 표현이 가능한 문법을 제공할 수 있기 때문에 빠르고 재미있게 코드를 작성할 수 있습니다.
  • 쉬운 학습: Razor는 배우기 쉬우며 최소한의 개념으로 매우 생산적으로 코드를 작성할 수 있습니다. 여러분은 이미 알고 있는 언어와 HTML 기술을 그대로 활용할 수 있습니다.
  • 새로운 언어는 아닙니다: 우리는 의도적으로 Razor를 위한 새로운 언어를 구현하지 않았습니다. 대신 개발자들이 이미 친숙한 C#과 VB (혹은 다른 언어)를 Razor와 함께 사용할 수 있기를 원했으며 여러분이 사용하는 언어를 토대로 놀라운 HTML 생성 기능을 제공하는 템플릿 마크업 문법을 제공하고자 했습니다.
  • 모든 텍스트 편집기에서 동작: Razor는 특별한 도구를 필요로 하지 않으며 평범한 텍스트 편집기에서도 코드를 작성할 수 있습니다(심지에 메모장으로도 훌륭한 결과를 만들어 낼 수 있습니다).
  • 훌륭한 인텔리센스 지원: Razor가 별도의 도구나 코드 편집기를 필요로 하지는 않지만 Visual Studio에서는 완벽한 인텔리센스를 지원합니다. Visual Studio 2010과 Visual Web Developer 2010은 Razor를 위한 완벽한 기능의 코드 편집기를 제공합니다.
  • 단위 테스트: 새로운 뷰 엔진은 뷰에 대한 단위 테스트를 제공합니다(컨트롤러나 웹 서버 및 특별한 AppDomain 없이도 모든 종류의 단위 테스트 프로젝트를 호스팅할 수 있습니다).
우리는 지난 몇 개월 간 애플리케이션의 구현을 위해 Razor를 사용했으며 (.NET이 아닌 다른 분야의 웹 개발자들의 그룹을 포함한) 다양한 자원봉사자들과 여러 가지 사용성 연구를 하고 있습니다. 많이 사용해 보시고 다양한 피드백을 주시면 감사하겠습니다.

선택과 확장성

ASP.NET의 훌륭한 기능 중 하나는 대부분의 기능들이 교체 가능하다는 점입니다. 만일 여러분이 원하는대로 동작하지 않는 기능을 발견한다면 다른 모듈로 대체할 수 있습니다.

ASP.NET MVC의 다음 버전에서는 "추가 > 뷰" 대화 상자를 새로 구현하여 뷰 템플릿 파일을 생성할 때 여러분이 템플릿 문법을 선택할 수 있도록 개선될 예정입니다. 또한 시스템에 설치되어 사용 가능한 뷰 엔진 중 하나를 손쉽게 선택할 수 있는 기능을 제공하여 보다 자연스럽게 뷰를 선택할 수 있게 될 것입니다.


Razor는 ASP.NET MVC에 내장된 뷰 엔진 중 하나가 될 것입니다. 모든 도우미 메서드들과 프로그래밍 모델 기능들은 Razor와 .ASPX 뷰 엔진에서 동일하게 적용됩니다.

또한 하나의 애플리케이션이나 사이트에서 여러 개의 뷰 엔진을 사용하는 뷰 템플릿을 혼합해서 사용할 수 있습니다. 예를 들어 일부 뷰는 .aspx 파일을 사용하고 다른 일부는 .cshtml이나 vbhtml 파일(각각 C#과 VB.NET을 위한 Razor의 파일 확장자입니다)을 사용할 수 있으며 또 다른 일부는 Spark나 NHaml을 사용하여 구성할 수 있습니다. 또한 어떤 뷰 엔진을 사용하는 뷰 템플릿 내에서 다른 뷰 엔진을 위한 부분 뷰(Partial View)를 중첩할 수도 있습니다. 여러분은 완벽한 선택과 유연성의 혜택을 보게 될 것입니다.

Razor를 이용한 Hello World 예제

Razor를 이용하면 정적 HTML (혹은 다른 텍스트 콘텐츠)에 서버 코드를 추가하여 동적인 페이지를 구성할 수 있습니다. Razor의 핵심 디자인 목표 중 하나는 이러한 코딩 과정을 보다 유기적으로 만들어 최소한의 코딩으로 서버 코드를 HTML 마크업에 추가할 수 있도록 하는 것입니다.

그러면 아래와 같은 결과를 만들어 내는 간단한 "Hello, World" 예제를 만들어 보겠습니다.


.ASPX 코드로 구현된 뷰

이 "Hello World" 예제를 ASP.NET의 ASPX 마크업 문법으로 구현한다면 우리는 HTML 마크업 내의 코드를 표현하기 위해 아래와 같이 <%= %> 태그를 사용해야 합니다.


이 "Hello World" 예제를 잘 살펴보면 코드의 시작과 끝을 알리기 위해 5개의 문자(<%= %>)가 필요합니다. 이들 중 일부(특히 키보드의 가운데 상단에 있는 % 문자)는 입력하기가 쉽지 않습니다.

Razor를 이용한 뷰

Razor에서는 @ 문자를 이용하여 코드의 시작과 끝을 표시할 수 있습니다. <% %> 태그와 달리 Razor는 코드 블록을 명시적으로 닫을 필요가 없습니다.


Razor 파서는 코드 블록 내에서 사용된 C#/VB 코드를 의미적으로 파악하기 때문에 위에서와 같이 코드 블록을 명시적으로 닫을 필요가 없습니다. Razor는 이런 구문들을 자체 코드 블록으로 파악하고 우리를 대신해 묵시적으로 코드 블록을 닫아줍니다.

이렇게 간단한 "Hello World" 예제에서 우리는 벌써 12개의 키 입력을 아낄 수 있었습니다. @ 문자는 % 문자보다 더 쉽게 입력할 수 있기 때문에 전체적으로 빠르고 유기적으로 코드를 입력할 수 있습니다.

루프와 중첩된 HTML 예제

일련의 제품 목록(과 제품의 가격)을 나열하는 또 다른 예제를 살펴보도록 합시다.


.ASPX 코드로 구현된 뷰

ASP.NET의 .ASPX 마크업 문법을 사용한다면 각각의 제품 정보를 나열할 <ul> 목록과 <li> 항목을 동적으로 생성하기 위해 아래와 같이 코드를 작성해야 합니다.


Razor를 이용한 뷰

아래의 예제 코드는 위의 코드를 Razor로 구현한 코드입니다.


위의 예제에서 알 수 있듯이 foreach 구문은 @ 기호를 이용하며 코드 블록 내에서 HTML 콘텐츠를 생성합니다. Razor 파서는 코드 블록 내의 C# 코드의 의미를 알고 있으므로 <li> 콘텐츠는 foreach 구문 내에 존재해야 하며 반복되는 콘텐츠로 처리됩니다. 또한 마지막의 } 문자는 foreach 구문을 종료하기 위한 것이라는 점도 인식합니다.

Razor는 <li> 태그 내의 @p.Name과 @p.Price 구문이 서버 측 코드이며 루프 내에서 매번 실행해야 한다는 것을 인식할 만큼 충분히 똑똑합니다. 또한 HTML과 코드가 혼합된 상황에서 유추를 통해 @p.Name과 @p.Price 코드를 자동적으로 닫아야 한다는 점도 스스로 알아냅니다.

이처럼 코드 블록을 열거나 닫기 위한 기호 없이도 코드를 작성할 수 있다는 것은 전체적인 코딩 프로세스를 매우 유기적이며 빠르게 만들어 줍니다.

If 블록과 다중 구문

아래와 같은 몇 가지 예제 코드를 더 살펴보겠습니다.

if 구문

foreach 예제와 마찬가지로 if 구문(혹은 C#이나 VB외의 다른 언어 구조)에서도 명시적인 코드 블록의 시작과 끝을 지정하지 않고도 콘텐츠를 포함할 수 있습니다. 예제를 살펴보겠습니다.


다중 구문

또한 @{ code } 블록내에 여러 줄의 구문을 포함할 수도 있습니다.


위의 예제에서 알 수 있듯이 변수들은 여러 개의 서버 코드 블록으로 확장될 수 있습니다. 예를 들어 message 변수는  여러 구문을 포함하는 @{ } 블록에 선언되었지만 @message 코드 블록에서도 사용됩니다. 이는 개념적으로 .aspx 마크업 파일의 <% %>와 <%= %> 구문과 동일합니다.

다중 토큰 구문

@( ) 구문은 여러 개의 토큰을 가질 수 있습니다. 예를 들어 문자열을 조합하는 앞서 예제의 코드는 다음과 같이 @( ) 코드 블록으로 재작성할 수 있습니다.


콘텐츠와 코드의 통합

Razor 파서는 여러분이 명시적으로 해왔던 작업들을 간소화할 수 있는 여러 가지 내장 기능들을 제공합니다.

HTML에서 메일 주소에 사용된 @와 충돌은 없나요?

Razor의 언어 파서는 템플릿에 사용된 @ 문자가 코드인지 정적 콘텐츠인지를 구분할 수 있습니다. 예를 들어 아래의 @ 문자는 메일 주소의 일부로 사용되고 있습니다.


이 파일을 파싱할 때 Razor 파서는 @ 문자 오른편의 콘텐츠가 C# 코드인지(CSHTML 파일의 경우) 아니면 VB 코드인지(VBHTML 파일의 경우) 아니면 그냥 정적인 콘텐츠인지를 판단합니다. 위의 코드는 아래와 같은 HTML 코드를 만들어 냅니다(메일 주소는 정적인 콘텐츠로 표현되며 @DateTime.Now는 서버 코드로 표현되고 있습니다).


콘텐츠가 코드로도 사용될 수 있는 경우(그리고 여러분은 이 콘텐츠를 콘텐츠로 처리하고 싶은 경우)에는 @@을 입력하여 명시적으로 @문자로 취급하도록 할 수도 있습니다.

중첩된 코드의 식별

if/else 구문이나 foreach 등 코드 블록 구문을 이용하여 HTML 콘텐츠를 중첩할 때 콘텐츠 블록의 시작을 명시적으로 판별할 수 있도록 HTML이나 XML 요소로 내부의 콘텐츠를 감싸야 합니다.

예를 들어 아래의 코드는 (서버 코드를 포함한) 다중 콘텐츠 블록을 <SPAN> 요소로 감싸고 있습니다.


이 코드는 다음과 같이 <SPAN> 태그를 그대로 사용하는 HTML 콘텐츠를 만들어 냅니다.


중첩된 콘텐츠를 <text> 블록으로 감싸면 콘텐츠를 감싸는 태그를 생략하고 나머지 콘텐츠만 렌더링할 수 있습니다.


위의 코드는 아래와 같이 콘텐츠를 감싸는 태그를 생략한 결과를 만들어 냅니다.


HTML 인코딩

기본적으로 @ 블록에 의해 출력되는 콘텐츠는 XSS 공격을 차단하기 위해 자동적으로 HTML 인코딩된 값으로 출력됩니다.

레이아웃/마스터 페이지 시나리오 - 기초

웹 사이트나 웹 애플리케이션에서 일관된 UI를 제공하는 것은 매우 중요한 일입니다. ASP.NET 2.0은 "마스터 페이지" 개념을 도입하여 .aspx 기반 페이지와 템플릿에 일관된 UI를 구현할 수 있는 기능을 제공합니다. Razor 또한 "레이아웃 페이지"를 제공하기 때문에 일반적인 사이트의 템플릿을 정의하고 이를 상속하여 사이트 내의 뷰와 페이지가 일관된 UI를 구현할 수 있습니다.

간단한 레이아웃 예제

아래의 예제는 SyteLayout.cshtml이라는 파일에 작성한 간단한 레이아웃 페이지입니다. 이 페이지에는 정적인 HTML 콘텐츠는 물론 동적인 서버 코드도 작성할 수 있습니다. 또한 "RenderBody()" 도우미 메서드를 호출해서 요청된 URL에 대한 콘텐츠를 채워넣을 수 있습니다.


그런 후 요청된 페이지를 위한 페이지 본문을 구성하는 콘텐츠/코드를 가지고 있는 "Home.cshtml" 뷰 템플릿을 생성하고 아래와 같이 콘텐츠를 작성하면 됩니다.


위의 코드에서 보듯이 LayoutPage 속성에 Home.cshtml 파일을 지정하였습니다. 이렇게 하면 이 뷰에 대한 레이아웃 페이지로 SiteLayout.cshtml 파일이 사용됩니다. 혹은 ASP.NET MVC 컨트롤러가 Home.cshtml 파일을 뷰 템플릿으로 호출할 때 레이아웃 파일을 지정하거나 아니면 사이트를 위한 기본 레이아웃을 설정할 수도 있습니다(이 경우 프로젝트 내의 모든 뷰 템플릿이 자동적으로 지정된 하나의 레이아웃 페이지를 사용하게 됩니다).

Home.cshtml 파일을 뷰 템플릿으로 렌더링하면 레이아웃과 자식 페이지의 콘텐츠가 하나로 합쳐져 아래와 같은 결과를 만들어 냅니다.


간결하고 명확하며 풍부한 표현이 가능한 코드

위의 예제에서 알 수 있듯이 레이아웃을 정의하고 사용하면 뷰나 페이지를 간결한 코드로 명확하게 구현할 수 있습니다. SiteLayout.cshtml 파일과 Home.cshtml 파일의 코드는 모든 콘텐츠를 두 개의 .cshtml 파일로 구성합니다. 즉, 추가적인 설정이나 태그도 필요치 않으며 <%@Page %> 디렉티브도 필요하지 않으며 다른 어떤 태그나 값을 설정할 속성도 필요치 않습니다.

우리는 여러분이 간력하고 쉬우며 유기적인 코드를 작성할 수 있도록 노력하고 있습니다. 또한 텍스트 편집기 만으로 모든 작업이 손쉽게 가능하도록 구현하고자 합니다. 여기에는 어떠한 코드 생성이나 인텔리센스도 필요치 않습니다.

레이아웃/마스터 페이지 시나리오 - 섹션 덮어쓰기

레이아웃 페이지에는 또 다른 섹션을 정의하여 해당 레이아웃을 사용하는 뷰 템플릿이 사용자 정의 콘텐츠를 채워넣을 수 있는 기능을 제공합니다. 이 기능을 활용하면 단편화된 콘텐츠 영역을 레이아웃 페이지에 채워넣어 사이트의 레이아웃에 대한 유연성을 확보할 수 있습니다.

예를 들어 SiteLayout.cshtml 파일에 두 개의 섹션을 추가하여 사이트가 제공하는 뷰 템플릿을 어디에 채워넣을지를 결정할 수 있습니다. 새로 추가된 섹션들은 "menu"와 "footer"라고 명명하고 RenderSection() 메서드를 호출할 때 optional=true 매개 변수를 지정하여 해당 섹션이 선택적인(즉, 반드시 필요하지는 않은) 부분이라고 표시할 수 있습니다(이 기능은 제가 예전에 포스팅했던 C#의 새로운 선택적 매개 변수 문법을 통해 구현됩니다).


새로 추가된 두 섹션은 선택적인 섹션으로 표시하였기 때문에 이 두 섹션을 Home.cshtml 파일에 정의할 필요는 없습니다. 따라서 이 사이트는 두 개의 섹션에 채워질 뷰 템플릿이 존재하지 않더라도 잘 동작합니다.

그러면 Home.cshtml 파일로 돌아가 Menu와 Footer 섹션을 정의해 보겠습니다. 아래의 코드는 Home.cshtml 파일의 모든 콘텐츠를 구현한 코드입니다. 별도로 다른 파일에 구현할 필요는 없습니다. 한 가지 변경된 것은 LayoutPage 속성 설정을 전체 사이트를 위한 설정으로 변경했다는 점입니다.


Menu와 Footer 섹션은 파일 내의 @section {}  블록을 통해 재정의되었습니다. 주 콘텐츠와 본문 콘텐츠를 구별할 필요 없이 단순히 인라인 코드로 정의하기만 하면 됩니다 (이렇게 함으로써 키 입력을 줄일 수 있음은 물론 이미 존재하는 페이지의 코드를 변경하지 않고도 레이아웃 페이지에 새로운 섹션을 손쉽게 추가할 수 있습니다).

Home.cshtml 파일을 다시 렌더링해보면 레이아웃과 서브 페이지들의 콘텐츠가 결합되어 새로 정의한 두 개의 섹션이 나타나게 될 것입니다. 클라이언트에 전송되는 HTML 코드는 다음과 같습니다.


캡슐화와 HTML 도우미 클래스의 재사용

지금까지 레이아웃 페이지를 이용해 사이트 전반의 UI를 구성하는 방법에 대해 알아보았습니다. 이제는 HTML 도우미 클래스를 재사용하여 HTML 생성 기능을 전체 사이트 - 는 물론 서로 다른 여러 사이트에서도 - 에서 재사용하는 방법에 대해 알아보겠습니다.

코드 기반 HTML 도우미

ASP.NET MVC에서는 코드 블록 내에서 호출이 가능하며 HTML 코드를 생성하는 HTML 도우미 메서드라는 개념을 제공합니다. 이들은 순수하게 코드로 (특히 확장 메서드 형태로) 구현되어 있습니다. ASP.NET MVC에 내장된 모든 HTML 확장 메서드는(여러분이 직접 구현하든 다른 사람이 구현했든) Razor 뷰 엔진에서도 그대로 활용할 수 있습니다.


선언적 HTML 도우미

코드만으로 구성된 클래스를 이용하여 HTML 출력물을 생성하는 방법은 잘 동작하기는 하지만 이상적인 형태는 아닙니다.

우리가 Razor를 통해 제공하고자 했던 기능 중 하나는 재사용 가능한 HTML 도우미를 보다 선언적인 방법으로 사용할 수 있도록 하는 것이었습니다. 아래와 같이 @helper {} 문법을 이용하면 재사용 가능한 도우미를 정의할 수 있습니다.


여러분은 Views\Helpers\ 디렉토리에 세 개의 .cshtml 파일을 구현하고 이를 사이트 내의 다른 어떤 뷰나 페이지에서도 재사용할 수 있습니다(특별히 다른 단계를 밟을 필요가 없습니다).


위의 예제에서 보듯이 ProductListing() 도우미 메서드는 매개 변수와 인자를 정의할 수 있습니다. 따라서 여러분은 원하는 어떤 것이든 매개 변수로 전달할 수 있습니다(또한 선택적 매개 변수와 Nullable 타입, 제네릭 등 기존의 언어적 특성들을 그대로 활용할 수 있습니다). 또한 Visual Studio를 사용한다면 이 메서드들을 디버깅할 수도 있습니다.

참고: @helper 구문은 Razor의 첫 번째 베타에는 포함되지 않을 예정이지만 이후의 버전에는 포함될 것입니다. 코드 기반 도우미는 첫 번째 베타에서 정상적으로 동작할 것입니다.

인라인 템플릿을 매개 변수로 전달하기

Razor의 또 다른 유용한 (그리고 정말 강력한) 기능은 "인라인 템플릿"을 도우미 메서드의 매개 변수로 전달할 수 있다는 점입니다. 이러한 "인라인 템플릿"은 HTML과 코드를 모두 포함할 수 있으며 필요에 따라 도우미 메서드에서 호출할 수도 있습니다.

아래의 예제는 DataGrid를 렌더링하는 "Grid" HTML 도우미 메서드를 사용하는 방법을 보여줍니다.


위의 예제에서 호출하는 Grid.Render() 메서드는 C# 구문입니다. 우리는 C#의 새로운 명명된 매개 변수 문법을 이용하여 강력하게 형식화된 인수를 Grid.Render 메서드에 전달합니다. 따라서 위의 구문은 컴파일 시점에 완벽한 코드 완성 및 인텔리센스의 지원을 받을 수 있습니다.

우리가 컬럼을 정의할 때 전달한 "format" 매개 변수가 바로 "인라인 템플릿"이며 보시다시피 HTML과 코드를 모두 포함하여 출력될 데이터의 형식을 재정의할 수 있습니다. 이 기능의 강력한 점은 Grid 도우미가 인라인 템플릿을 마치 대리자 메서드처럼 호출할 수 있으며 필요에 따라 몇 번이고 호출할 수 있다는 점입니다. 예제에서는 Grid의 각 행을 렌더링할 때 매번 인라인 템플릿이 호출되며 데이터를 표시하는 역할을 담당하는 템플릿에서 참조할 수 있는 "item" 변수를 전달해 줍니다.

이 방법을 이용하면 보다 풍부한 HTML 도우미 메서드를 개발할 수 있습니다. 이러한 도우미 메서드는 코드 접근 방식(현재 확장 메서드를 이용하는 방식) 뿐만 아니라 @helper {} 구문을 이용하는 선언적 방식으로도 구현이 가능합니다.

Visual Studio 지원

앞서 설명했듯이 Razor의 구현 목표 중 하나는 최소한의 입력이며 기본적인 텍스트 편집기에서도 손쉽게 편집이 가능하도록 하는 것입니다. 이를 위해 우리는 문법 자체를 간결하며 명확하게 유지했습니다.

또한 우리는 Razor를 디자인함에 있어 Visual Studio의 풍부한 코드 편집 기능을 활용할 수 있도록 구현했습니다. Razor 기반 파일에서도 HTML과 자바스크립트, C#/VB 코드에 대한 인텔리센스 기능을 완벽하게 지원합니다.


위의 그림에서 보듯이 foreach 구문 내에 포함된 코드에서 @p를 통해 참조하는 Product 객체에 대해 인텔리센스 기능이 동작하는 것을 볼 수 있습니다. 또한 솔루션 탐색기를 보면 Views 폴더가 .aspx 뷰 템플릿과 .cshtml 뷰 템플릿을 모두 가지고 있음도 알 수 있습니다. 여러분은 하나의 애플리케이션에서 여러 가지 뷰 엔진을 사용할 수 있으며 이로 인해 여러분에게 적합한 뷰 엔진을 손쉽게 선택할 수 있습니다.

요약

우리는 "Razor"가 코드 중심의 템플릿을 위한 새로운 뷰 엔진이라고 생각합니다. Razor를 이용하면 빠르고 풍부하며 재미있는 코딩을 즐길 수 있습니다. 문법은 매우 간결하며 입력을 최소로 줄여주는 동시에 마크업과 코드의 가독성을 전체적으로 향상시켜줍니다. Razor는 ASP.NET MVC의 다음 버전에서는 내장 뷰 엔진 중 하나로 제공될 것입니다. 또한 개별적인 .cshtml/.vbhtml 파일을 애플리케이션에 추가하여 개별적인 페이지로 실행시킬 수도 있습니다. 이를 통해 ASP.NET 웹 애플리케이션에서도 Razor를 활용할 수 있게 됩니다.

우리가 지난 몇 달간 고생해서 만든 Razor를 많은 개발자들이 사용해보고 다양한 피드백을 제공해 주기를 기대합니다. 조만간 첫 번째 공개 베타를 공개할 예정이므로 많은 피드백을 제공해 주시기를 바랍니다.

이 글이 도움이 되었기를 바라며

Scott

P.S: 블로그 외에도 저는 트위터를 통해 업데이트 소식과 관련 링크를 전달하고 있습니다. twitter.com/scottgu를 통해 저를 팔로우 해주세요.


Posted by 웹지니 트랙백 0 : 댓글 8
안녕하세요? 웹지니입니다.

오늘 저는 삼성동 코엑스 인터컨티넨탈 호텔에서 진행되었던 ReMIX 10 행사에서 "ASP.NET MVC 2 확장성 경험하기"라는 주제로 세션을 발표했어요.



많은 분들이 참석해 주셨는데 컨티션 난조(쿨럭 -ㅅ-;;)와 충분하지 못한 준비로 귀한 시간 내주신 분들께 실망을 드린 것은 아닌지 많은 아쉬움과 후회가 남는 세션이었습니다.

세션 마지막에 예제 코드를 요구하신 분이 계셔서 일단 블로그를 통해 공유하고자 합니다. 예제 코드가 필요하신 분들은 아래의 SkyDrive 링크를 참고해 주세요.

세션 예제 자료 다운로드: [ReMIX10.zip (355KB)]

지루하고 재미없는 세션을 끝까지 참고 경청해 주신 많은 분들께 감사의 말씀을 드립니다.


Posted by 웹지니 트랙백 0 : 댓글 0

안녕하세요 금연 중인 웹지니입니다. 쿨럭 -ㅅ-;;

어느새 2010년 5월입니다 5월! 벌써 절반 가까이 훌떡 지나갔어요. 다들 년초에 생각하셨던 계획들을 차근차근 이루어 가시는지요?

제게는 이번 5월과 6월에 갑작스레 오프라인 세미나가 텍사스 소떼마냥 들이닥쳤어요. 더불어 회사 업무까지 쓰나미처럼  몰아치니 정신 못차리겠습니다. 쩝!
바쁜게 좋은거라지만 이제 바쁘단 말 좀 그만하고 싶어효 ㅠㅠ

1. 5월 12일 MSDN 세미나 

아마도 (당연하겠지만) 한국 마이크로소프트 5층에서 하게 되겠죠? ㅎㅎ 
MSDN 세미나의 주제는 초급자를 위한 ASP.NET MVC 2입니다. 아직 ASP.NET MVC에 생소한 ASP.NET 개발자들을 위한 엔트리 레벨의 세션이에요.

[UPDATED: 안타깝게도(?) 이 세미나는 9월 이후로 연기되었습니다. 아직 두 개가 남아있으니 화이팅입니다!]

2. 5월 28일 제2회 닷넷 개발자 컨퍼런스 

제2회 닷넷 개발자 컨퍼런스에도 웹지니가 출동합니다. 역시 ASP.NET MVC 2가 주제이며 ASP.NET MVC를 기반으로 엔터프라이즈 웹 개발을 위한 기본 개념들에 대해 소개할 예정이에요.

3. 6월 1일 REMIX 10 

모두들 알고 계실 REMIX 10에서는 ASP.NET MVC 2의 확장성에 대한 이야기를 해볼까 합니다. 기존의 ASP.NET과는 달리 ASP.NET MVC는 많은 부분을 개발자가 스스로 커스터마이징 할 수 있어요. 

이 세션에서는 ASP.NET MVC가 제공하는 기본 기능을 확장하여 여러분의 프로젝트의 목적에 맞도록 커스터마이징하는 몇 가지 기법에 대해 소개할 예정이에요.

어찌하다보니 주제가 전부 ASP.NET MVC네요. 할 줄 아는게 이것 밖에 없는 걸까요 -ㅅ-;; 

저로서는 오프라인 무대에 서는 거의 마지막 기회가 될 것 같아요. 이제 늙다리가 다 되어서 (ㅠㅠ) 앞으로는 오프라인으로 찾아뵐 수 있는 시간이 많지 않을 것 같네요. 

후회없는 세션이 되도록 열심히 준비하겠습니다.

행사장에서 뵈어영~

Posted by 웹지니 트랙백 0 : 댓글 0

안녕하세요? 웹지니입니다.

2010년 새해가 시작된지도 벌써 1주일이나 지났네요. 다들 건강하시죠?
전 요즘 감기가 걸려서 개고생을 하고 있습니다. 사실 아프려면 크게 아파야 좀 쉬기도 하고 그럴텐데 지금처럼 콧물만 졸졸 흐르면 괜히 지저분하기만 하고 일하기만 더 힘들고 그러네요.

2010년 새해에도 웹지니는 여전히 ASP.NET을 이용한 크로스 플랫폼 웹 애플리케이션을 개발하고 있습니다.
물론 Primary Target은 Windows 플랫폼이지만 리눅스/유닉스 계열과 Mac OS 등을 탑재한 시스템에도 저희 팀에서 구현한 애플리케이션이 동작해야 한다는 (그것도 별도의 코드 수정이나 재컴파일 없이 -ㅅ-;;) 다소 난해한 요구 사항과 맞닥뜨린 상황입니다.
해서 Windows 이외의 플랫폼에서는 마이크로소프트가 지원하고 노벨(Novell)이 프로젝트를 주도하는 모노(Mono)를 사용하게 된 것이지요.

사실 10년 간 Windows 플랫폼만 사용해 온 웹지니에게 리눅스 플랫폼은 매우 생소한 녀석이었기에 이런 저런 고생을 좀 했습니다.
특히 Windows 플랫폼에서는 그냥 “다음” 버튼만 누르면 간단히 해결되던 소프트웨어의 설치가 가장 난제였었지요.
해서 이번 포스트는 크게 두 가지 목적으로 작성되었습니다.

1. Ubuntu 리눅스에서 소스 컴파일을 통해 모노 프레임워크를 설치하는 방법을 잊어버리지 않기 위해서
2. 혹시 다른 .NET/모노 개발자에게 도움이 될까봐

그럼 시작해 볼까요?

1. Installing MonoDevelop 2.0 on Ubuntu 9.10

일단 이 포스트는 Ubuntu 리눅스 9.10 버전을 기준으로 하며 여러분이 이미 가상 머신 혹은 실제 물리적 머신에 Ubuntu 9.10을 설치한 상태라고 가정합니다. 사실 Ubuntu 9.10에는 Mono 2.4.2.3 버전이 이미 설치되어 있으며 모노 개발을 위한 IDE인 MonoDevelop 2.0이 미리 패키징되어 있어 손쉽게 설치가 가능합니다.

Mono 2.4.2.3과 MonoDevelop 2.0만으로도 얼마든지 리눅스 플랫폼에서 .NET 개발이 가능합니다. 게다가 설치도 참 쉽죠~잉. 리눅스 터미널에서 다음과 같은 커맨드만 실행하면 됩니다.

  1: sudo apt-get install monodevelop monodevelop-database

그런 후 리눅스 사용자 계정의 비밀 번호를 입력해 주면 간단히 MonoDevelop 2.0을 설치할 수 있습니다. 그러나 최신 버전은 Mono 2.6.1과 MonoDevelop 2.2이며 늘 최신 버전에 목말라 하는 웹지니는 미리 패키징 된 개발 도구들 대신 최신 소스를 다운로드하여 직접 컴파일 후 설치하는 과정에 도전해 보기로 한 것이지요.

2. Building and Installing Mono 2.6.1 on Ubuntu 9.10

Mono 2.6.1을 설치는 과정은 제 개인적으로는 절대 쉽지 않았습니다. 우선 Ubuntu에 필요한 라이브러리들이 모두 설치되어 있어야 하기에 어떤 것들을 먼저 설치해야 하는지 알아내는 것도 쉽지 않았지요. 그러면 이제 필요한 라이브러리를 먼저 설치해볼까요? 터미널에서 다음과 같은 커맨드를 실행합니다.

  1: sudo apt-get remove mono-common
  2: 
  3: sudo apt-get install build-essential pkg-config bison gettext libglib2.0-dev libcairo2-dev libungif4-dev libjpeg62-dev libtiff4-dev 
  4: 
  5: wget http://ftp.novell.com/pub/mono/sources/libgdiplus/libgdiplus-2.6.tar.bz2
  6: tar xvjf libgdiplus-2.6.tar.bz2
  7: cd libgdiplus-2.6
  8: ./configure && make && sudo make install

자 먼저 1번 라인의 커맨드를 이용하여 이전 버전의 mono-common 라이브러리를 삭제한 후 3번 라인과 같이 필요한 라이브러리들을 모두 설치합니다. 그런 후 마지막으로 libgdiplus 라이브러리를 설치해야 하는데 이 라이브러리는 Mono 2.6.1에 맞춘 최신 버전이 제공되므로 5번 라인과 같이 FTP를 통해 먼저 다운로드 한 후 6번 라인의 커맨드로 압축을 해제하고 8번 라인과 같이 설치해 줍니다.

여기까지 설치했으면 이제 드디어 Mono 2.6.1을 설치할 차례입니다. 다음의 커맨드를 차례로 실행해 보겠습니다.

  1: wget http://ftp.novell.com/pub/mono/sources/mono/mono-2.6.1.tar.bz2
  2: tar xvjf mono-2.6.1.tar.bz2
  3: cd mono2.6.1
  4: ./configure && make && sudo make install

차례대로 설명을 드리자면 1번 라인의 커맨드로 Mono 2.6.1의 소스 코드를 다운로드 한 후 2번 라인의 커맨드로 압축을 해제하고 4번 라인의 커맨드로 설치를 합니다. 설치 과정은 생각보다 오래 걸립니다 (흡연자라면 담배 한 대 때리고 오심이... 쿨럭). 알 수 없는 내용들이 터미널에 가득 출력이 되고 가끔 .NET 컴파일 경고가 보이기도 하지만 일단 불안해 하지 말고 넘어갑니다. 설치 과정이 모두 완료되면 다음과 같이 시스템에 설치된 모노의 버전을 확인할 수 있습니다.

  1: mono --version
  2: 
  3: Mono JIT compiler version 2.6.1 (tarball Thu Jan 7 16:44:43 KST 2010)
  4: Copyright (C) 2002-2008 Novell, Inc and Contributors. www.mono-project.com
  5: 	TLS:		__thread
  6: 	GC:		Included Boehm (with typed GC and Parallel Mark)
  7: 	SIGSEGV:	altstack
  8: 	Architecture:	x86
  9: 	Disabled:	none
 10: 

1번 라인과 같이 mono 실행 파일에 --version 옵션을 주면 3라인 이후의 정보가 출력됩니다. 3번 라인의 버전이 2.6.1로 표시되면 올바르게 설치가 완료된 것입니다.

3. Building and Installing MonoDevelop 2.2 on Ubuntu 9.10

Mono 2.6.1을 설치했으면 이제 모노 개발을 위한 IDE인 MonoDevelop의 최신 버전인 2.2 버전을 설치해 보겠습니다. 안타깝게도 MonoDevelop 2.2는 Windows 플랫폼을 비롯하여 Mac OS X와 노벨의 OpenSUSE 리눅스에서의 설치를 위한 패키지를 지원하지만 Ubuntu 리눅스를 위한 패키지는 지원하지 않습니다. 해서 번거롭지만 소스 컴파일을 통해 설치해야 하는 것이지요. MonoDevelop을 설치하기 이전에 먼저 GTK# 및 Gnome#이라는 라이브러리를 설치해야 합니다. 터미널에서 다음과 같이 커맨드를 실행합니다.

  1: wget http://ftp.novell.com/pub/mono/sources/gtk-sharp212/gtk-sharp-2.12.9.tar.bz2
  2: wget http://ftp.novell.com/pub/mono/sources/gnome-sharp2/gnome-sharp-2.24.1.tar.bz2
  3: wget http://ftp.novell.com/pub/mono/sources/gnome-desktop-sharp2/gnome-desktop-sharp-2.24.0.tar.bz2
  4: 
  5: tar xvjf gtk-sharp-2.12.9.tar.bz2
  6: tar xvjf gnome-sharp-2.24.1.tar.bz2
  7: tar xvjf gnome-desktop-sharp-2.24.0.tar.bz2
  8: 
  9: cd gtk-sharp-2.12.9
 10: ./configure && make && sudo make install
 11: 
 12: cd ../gnome-sharp-2.24.1
 13: ./configure && make && sudo make install
 14: 
 15: cd ../gnome-desktop-sharp-2.24.0
 16: ./configure && make && sudo make install

먼저 위와 같이 세 개의 GTK#및 Gnome# 라이브러리를 다운로드 한 후 각자 압축을 해제하고 각각의 라이브러리가 압축 해제된 폴더에서 설치를 실행합니다. 설치가 완료되면 이제 다음의 커맨드를 이용하여 MonoDevelop 2.2의 소스 코드를 다운로드 하고 설치합니다.

  1: wget http://ftp.novell.com/pub/mono/sources/monodevelop/monodevelop-2.2.tar.bz2
  2: 
  3: tar xvjf monodevelop-2.2.tar.bz2
  4: 
  5: cd monodevelop-2.2
  6: ./configure && make && sudo make install

위의 커맨드를 실행하고 설치가 완료될 때까지 기다립니다. 설치가 완료되면 아래 그림과 같이 Ubuntu 데스크톱의 [Applications] 메뉴에 [Programming > MonoDevelop] 메뉴가 생성됩니다.

MonoDevelop 메뉴를 클릭하면 아래 그림과 같이 MonoDevelop 2.2가 실행됩니다. 새 프로젝트 대화 상자에서 볼 수 있듯이 ASP.NET MVC 1.0 프로젝트과 Silverlight의 리눅스 버전인 Moonlight 애플리케이션 개발을 위한 템플릿도 지원되네요. 훗~

이상으로 Mono 2.6.1과 MonoDevelop 2.2를 설치해 보았습니다. 사실 Mono 2.6.1은 아직 .NET 프레임워크 3.5의 모든 기능을 구현하고 있지는 않습니다만 .NET 2.0의 클래스 라이브러리는 거의 대부분 구현하고 있어 실제 프로젝트를 진행하기에 큰 무리는 없어 보입니다.

앞으로 .NET/Mono 프레임워크로 크로스 플랫폼 웹 애플리케이션을 구현하면서 알게 되는 유용한 정보들도 함께 공유하도록 할게요.
허접한 설치 경험담은 여기서 끝~

Posted by 웹지니 트랙백 0 : 댓글 0

안녕하세요? 웹지니입니다.
오늘 드디어 Professional ASP.NET MVC 1.0 번역서의 최종 완성된 PDF 문서를 전달받았습니다. 약속대로 제1장을 공개해 드려야죠?

표지 이미지 아래의 링크를 클릭하시면 제1장의 PDF 파일을 다운로드 하실 수 있습니다.
아... 근데 블로그에 온통 책 자랑으로 도배질을 하고 있네요...

다운로드: Professional_ASPNET_MVC_ch1.PDF (12.26 mb)

많은 분들께 도움이 되기를 바랍니다 =)

Posted by 웹지니 트랙백 0 : 댓글 0

안녕하세요? 웹지니입니다.

어제 표지를 공개했는데 오늘 벌써 예약 판매가 시작되었어요. 예약 판매 때 구입하시면 10%의 할인과 10%의 적립이 지원되는 것 같습니다. 온라인 서점마다 같은 할인률이 적용되는걸까요?

예전에 약속드린 편집이 완료된 이후의 번역서 제1장의 공개도 얼마 남지 않은 것 같네요. PDF가 입수되는 대로 곧바로 공개하도록 하겠습니다.

책의 출간이 가까워오면, 그리고 책을 출간한 후에도 항상 아쉬움만 남는 것 같아요.

조금 더 매끄럽게 글을 쓰지 못했던 것도, 조금 더 독자의 입장을 배려하지 못한 것도, 조금 더 여유를 가지고 작업을 하지 못한 것도 모두 아쉽기만 합니다. 번역서의 경우에는 (다소 어처구니 없게 들릴 수도 있겠지만) 짧은 영어로 감히 번역 작업을 하고 있는 제 자신이 과연 정신 똑바로 박힌 인간인지를 의심하곤 하지요. 그저 책을 구입하시는 분들께 작은 도움이 될 수 있기를 바랄 뿐입니다.

그럼 이쯤에서 깜짝 이벤트 하나 할까여? ㅎㅎ
책이 출간되는 기념으로 이 포스트에 댓글 일빠 하시는 분께 저자 증정본으로 제공되는 도서 1권을 선물로 보내드릴게요. 2등부터는 아쉽지만...

img1

힛~ 그럼 이만 자랑질 끗~

Posted by 웹지니 트랙백 0 : 댓글 1

안녕하세요? 웹지니입니다.
많은 분들이 궁금해 하시는(!!) Professional ASP.NET MVC 1.0 번역서 소식을 전해드리려고 해요.

어제 완성된 표지를 전달받았습니다. Wrox의 여느 번역서와 마찬가지로 표지는 원서와 동일하게(ㅠㅠ) 만들어졌습니다. Wrox는 표지를 절대 바꾸지 못하도록 한다고 하네요.
책 표지의 ASP.NET 개발팀 멤바 4인방의 포스는 정말이지 ㅎㄷㄷ이군요. 제 사진도 하나 살짝 꽂아 넣고 싶은 마음이 굴뚝같습니다 그려 쩝…

img1

태오 형님의 Pro ASP.NET MVC (Apress, 2009) 번역서도 어느 새 예약 판매를 진행하고 있더라고요. 비슷한 시기에 두 권의 ASP.NET MVC 번역서가 출간되는 것은 MVC 마니아가 되어버린 저로서는 정말이지 기분 좋은 소식입니다. 태오 형님, 한 권씩 교환 어떠세여? 힛~

Posted by 웹지니 트랙백 0 : 댓글 0

안녕하세요? 웹지니입니다.
지난 포스트에서 말씀드렸듯이 10월 2일을 기점으로 ASP.NET MVC 2.0의 Preview 2 버전이 릴리즈 되었습니다. 이번 Preview 2 버전에서는 약간의 새로운 기능들이 추가되었는데요. 이번 포스트에서는 클라이언트 측 유효성 검사 기능에 대해 알아보도록 하겠습니다.

예제 다운로드:  MvcApplication1.zip (302.64 kb)

Valdiation with DataAnnotations API

ASP.NET MVC 2 Preview 1에서는 .NET 3.5 SP1에서 새롭게 선보인 DataAnnotations API를 이용한 유효성 검사 기능이 추가되어 있습니다. 예전에 올렸던 ASP.NET MVC 2 Preview 1 릴리즈에 대한 번역 포스트에서 이미 한 번 소개해 드렸었지요. 이 방식의 문제점은 데이터 유효성 검사를 위해 매번 폼을 POST 해야 한다는 점이었습니다. 즉 유효성 검사가 무조건 서버 측 코드에 의해 실행되기 때문에 폼이 Submit 되기 전에는 입력 요소에 대한 유효성 검사를 할 수 없다는 뜻입니다. 사실 말이야 바른 말이지 요새 누가 이런 식으로 구현하겠어요?

Client-Side Valdiation in ASP.NET MVC 2 Preview 2

해서 ASP.NET MVC 2 Preview 2에서는 Client-Side Validation API를 제공하여 이와 같은 불편을 해소하고 있습니다. 거두절미하고 예제를 통해 한 번 살펴볼까요? 우선 아래와 같이 모델 객체와 모델 객체에 대한 메타데이터 클래스가 존재한다고 가정해 보겠습니다.

코드 1: Profile 클래스와 ProfileMetadata 클래스

  1: namespace MvcApplication1.Models
  2: {
  3: 	[MetadataType(typeof(ProfileMetadata))]
  4: 	public class Profile
  5: 	{
  6: 		public string Name { get; set; }
  7: 		public string Email { get; set; }
  8: 	}
  9: 
 10: 	public class ProfileMetadata
 11: 	{
 12: 		[Required(ErrorMessage="이름을 입력해 주세요.")]
 13: 		public object Name;
 14: 
 15: 		[Required(ErrorMessage="국가를 입력해 주세요.")]
 16: 		public object Email;
 17: 	}
 18: }
 19: 

그리고 이 객체를 생성하는 HomeController 클래스의 Create 액션 메서드는 다음과 같이 구현되어 있습니다.

코드 2: HomeController.Create 액션 메서드

  1: [HandleError]
  2: public class HomeController : Controller
  3: {
  4: 	public ActionResult Create()
  5: 	{
  6: 		return View();
  7: 	}
  8: 
  9: 	[HttpPost]
 10: 	public ActionResult Create(FormCollection forms)
 11: 	{
 12: 		Profile profile = new Profile();
 13: 		this.TryUpdateModel<Profile>(profile);
 14: 		return View();
 15: 	}
 16: }

마지막으로 /Home/Create 뷰 페이지는 다음과 같이 구현되어 있습니다.

코드 3: /Home/Create 뷰 페이지의 소스 코드

  1: <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
  2: 
  3:     <h2>Create</h2>
  4: 
  5:     <%= Html.ValidationSummary("Create was unsuccessful. Please correct the errors and try again.") %>
  6: 
  7:     <% using (Html.BeginForm()) {%>
  8: 
  9:         <fieldset>
 10:             <legend>Fields</legend>
 11:             <p>
 12:                 <label for="Name">Name:</label>
 13:                 <%= Html.EditorFor(p => p.Name) %>
 14:                 <%= Html.ValidationMessage("Name", "*") %>
 15:             </p>
 16:             <p>
 17:                 <label for="Email">Email:</label>
 18:                 <%= Html.EditorFor(p => p.Email)%>
 19:                 <%= Html.ValidationMessage("Email", "*") %>
 20:             </p>
 21:             <p>
 22:                 <input type="submit" value="Create" />
 23:             </p>
 24:         </fieldset>
 25: 
 26:     <% } %>
 27: 
 28:     <div>
 29:         <%=Html.ActionLink("Back to List", "Index") %>
 30:     </div>
 31: 
 32: </asp:Content>

예제 코드에서 보듯이 13번 라인과 19번 라인은 기본적으로 생성되는 Html.TextBox 메서드 대신 Html.EditorFor 메서드를 사용하도록 수정한 상태입니다. 이 상태에서 예제 애플리케이션을 실행하고 /Home/Create 뷰를 실행하면 아래와 같은 그림이 나타나게 됩니다.

그림 1: /Home/Create 뷰에서 Submit 버튼을 클릭하여 서버 측 유효성 검사가 실행된 모습
img1

위의 그림 1의 모습은 /Home/Create 뷰에서 Create 버튼을 클릭하여 서버 측 유효성 검사가 실행된 모습입니다. Preview 1에 추가된 DataAnnotations API를 이용한 유효성 검사가 잘 동작하고 있지만 앞서 설명드렸듯이 이 페이지는 이미 서버 측으로 페이지가 Submit 된 이후의 모습입니다. 그러면 이 /Home/Create 뷰를 클라이언트 측 유효성 검사가 동작하도록 수정해 보겠습니다.

코드 3: 수정된 /Home/Create 뷰 페이지의 소스 코드

  1: <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
  2: 
  3:     <script src="../../Scripts/jquery-1.3.2.js" type="text/javascript"></script>
  4:     <script src="../../Scripts/jquery.validate.js" type="text/javascript"></script>    
  5:     <script src="../../Scripts/MicrosoftMvcJQueryValidation.js" type="text/javascript"></script>
  6: 
  7:     <h2>Create</h2>
  8:     <% Html.EnableClientValidation(); %>
  9:     <%= Html.ValidationSummary("Create was unsuccessful. Please correct the errors and try again.") %>
 10: 
 11:     <% using (Html.BeginForm()) {%>
 12: 
 13:         <fieldset>
 14:             <legend>Fields</legend>
 15:             <p>
 16:                 <label for="Name">Name:</label>
 17:                 <%= Html.EditorFor(p => p.Name) %>
 18:                 <%= Html.ValidationMessage("Name", "*") %>
 19:             </p>
 20:             <p>
 21:                 <label for="Email">Email:</label>
 22:                 <%= Html.EditorFor(p => p.Email)%>
 23:                 <%= Html.ValidationMessage("Email", "*") %>
 24:             </p>
 25:             <p>
 26:                 <input type="submit" value="Create" />
 27:             </p>
 28:         </fieldset>
 29: 
 30:     <% } %>
 31: 
 32:     <div>
 33:         <%=Html.ActionLink("Back to List", "Index") %>
 34:     </div>
 35: 
 36: </asp:Content>

예제 코드에서 눈 여겨 볼 부분은 3번 라인부터 5번 라인까지 스크립트를 페이지에 추가하는 코드와 8번 라인의 Html.EnableClientValidation() 메서드 호출입니다. 이 메서드는 페이지에 클라이언트 측 유효성 검사를 활성화하는 역할을 담당하며 이는 앞서 설명한 세 개의 스크립트 파일이 페이지에 추가되어 있어야 가능하게 됩니다. 그 이외의 코드는 전혀 변한 것이 없지만 페이지를 실행해 보면 아래 그림과 같이 그림 1과는 다소 다른 모습을 볼 수 있습니다.

그림 2: 클라이언트 측 유효성 검사가 동작한 모습
img2

그림 2에서 보듯이 유효성 검사 메시지가 Html.ValidationMessage 메서드를 호출한 위치에 나타나지만 ValidationSummary 메서드를 호출한 영역에는 아무것도 나타나지 않습니다. 또한 텍스트 상자에 값을 입력하면 유효성 오류 메시지는 곧바로 사라지도록 구현이 되어 있습니다. 제법 잘 만들어 진 것 같지요?

이렇게 함으로써 우리가 얻을 수 있는 이점은 모델 객체에 대한 메타데이터 클래스만 구현함으로써 모델 객체에 대한 유효성 검사 코드를 전혀 작성할 필요가 없다는 점. 또한 클라이언트 측 유효성 검사 기능을 이용함으로써  메타데이터 클래스 만으로 서버 측과 클라이언트 측 유효성 검사를 동시에 처리할 수 있다는 점 등이 장점으로 작용할 수 있겠습니다. 시간이 지날수록 점점 더 편리하면서도 강력한 기능을 제공하는 ASP.NET MVC 2의 정식 버전이 벌써부터 기다려지네요. =)

Posted by 웹지니 트랙백 0 : 댓글 0

안녕하세요? 웹지니입니다.
모두 추석 연휴는 즐겁게 보내셨나요? 저도 오랫만에 식구들과 편안한 시간을 가졌습니다. 열심히 재충전 했으니 이제 또 열심히 달려봐야죠?
추석 연휴 전날 즉 10월 1일부터 재충전 한답시고 인터넷을 뚝 끊었다가 오늘 출근해서 다시 보니 ASP.NET MVC 2.0 Preview 2 버전이 그새 릴리즈 되었더라구요 -ㅅ-;;
이번 Preview 2 버전에서 달라진 점들을 간략히 요약해 볼까요?

1. Client-Side Validation
ASP.NET MVC 2.0 Preview 2에서는 jQuery 기반의 클라이언트 측 유효성 검사 라이브러리가 추가되었습니다.

2. Areas
ASP.NET MVC 2.0 Preview 2에서는 Area 지원이 보다 원활해 졌습니다. 이미 Preview 1버전에서도 지원은 가능했지만 자동화되어 있지 않았기에 개발자가 직접 프로젝트 파일을 수정해야 하는 불편이 있었지요.

3. Model Validation Providers
모델 객체를 바인딩할 때 개발자가 원하는 유효성 검사를 수행할 수 있게 되었습니다. 기본적으로는 DataAnnotations API를 이용하고 있네요. 사실 아시는 분은 아시겠지만 DataAnnotations API는 아직 문제가 좀 있지요?

4. Metadata Providers
앞서 모델 객체의 유효성 검사와 연관되는 사항으로 모델 객체에 대한 사용자 정의 메타데이터 객체를 지정할 수 있습니다. 기본적으로 사용되는 메타데이터 제공자 역시 DataAnnotations API를 활용하고 있습니다.

Preview 1에 비해 많이 달라진 부분은 없는 것 같네요. 아마도 안정성의 향상과 Preview 1에서의 불편했던 점을 해소하는 방향으로 개발이 진행된 느낌입니다. 저도 아직 설치를 못해봤으니 빨리 설치해 봐야겠어요. 참, 다운로드는 여기를 클릭하세요!

Posted by 웹지니 트랙백 0 : 댓글 1