<알기 쉬운 마이크로소프트 ASP.NET AJAX> 출간!!!

View Comments

사용자 삽입 이미지

그동안 정말 열심히 작업한 <알기 쉬운 마이크로소프트 ASP.NET AJAX - Atlas에서 ASP.NET AJAX까지>가 예약판매에 들어갔습니다. 이 책은 FOA(Foundations of Atlas)를 번역한 것으로, 그간에 있었던 변경사항을 맞추기 위해 역자가 공을 많이 들인 작품입니다.

의도하지 않게 미흡한 점들이 곳곳에 드러날 수도 있겠지만, ASP.NET "AJAX"를 이해하는데 도움이 되는 책이 되었으면 하는 바램입니다.

번역하면서 모아둔 자료들은 독자들을 위한 AS 위키를 통해 공유하고 있으니 확인해보시면 되겠습니다.

그리고, 3월 중순쯤에 2회 정도 세미나를 진행할 예정이니 관심 있으신 분들은 기억해두세요.


강컴 도서 소개
http://kangcom.com/common/bookinfo/bookinfo.asp?sku=200702210001

독자들을 위한 AS 위키
http://paewang.net/dokuwiki/doku.php?id=foundations_of_atlas


** 도서 구매
강컴 알라딘 인터파크
2007/02/21 16:01 2007/02/21 16:01

댓글0 Comments (+add yours?)

트랙백0 Tracbacks (+view to the desc.)

Visual C# 2005 Keyboard Shortcut Reference Poster

View Comments

Microsoft에서 Visual C# 2005 Keyboard Shortcut Reference Poster를 제공하고 있다. 링크는 http://www.microsoft.com/downloads/details.aspx?familyid=C15D210D-A926-46A8-A586-31F8A2E576FE&displaylang=en 이고, 아래에 다운로드 링크를 직접 걸어두었다.

VCSharp_2005_color.pdf
VCSharp_2005_grayscale.pdf


VB와 VC++도 제공하고 있으니, 필요하다면 링크를 타고 다운로드 받으면 되겠다.

Visual Basic 2005 Keyboard Shortcut Reference Poster
http://www.microsoft.com/downloads/details.aspx?FamilyID=bccf84f4-4136-48b2-b4ec-83eaa484da20&DisplayLang=en

Visual C++ 2005 Keyboard Shortcut Reference Poster
http://www.microsoft.com/downloads/details.aspx?FamilyID=bccf84f4-4136-48b2-b4ec-83eaa484da20&DisplayLang=en

2007/02/20 16:03 2007/02/20 16:03

2 Comments (+add yours?)

트랙백0 Tracbacks (+view to the desc.)

C# 언어요소(1) - 데이터 멤버 대신에 항상 프로퍼티를 사용하라

View Comments

출처 : 한빛 네트워크 "C# 언어요소(1) - 데이터 멤버 대신에 항상 프로퍼티를 사용하라"


C# 언어에서는 다양한 이유로 프로퍼티의 사용을 장려한다. 지금까지도 타입(type)에서 public 데이터 변수를 사용하거나, get/set류의 메서드를 직접 만들어서 사용하고 있다면 이제 그런 방 법을 사용하지 않는 것이 좋다. 프로퍼티는 우리가 만든 타입의 값을 외부에서 접근할 수 있도 록 하면서 동시에 객체지향에서 논의되는 캡슐화를 지원한다. 프로퍼티는 데이터 멤버처럼 접 근가능하면서 메서드의 형태로 구현되는 C# 언어의 요소이다.

고객의 이름, 특정 위치의 (x, y) 좌표, 지난 연도의 수입 등은 타입 내에서 데이터의 형태로 작 성하는 것이 가장 적절하다. 프로퍼티는 메서드의 이점을 가지면서도 마치 데이터에 직접 접근 하는 것과 같은 방법을 제공한다. 프로퍼티를 이용하여 작성된 타입을 사용하는 것은 public 변수를 접근하는 것과 동일한 방법으로 구현된다. 하지만 프로퍼티를 구현하는 것은 메서드를 구현하는 것과 유사하며, 실제 데이터 값을 얻어내거나 바꿀 때에 행동방식을 정의할 수 있다. 닷넷 프레임워크(.NET Framework)는 우리가 외부에서 접근 가능한 데이터에 대해서 프로퍼티 를 사용할 것이라 가정하고 있다. 실제로 닷넷 프레임워크에서 데이터 바인딩을 지원하는 클래 스들은 일반적인 public 데이터 멤버가 아니라 프로퍼티를 이용하여 타입의 값을 구현했을 때 만 정상적으로 동작한다. 웹 컨트롤이나 윈도우 폼 컨트롤과 같은 유저 인터페이스 컨트롤들은 객체의 프로퍼티와 밀접하게 연관되어 있다. 데이터 바인딩 메커니즘은 타입 내의 값에 접근하 기 위해서 Reflection을 이용하는데, 이때 프로퍼티로 구현된 값만을 찾는다.

textBoxCity.DataBindings.Add("Text", address, "City");


이 코드는 textBoxCity 컨트롤의 Text 프로퍼티와 address 객체의 City 프로퍼티를 바인딩한 다.[ITEM #38 참고] 이 코드는 City라는 이름의 public 멤버 변수에 대해서는 동작하지 않는다. public 데이터 멤버를 사용하는 것은 좋은 방법이 아니기 때문에 프레임워크 클래스 라이브러 리 설계자는 이러한 방법을 제공하지 않았다. 그들은 우리가 좀 더 나은 객체지향적 기술을 사용 하기 바랬기 때문이다. C++나 자바 개발자들은 불만이 있을 수도 있겠지만, 데이터 바인딩 코드 는 get/set류의 메서드 어떤 것도 찾지 않는다. get/set류의 메서드 대신 프로퍼티를 사용하자.

데이터 바인딩은 화면상에 출력될 값을 포함하고 있는 객체에 대해서만 적용이 가능하다. 하지 만 이것이 프로퍼티가 UI 요소에 한해서만 사용될 수 있다는 의미는 아니다. 다른 요소에 대해 서도 프로퍼티는 훌륭히 사용될 수 있으며, 프로그램에 대하여 추가적인 요구사항이나 변경사항 을 반영하기도 쉽게 해준다. 프로그램 내에서 고객의 이름이 반드시 존재하도록 하고 싶다고 하 자. 만일Name이라는 public 프로퍼티를 사용하고 있다면 단지 한 군데 정도만 수정하면 된다.

public class Customer
{
   private string _name;
   public string Name
   {
      get
      {
         return _name;
      }
      set
      {
         if ((value == null) || (value.Length == 0))
            throw new ArgumentException("Name cannot be blank",
            "Name");
         _name = value;
      }
   }
}


만일 public 데이터 멤버 형태로 고객의 이름을 저장하고 있었다면 코드 전체에서 customer 이름을 변경하는 부분을 모두 찾아내어 수정해야 하는데, 이는 많은 시간이 소모된다.

프로퍼티는 메서드의 형태로 구현되기 때문에 멀티쓰레드 지원기능 또한 손쉽게 추가될 수 있 다. 단순히 프로퍼티의 get과 set 메서드가 데이터에 대해서 동기적으로 접근할 수 있도록 수정 하면 된다.

public string Name
{
   get
   {
      lock(this)
      {
         return _name;
      }
   }
   set
   {
      lock(this)
      {
         _name = value;
      }
   }
}


프로퍼티는 메서드의 언어적인 특성을 모두 가지고 있다. 따라서 프로퍼티는 virtual로 지정이 가능하다.

public class Customer
{
   private string _name;
   public virtual string Name
   {
      get
      {
         return _name;
      }
      set
      {
         _name = value;
      }
   }
}


또한, 프로퍼티는 abstract 형태로 정의되거나 interface의 한 부분으로 정의될 수도 있다.

public interface INameValuePair
{
   object Name
   {
      get;
   }
   object Value
   {
      get;
      set;
   }
}


마지막으로, interface를 const와 non-const 형태로 만들 수 있다.

public interface INameValuePair
{
   object Name
   {
      get;
   }
   object Value
   {
      get;
      set;
   }
}

public interface INameValuePair
{
   object Value
   {
      get;
      set;
   }
}

// 사용예:
public class Stuff : IConstNameValuePair, INameValuePair
{
   private string _name;
   private object _value;
   #region IConstNameValuePair Memebers

   public object Name
   {
      get
      {
         return _name;
      }
   }

   object IConstnameValuePair.Value
   {
      get
      {
         return _value;
      }
   }

   #endregion

   #region INameValuePair Memebers
   public object Value
   {
      get
      {
         return _value;
      }
      set
      {
         _value = value;
      }
   }

   #endregion
}


프로퍼티는 클래스 내부의 데이터를 가져오거나 수정할 수 있는 메서드의 확장으로 볼 수도 있 다. 따라서 메서드를 통해서 할 수 있는 모든 동작은 프로퍼티에서도 똑같이 수행 가능하다.

프로퍼티는 두 가지의 형태의 accessor를 가질 수 있다. C# 2.0에서는 get/set accessor 각각 에 대해서 접근 한정자를 지정할 수 있는데, 이를 통해서 프로퍼티의 형태로 노출한 데이터에 대해서 적절히 가시성을 조정할 수 있다.

public class Customer
{
   private string _name;
   public virtual string Name
   {
      get
      {
         return _name;
      }
      protected set
      {
         _name = value;
      }
   }
}


프로퍼티 문법은 단순 데이터 필드를 좀 더 확장해서 사용할 수 있도록 하는데, 특정 타입을 사 용할 때 배열과 같이 인덱스를 이용한다. 인덱서(indexer)라고도 하는 이 기능은 배열의 인덱스 를 넘겨받는 프로퍼티라고도 볼 수 있다. 이러한 기능은 하나의 프로퍼티를 이용하여 다수의 값 에 접근하고자 할 때 매우 유용하다.

public int this [int index]
{
   get
   {
      return _theValues[index];
   }
   set
   {
      _theValues[index] = value;
   }
}

int val = MyObject[i];


인덱서는 하나의 값에 접근하는 프로퍼티와 동일한 특성을 갖는다. 인덱서는 메서드의 형태로 구현되고, 인덱서 내부에 값에 대한 검증이나 특정 계산 루틴을 포함시킬 수 있다. 또한 virtual 이나 abstract 형태로 만들어서 interface 내부에 선언하거나, 읽기 전용 또는 읽고 쓰기 가능 한 형태로 만들 수 있다. 숫자 인덱스를 사용하는 1차원 배열 형태의 인덱서는 데이터 바인딩에 서도 사용된다. 숫자가 아닌 인덱스를 사용할 경우에는 맵(map)이나 디렉토리(directory)와 같 은 자료구조의 표현도 가능하다.

public Address this[string name]
{
   get
   {
      return _theValues[name];
   }
   set
   {
      _theValues[name] = value;
   }
}


C# 언어에서의 다차원 배열 형태로 인덱서를 구성할 수도 있는데, 이 경우 각각의 차원에 대해 서 서로 다른 인덱스형을 지정할 수도 있다.

public int this[int x, int y]
{
   get
   {
      return ComputeValue(x, y);
   }
}

public int this[int x, string name]
{
   get
   {
      return ComputeValue(x, name);
   }
}


인덱서는 임의로 이름을 지정할 수 없기 때문에 항상 this 키워드를 이용하여 선언된다. 그러므 로 모든 타입은 항상 한 개의 인덱서만을 포함시킬 수 있다.

이러한 프로퍼티들은 매우 유용하며 구조가 잘 정의되어 있기 때문에, 프로그램 개발시 다양한 개선 효과를 볼 수 있다. 하지만 최초 구현시에는 public 데이터 멤버로 선언하고 나중에 프로퍼 티의 이점이 필요한 시점에 프로퍼티를 사용하도록 코드를 작성하고 싶을 수도 있다. 매우 이치 에 맞는 듯 하지만 이것은 적절하지 않다. 다음의 클래스 선언을 보자.

public class Customer
{
   public string Name;
}


이 클래스는 고객의 이름을 포함하는 Customer 클래스다. 고객의 이름을 얻거나 변경하기 위 해서 다음과 같은 코드를 사용한다.

string name = customerOne.Name;
customerOne.Name = "This Company, Inc";


이것은 매우 간단하고 수월해 보인다. 아마도 이러한 코드를 작성한 개발자는 나중에 Name이 라는 public 멤버를 프로퍼티로 변경하면 이를 사용하는 코드는 아무런 변경 없이 동작이 가능 하다고 생각했을 것이다. 일면 맞는 말이다.

하지만 프로퍼티는 데이터 멤버와 유사해 보이기는 하지만 데이터와 완전히 동일하지는 않다. 단지 새로운 문법 구조를 사용하지 않기 위해서 데이터 멤버에 접근하는 방식과 유사한 방법을 사용하는 것뿐이다. 사실 프로퍼티에 접근하는 IL 코드는 데이터 멤버에 접근하는 IL 코드와 서 로 다른 코드를 만들어낸다.

.field public string Name


Name 필드로부터 값을 가져오는 코드는 다음과 같은 IL 코드를 만들어낸다.

ldloc.0
ldfld         string NameSpace.Customer::Name
stloc.1


Name 필드에 값을 저장하는 코드는 다음과 같은 IL 코드를 만들어낸다.

ldloc.0
ldstr         "This Company, Inc"
stfld         string NameSpace.Customer::Name


IL을 하루 종일 볼 것은 아니기 때문에 IL을 모른다고 너무 걱정하지 말자. 여기서는 데이터 멤 버에 접근하는 코드와 프로퍼티에 접근할 때의 코드가 서로 다르고, 이러한 차이점이 결국 이진 호환성을 깨뜨린다는 것만 알면 된다. 이번에는 프로퍼티를 사용하는 Customer 타입을 보자.

public class Customer
{
   private string _name;
   public string Name
   {
      get
      {
         return _name;
     }
      set
      {
         _name = value;
      }
   }
}


이 Customer 타입을 사용하는 코드는 앞에서 알아본 것과 완전히 동일하다.

string name = customerOne.Name;
customerOne.Name = "This Company, Inc";

하지만 C# 컴파일러가 생성한 IL 코드는 완벽히 다르다. 다음에 나타난 Customer 타입에 대한 IL 코드를 보자.

.property instance string Name( )
{
   .get instance string Customer::get_Name( )
   .set instance void Customer::set_Name(string)
} // Customer::Name 메서드의 끝
.method public hidebysig specialname instance string
   get_Name( ) cil managed
{
   // 코드 크기 12 (0xc)
   .maxstack 1
   .locals init ([0] string CS$1$0000)
   IL_0000: nop
   IL_0001: ldarg.0
   IL_0002: ldfld string Customer::_name
   IL_0007: stloc.0
   IL_0008: br.s IL_000a
   IL_000a: ldloc.0
   IL_000b: ret
} // Customer::get_Name 메서드의 끝
   .method public hidebysig specialname instance void
      set_Name(string 'value') cil managed
{
   // 코드 크기 9 (0x9)
   .maxstack 8
   IL_0000: nop
   IL_0001: ldarg.0
   IL_0002: ldarg.1
   IL_0003: stfld string Customer::_name
   IL_0008: ret
} // Customer::set_Name 메서드의 끝


가장 중요한 점은 IL 코드내에서 프로퍼티가 어떻게 표현되는가 하는 문제이다. 위에서 보듯이 .property 지시자에 의해서 프로퍼티가 선언된다. 그리고 이 프로퍼티에 접근할 수 있는 get/set accessor가 구현된다. 두 개의 메서드는 사용자가 직접적으로 메서드를 사용할 수 없 도록 hidebysig와 specialname으로 선언되어 있다. 직접적으로 이 메서드를 호출하는 코드 는 쓸 수 없지만 우리는 프로퍼티 접근 방식으로 이러한 메서드를 간접적으로 사용할 수 있다.

// get
IL_0007: ldloc.0
IL_0008: callvirt instance string Customer::get_Name( )
IL_000d: stloc.1
// set
IL_000e: ldloc.0
IL_000f: ldstr "This Company, Inc"
IL_0014: callvirt instance void Customer::set_Name(string)


설사 우리가 동일한 형태의 코드를 사용해서 Customer 타입에 접근할지라도 Name이 데이터 멤버로 구현되었는지 아니면 프로퍼티로 구현되었는지에 따라 서로 다른 코드가 만들어진다. 프로퍼티나 데이터 멤버에 접근하는 코드를 만드는 것은 C# 컴파일러의 몫이므로 우리가 제어 할 수 있는 방법은 없다.

비록 프로퍼티와 데이터 멤버의 접근 코드가 소스차원에서는 완전히 동일하다 하더라도 이진 호환성을 가지지는 못한다. 따라서 데이터 멤버로 선언된 값을 나중에 프로퍼티로 변경하게 되 면, 모든 코드에 대해서 재컴파일을 반드시 수행해야 한다.‘ 4장, 이진 컴포넌트 작성’에서 이진 컴포넌트에 대해서 자세히 다루게 될 것이다. 하지만 데이터 멤버를 사용하는 코드를 프로퍼티 를 사용하도록 변경하면 이진 호환성을 잃어버린다는 것은 반드시 기억해야 한다. 이것은 애플 리케이션을 구성하는 어셈블리 중에 일부만을 개선하여 배포할 경우 복잡한 배포문제를 유발할 가능성이 있다.

프로퍼티에 대한 IL 코드를 살펴보면 프로퍼티를 사용할 때 수행성능에 좋지 않은 영향을 미칠 것 같다고 생각할 수도 있다. 프로퍼티에 대한 접근이 데이터 멤버의 접근보다 빠른 것은 아니 지만 그렇다고 현저히 느리지도 않다. JIT 컴파일러는 property accessor와 같은 몇몇의 메서 드 호출에 대해서 inline을 수행한다. 만일 JIT 컴파일러가 property accessor에 대해서 inline을 수행한다면, 데이터 멤버에 대한 접근과 프로퍼티에 대한 접근은 동일한 수행성능을 보일 것이다. 설사 inline이 수행되지 않는다 하더라도 실질적인 수행성능은 메서드 호출에 대 한 비용 정도로 아주 사소하다. 대부분의 상황에서 이러한 메서드 호출 비용은 측정하기 불가능 할 정도로 작다.

만일 public이나 protected 형태로 타입의 값을 노출해야 하는 경우라면 항상 프로퍼티를 사 용하자. 다수의 값에 대해서 순서대로 접근을 하거나 디렉토리 형태의 자료 구조를 표현할 때는 인덱서를 사용하자. 모든 데이터 멤버는 예외 없이 private로만 사용하는 것이 좋다. 이렇게 함 으로써 데이터 바인딩 지원이 가능해지며 추후에 메서드의 변경이 발생했을 때 좀 더 쉽게 코드 를 변경할 수 있다. 프로퍼티 형태를 지원하기 위해서 추가적으로 입력해야 하는 시간은 고작 1~2분 정도일 것이다. 하지만 나중에 프로퍼티를 사용하기 위해서 코드를 찾고 수정하려면 최 소한 몇 시간이 필요할 것이다. 현재의 짧은 시간을 절약하기 위해서 미래의 더 큰 시간을 낭비 하지 말자.

2007/02/17 23:22 2007/02/17 23:22

댓글0 Comments (+add yours?)

트랙백0 Tracbacks (+view to the desc.)

Microsoft Script Editor

View Comments

Microsoft Script Editor에 대해 잘 소개하고 있는 블로그를 발견하여 소개한다. 여기에서는 설치화면에 대한 설명만 할테니 나머지는 Welcome To 봉달's World "Microsoft Script Editor"를 읽어보시라.

사용자 삽입 이미지

MSE7에 해당하는 설치옵션을 선택하면 '처음 사용할 때 설치'로 되어 있는데, '내 컴퓨터에서 실행'으로 변경하고 '업데이트' 버튼을 누른다. 설치가 완료되면 C:\Program Files\Microsoft Office\OFFICE11\ 에서 MSE7.exe를 실행시켜 본다.
2007/02/17 05:52 2007/02/17 05:52

댓글0 Comments (+add yours?)

트랙백0 Tracbacks (+view to the desc.)

ASP.NET 컨트롤 – 클라이언트와 서버측 간의 브리지

View Comments

A Korean translation of 「ASP.NET Controls - The Bridge between Client and Server-Side



유의: 누노 고메즈(Nuno Gomes) responsibility constrain 어떤 의미로 사용했는지 파악하기 어렵습니다. 때문에 글을 읽고 이해하는데 매끄럽지 못한 부분이 있을 수 있음을 알려드립니다. 


ASP.NET 컨트롤 클라이언트와 서버측 간의 브리지

 

 

소개


이전
아티클[注:|한국어에 익숙한 독자들을 위해 번역문으로 연결하였다.]에서 특성 'name' 'id' 길이에 대해 언급하면서, 크기게 쉽게 커지는 이유에 대해 알아보았다
.
이번 아티클에서는 필자의 관심사인 "렌더링되는 이유는 무엇이고, ASP.NET에서의 역할은 무엇인가?" 대한 대답에 초점을 맞추도록 하겠다.

배경


특성
'id' 대한 역할은 클라이언트측에서 마무리된다. 특성은 브라우저 엔진과 프로그래머가 페이지에 html 요소를 쉽기 찾도록 지정한 경우에만 사용된다. 매우 유용한 특성은 요구되지 않기에 프로그래머가 ASP.NET 컨트롤
ID 속성 값을 명시적으로 설정할 때에만 렌더링된다.

클라이언트측 스크립트를 비중 있게 사용한다면, 특성이 항상 렌더링되지 않는지 생각해봐야 한다. 반대로 모든 html 요소가 렌더링된 id 특성을 가지고 있는 상황- 일반적으로 스크립트(label 같은) 의해 변경되지 않는 요소와 필요하지 않은 경우 조차도 렌더링을 해버리는 경우- 생각해보자. 마지막 시나리오는 네트워크 대역폭과 같이 귀중한 자원들인 리소스의 낭비로 여겨질 따름이다.

특성 'name' 역할은 너무나 복잡하다. 사실, ASP.NET 사용자 경험(UX: User eXperience) 풍부함(richness) 대개 고유성(uniqueness) 부분에서 생긴다.
'id'
달리 'name' 특성은 거의 항상 렌더링되고, 요소 특성을 직접 렌더링하지 않을 때조차도 여전히 스크립트 (script statement)(일상적으로 유명한 __doPostBack) 사용된다
.
'name'
특성은 해결해야 가지 작업을 갖고 있다.

  1. 번째는 이전 ASP 시절에서 유래되었고, 증거로 'name' 값은 post 서버측 컬렉션(HttpContext.Current.Request.Form)에서 키로 사용되었다.
  2. 번째로, ASP.NET 의해 추가된 새로운 responsibility 현재 포스트 백의 고유한 요소 소스를 식별하는 responsibility 'name' 값에 위임한다.

마지막 responsibility 'name' 특성 값에 새로운 constrain[注:|Processing에서 constrain()는 특정 값이 최소값과 최대값을 넘지 않도록 강제한다. http://www.nabi.or.kr/processing/constrain_.html] 추가한다. 이건 반드시 고유해야 하지만, 동시에 때때로 이전에 렌더링한 'name' 특성 값과 동일한 단일 서버측 컨트롤에 직접적으로 매칭해야만 한다.

작업을 완료하려면 모든 ASP.NET 컨트롤은 컨트롤 계층구조를 기반으로 빌드되는 UniqueID 속성을 가져야 한다. 컨트롤이 항상 동일한 UniqueID 갖는다는 보증하기 위해서는 역동적인 컨트롤이 항상 페이지 수명 주기(Page Life cycle)[注:|한국어에 익숙한 독자들을 위해 한국어 버전으로 연결하였다.] 동일한 지점에 추가될 조차도 부모 계층구조가 항상 동일한 방식으로 빌드되어야 한다.

결론


이전
아티클에서 언급했듯이, 'name' 'id' 특성은 때때로 예상보다 커지고, 이들의 크기를 줄이는 방안을 생각해봐야 필요가 있다. 어떠한 시도에서도 'name' 번째 responsibility 항상 보존해야 한다. 작업을 완료하지 않았다면, ASP.NET 주요 장점 가운데 하나를 잊게 되는 거다.


클라이언트와
서버측 브리지는 OnClick OnChange (OnTextChange, OnSelectIndexChange ) 같은 서버측 컨트롤 이벤트가 발생하도록 허용한다.

 

 

출처 : http://www.codeproject.com/useritems/ASPNET_BridgeClientServer.asp



PDF 다운로드 :




이 글은 고수닷넷, 데브피아, 훈스닷넷에도 게시되었습니다.

2007/02/17 05:27 2007/02/17 05:27

댓글0 Comments (+add yours?)

트랙백0 Tracbacks (+view to the desc.)

AJAX 웹 페이지에 모래시계 커서를 표시하는 방법은?

View Comments

A Korean translation of 「How to display an hourglass cursor on an AJAX web page?


AJAX 웹 페이지에 모래시계 커서를 표시하는 방법은?

 

소개

Microsoft Ajax 1.0
이 막 출시되었다! (http://go.microsoft.com/fwlink/?LinkID=77296).

동기 웹 폼에서는 페이지 플래시백(flashback)[注:|20세기 이래 희곡이나 소설에서도 사용된 영화용어로 현재 상황과 관계 있는 과거의 사건을 삽입하여 작품의 시간적 순서를 차단하는 서술상의 기법.]처리과정(processing) 볼 수 있기 때문에 사용자와의 상호작용이 더욱 쉬었다. Ajax에서는 update progress panel를 추가할 수 있고, 애니메이션 gif를 안에 넣을 수 있다. 하지만, 모래시계(hourglass)를 보기 원하다면 어떻게 해야할까?

다음 자바스크립트 코드를 사용하면 멋지게 끝낼 수 있다.


<scriptmanager id="ScriptManager1" runat="server" />

var prm = Sys.WebForms.PageRequestManager.getInstance();
    prm.add_initializeRequest(InitializeRequest);
    prm.add_endRequest(EndRequest);
    function InitializeRequest(sender, args) {
     document.body.style.cursor = "wait";   
    }
    function EndRequest(sender, args) {
     document.body.style.cursor = "default";   
    }
 

 

출처 : http://www.codeproject.com/Purgatory/hourglassajax.asp


이 글은 고수닷넷, 데브피아, 훈스닷넷에도 게시되었습니다.

2007/02/16 01:06 2007/02/16 01:06

댓글0 Comments (+add yours?)

트랙백0 Tracbacks (+view to the desc.)

ASP.NET 컨트롤 – ID 생성 이해하기

View Comments

A Korean translation of 「ASP.NET Controls - Understanding id generation


ASP.NET 컨트롤 – ID 생성 이해하기


 

소개


아티클의 독자들은 적어도 이상 간단한 DataGrid UserControl 포함하고 있는 HTML 페이지의 소스를 들여다보았을 터이고, 'id' 'name' 특성 길이(attribute length) 대해 이미 알고 있을 것이다.
아티클의 목적은 "어떻게?" 커다란 크기에 도달하는지, "?" 그렇게 되는지 이해하는데 있다.


배경


특성들은 항상 고유(unique)해서 UniqueID라고 불리는 컨트롤 서버측 속성(property) 가시적인 측면에 대해 말할 놀랍게 느껴지지 않는다.

Control 모든 WebControls 위한 기본 클래스이고, 가지 id 속성을 제공받는다.

·         ID프로그래머가 설정할 있다.

·         UniqueID서버측과 postdata 관리를 위해 사용되는 읽기전용 id 생성한다. 속성은 특성 'name' 렌더링한다.

·         ClientID클라이언트측 전용을 위한 UniqueID 특별한 읽기전용 변환. 속성은 특성 'id' 렌더링한다.


Control 코드를 시스템적으로 분석해보면 ClientID 값은 UniqueID 변환임을 보여주고, 최종 값은 NamingContainer.ID ID 합성이다.

이러한 속성들 간의 관계는 모든 컨트롤이 포함된 서버측 속성 ID 간단한 조작을 통해서 'id' 'name' 특성 관리할 있음을 알려준다.


잠재적인
문제


거의 모든 기업 ASP.NET 솔루션들은 높은 주문제작 수준을 요구함을 염두에 두고 있어야 하고, 한다. 그러므로 적절한 합성 사용자정의 컨트롤들을 많이 가지고 있어야 한다. 이러한 사용자정의 컨트롤(custom control)들은 중요하게 사용되고, 부모 계층구조(parent hierarchy) 깊이가 현저하게 증가한다.  게다가 우리가 잊지 말아야

  • 이름 지정은 쉬운 문제가 아니다.
  • 이름은 계획된 기능(functionality) 묘사해주어야 한다. 지은 이름은 가독성과 코드 유지보수(간단하게 페이지 크기를 최소화시킬 있다면 그에 상응하는 대가를 지불할 용의가 준비되어 있는가?) 향상시킨다.
  • 놀랄 만큼 창의적인 이름 지정은 프로그래머의 능력에 달려 있다.

 

 

출처 : http://www.codeproject.com/useritems/ASPNETcontrols_idgenerati.asp


PDF 다운로드 :




이 글은 고수닷넷, 데브피아, 훈스닷넷에도 게시되었습니다.


** MSDN 자료 **

Control.ID 속성 
http://msdn2.microsoft.com/ko-kr/library/system.web.ui.control.id(VS.80).aspx
Control.ClientID 속성
http://msdn2.microsoft.com/ko-kr/library/system.web.ui.control.clientid(VS.80).aspx
Control.UniqueID 속성
http://msdn2.microsoft.com/ko-kr/library/system.web.ui.control.uniqueid(VS.80).aspx
Control.NamingContainer 속성
http://msdn2.microsoft.com/ko-kr/library/system.web.ui.control.namingcontainer(VS.80).aspx
ASP.NET 웹 페이지의 클라이언트 스크립트 
http://msdn2.microsoft.com/ko-kr/library/3hc29e2a(VS.80).aspx

2007/02/15 14:08 2007/02/15 14:08

댓글0 Comments (+add yours?)

트랙백0 Tracbacks (+view to the desc.)

자바스크립트는 객체지향 프로그래밍 언어인가?

View Comments

출처 : AppDev: Something You Should Know by Irena Kennedy  "SYSK 285: Is JavaScript an Object Oriented Programming Language"


First, let’s agree on a definition of object oriented programming. 

It is generally agreed that “software objects” share two characteristics: they all have state and behavior (similar to real-life objects).  Behavior is implemented through methods (a.k.a. functions) and the state is stored in fields (a.k.a. class members).

In addition, an OOP language must support the following three major features: encapsulation, inheritance and polymorphism.

 

So, let’s see if JavaScript fits the OOP definition…

 

1.    First, let’s implement a “class” Pet (see note below) private member variables (i.e. information hiding) and properties  demonstrating encapsulation in JavaScript

 

Note: JavaScript doesn’t use classes as C# or VB.NET.  It is sometimes called a ‘class-free’ language as it uses functions to implement the concept of classes, constructors, and methods. 

 

function Pet(name, color)

{

    // Variables declared in constructor become private members

    var _name = name;

    var _color = color;

       

    // Public methods

    this.getName = function() { return _name; }

    this.setName = function(name) { _name = name; }

    this.getColor = function() { return _color; }      

    this.setColor = function(color) { _color = color }

}

 

// Public instance member

Pet.prototype.getFamily = "unknown";

 

 

To create this class:

 

var pet = new Pet("Spot", "white-and-black");

 

or

 

var pet = new Pet();

pet.setName("Spot");

pet.setColor("white-and-black");

 

 

To call methods:

 

alert("My pet's name is " + pet.getName() + " and he is " + pet.getColor() + ".  Family = " + pet.getFamily + ".");

 

 

2.    Now, let’s implement inheritance and extend the base type by adding ‘bark’ method:

 

function Dog(name, color)

{

    Pet.call(this, name, color);

}

 

Dog.prototype = new Pet();

Pet.prototype.getFamily = "canine";

 

Dog.prototype.bark = function()

{

    alert('Woof-woof');

}

 

 

var pet = new Dog("Spot", "white-and-black");

alert("My pet's name is " + pet.getName() + " and he is " +

     pet.getColor() + ".  Family = " + pet.getFamily + ".");

pet.bark();

 

 

3.    Next, let’s implement aggregation (has-a) support…

 

Simply defined, aggregation or containment, in object-oriented terms, means the ability to store objects entirely inside other objects.

 

function Address(street, city)

{

    var _street = street;

    var _city = city;

       

    this.getStreet = function() { return _street; }

    this.setStreet = function(street) { _street = street; }

    this.getCity = function() { return _city; }

    this.setCity = function(city) { _city = city; }

}

 

function Pet(name, color)

{

    // Variables declared in constructor become private members

    var _name = name;

    var _color = color;

   

    var _livesAt = new Address();

       

    // Public methods

    this.getName = function() { return _name; }

    this.setName = function(name) { _name = name; }

    this.getColor = function() { return _color; }      

    this.setColor = function(color) { _color = color; }

   

    this.setAddress = function(street, city)

    {

        _livesAt.setStreet(street);

        _livesAt.setCity(city);

    }

   

    Pet.prototype.Address = _livesAt;

}

 

 

var pet = new Dog("Spot", "white-and-black");

pet.setAddress("123 Main Street", "Wonderland");

 

alert("My dog " + pet.getName() + " lives at " + pet.Address.getStreet() + " in " + pet.Address.getCity() + " city");

 

 

4.     How would you implement polymorphism?  Let’s introduce the Cat and method ‘speak’ (notice that Dog and Cat use slightly different ways to override the Pet.speak method):

 

function Address(street, city)

{

    var _street = street;

    var _city = city;

       

    this.getStreet = function() { return _street; }

    this.setStreet = function(street) { _street = street; }

    this.getCity = function() { return _city; }

    this.setCity = function(city) { _city = city; }

}

 

function Pet(name, color)

{

    // Variables declared in constructor become private members

    var _name = name;

    var _color = color;

   

    var _livesAt = new Address();

       

    // Public methods

    this.getName = function() { return _name; }

    this.setName = function(name) { _name = name; }

    this.getColor = function() { return _color; }      

    this.setColor = function(color) { _color = color; }

   

    this.setAddress = function(street, city)

    {

        _livesAt.setStreet(street);

        _livesAt.setCity(city);

    }

   

    this.Address = _livesAt;

   

    Pet.prototype.speak = function() { throw "Method 'speak' must be implemented by derived classes"; }

}

 

 

// Public instance member

Pet.prototype.getFamily = "unknown";

 

 

function Dog(name, color)

{

    Pet.call(this, name, color);

}

 

Dog.prototype = new Pet();

Dog.prototype.getFamily = "canine";

 

Dog.prototype.speak = function()

{

    alert('Woof-woof');

}

 

// Cat type

function Cat(name, color)

{  

    Pet.call(this, name, color);

   

    this.speak = function()

    {

        alert('Meeeoooow');

    }

}

 

Cat.prototype = new Pet();

Cat.prototype.getFamily = "feline";

 

 

 

To call:

var pet1 = new Dog("Spot", "white-and-black");

var pet2 = new Cat("Felix", "white");

 

pet1.speak();

pet2.speak();

 

 

 

What’s the bottom line?  JavaScript is a loosely typed (as opposed to strongly typed C#) language that does appear to have support for OOP concepts, and thus, arguably, it is an object oriented programming language.

2007/02/15 01:18 2007/02/15 01:18

댓글0 Comments (+add yours?)

트랙백0 Tracbacks (+view to the desc.)

AJAX 테스트 자동화

View Comments

출처 : MSDN Magazine "AJAX 테스트 자동화"


AJAX(Asynchronous JavaScript And XML) 기술을 사용하는 웹 응용 프로그램이 지난 한 해 동안 꾸준히 증가했습니다. 올바르게 작성된 AJAX는 비 AJAX 웹 응용 프로그램에 비해 성능과 사용자 환경 측면에서 월등히 뛰어납니다. 그러나 AJAX 웹 응용 프로그램은 비동기적으로 작동하므로 일반적인 동기식 테스트 자동화 기법을 대부분 적용할 수 없습니다. 이번 달 칼럼에서는 간단한 테스트 자동화 프로그램을 작성하여 AJAX 웹 응용 프로그램 기능을 확인하는 기법에 대해 설명하겠습니다.

몇 장의 스크린샷을 통해 이번 달 칼럼의 방향을 살펴보겠습니다. 그림 1은 AJAX 기술을 사용하는, 간단하지만 전형적인 ASP.NET 웹 응용 프로그램을 보여 줍니다. 이 응용 프로그램은 Microsoft® Windows Live™ Local과 같은 지도 검색 도구를 시뮬레이션합니다. 사용자가 방향 단추 컨트롤 중 하나를 클릭하면 웹 서버에서 해당 지도 이미지를 가져와 중앙에 새 지도를 표시합니다. 스크린샷에 명확하게 나타나지 않는 부분은 웹 응용 프로그램이 Microsoft ASP.NET AJAX를 사용하여 비동기적으로 지도 이미지를 보내고 검색한다는 점입니다. 물론 실제 웹 응용 프로그램이 훨씬 더 복잡하겠지만 여기서 설명하는 기법은 복잡한 응용 프로그램에도 손쉽게 적용할 수 있으며 구현 기술에 관계없이 모든 AJAX 지원 응용 프로그램에서 작동합니다.

그림 1의 응용 프로그램을 수동으로 테스트하는 것은 비효율적이고 지루한 작업이며 시간도 많이 소모됩니다. 보다 효율적인 테스트 방식은 그림 2와 같이 간단한 테스트 자동화 프로그램을 작성하는 것입니다. 테스트 프로그램은 두 개의 프레임으로 구성되는 간단한 HTML 페이지입니다. 오른쪽 프레임은 웹 응용 프로그램을 호스팅하며 수정이나 조작이 이루어지지 않습니다. 왼쪽 프레임은 Internet ExplorerDOM(문서 개체 모델)을 사용하여 오른쪽 프레임의 웹 응용 프로그램을 조작하는 JavaScript 코드가 포함된 단일 HTML 페이지를 호스팅합니다.

이 테스트 기법은 비동기 작업을 사용하는 응용 프로그램을 처리하도록 설계되었지만 기존의 동기 HTTP 요청/응답 메커니즘을 사용하는 웹 응용 프로그램에도 적용됩니다. 이번 칼럼에서는 테스트 대상 웹 응용 프로그램에 대해 간략하게 설명하여 기존 테스트 자동화 기법이 AJAX 응용 프로그램에서 비효율적인 이유를 알아보겠습니다. 그 다음 그림 2의 이미지를 생성한 테스트 프로그램을 세부적으로 설명하고 여기서 제시되는 기법을 각자의 요구에 맞춰 수정 및 확장하는 방법을 살펴보겠습니다. 결과적으로 이 기법은 유용한 도구로 개발자와 테스터의 기술 지식을 보완해줄 것입니다.

사용자 삽입 이미지
그림 1 테스트 대상 AJAX 웹 응용 프로그램


테스트 대상 웹 응용 프로그램

필자는 AJAX 응용 프로그램 개발 작업을 훨씬 간편하게 해주는 ASP.NET AJAX 코드 라이브러리를 사용하여 그림 1과 같은 AJAX 지도 응용 프로그램을 만들었습니다. 실제 지도 이미지를 사용하는 대신 1-9 숫자를 포함하는 9개의 더미 이미지(1.jpg에서 9.jpg까지)를 사용했습니다. Microsoft TerraServer를 사용하여 실제 지도 이미지를 얻는 예제는 이번 호에 있는 Jeff Prosise의 Wicked Code 칼럼을 참조하십시오. 필자의 축소판 세계 지도는 3x3 격자로 구성되어 있습니다. 첫째 행이 1, 2, 3, 둘째 행이 4, 5, 6, 셋째 행이 7, 8, 9입니다. 5가 전체 지도의 중심이며 사용자가 5에서 North 컨트롤 단추를 클릭하면 2가 표시됩니다. North 단추의 논리는 그림 3에서 볼 수 있습니다.

사용자 삽입 이미지
그림 2
 샘플 테스트 실행

현재 지도가 전체 지도의 첫째 행(1, 2, 3)에 있을 때 North 단추를 클릭하면 아무런 효과가 없습니다. 그 외의 행에 있는 경우에는 현재 지도 위치의 숫자를 가져와 3을 빼 새로운 JPEG 이미지를 결정한 다음 Image 컨트롤의 ImageUrl 속성을 업데이트합니다. South, East 및 West 단추 컨트롤의 처리기도 이와 비슷합니다.

여기서는 응용 프로그램 코드를 간단하고 읽기 쉽게 만들기 위해 의도적으로 모범적인 코딩 방법을 사용하지 않았다는 점을 유의하십시오. 실제 웹 응용 프로그램에서 코드 논리는 백엔드 SQL 또는 XML 저장소에 있는 데이터를 액세스하여 가져오고 이 데이터를 사용하여 응용 프로그램 상태를 업데이트할 것입니다. 이 칼럼의 기법에서는 Internet Explorer DOM을 통해 웹 응용 프로그램을 테스트하므로 응용 프로그램의 상태를 확인하는 방식이 중요하지 않습니다. 사용자 입력이 발생하면 응용 프로그램 상태가 변경되어 응용 프로그램의 UI에 반영되는데, 이는 사용자가 Internet Explorer DOM을 통해 액세스할 수 있습니다.

일반적인 비 AJAX 방식은 웹 서버로 HTTP 요청을 게시하고 Form 개체나 쿼리 문자열로 요청 정보를 전달합니다. AJAX 기술을 사용하지 않아도 지도 응용 프로그램이 작동하지만 이 경우 두 가지 단점이 있습니다. 첫째, HTTP 요청/응답 메커니즘은 동기식이므로 웹 서버에서 요청을 처리하여 클라이언트로 응답을 반환하는 동안 사용자가 웹 응용 프로그램과의 상호 작용 기능 중 대부분을 사용할 수 없습니다. 둘째, 대부분의 경우 HTTP 요청은 전체 응답 페이지 생성으로 이어지기 때문에 Internet Explorer에서 응답을 받으면 전체 페이지가 다시 그려집니다. 요청/응답 속도가 비교적 빠른 경우에는 짜증나는 페이지 깜박임 현상이, 속도가 느린 경우에는 이보다 더 심한 빈 페이지 현상이 발생합니다.

AJAX는 이러한 문제를 모두 해결합니다. AJAX는 HTTP 요청 대신 XMLHTTP 요청을 보냅니다. XMLHTTP 요청은 비동기식이므로 XMLHTTP 요청이 처리되는 동안 사용자는 웹 응용 프로그램과 계속 상호 작용할 수 있습니다. 또한 XMLHTTP 응답을 받으면 전체 페이지를 다시 그리는 것이 아니라 Internet Explorer DOM을 통해 웹 페이지에서 새 데이터를 포함하는 부분만 다시 그리게 됩니다.

필자는 ASP.NET 지도 응용 프로그램에서 AJAX를 사용하기 위해 "원시" JavaScript를 작성하는 대신 Microsoft ASP.NET AJAX 프레임워크를 사용했습니다. 이 프레임워크는 Visual Studio® AJAX 웹 사이트 템플릿을 제공하기 때문에 매우 쉽게 사용할 수 있습니다. 이 템플릿을 선택하면 필요한 어셈블리 참조가 웹 응용 프로그램 프로젝트에 추가됩니다. 지도 응용 프로그램에서 AJAX 기능을 사용하기 위해 소스 파일에 다음 태그를 추가했습니다.

<asp:ScriptManager ID="sm" runat="server" />

이후 다음과 같이 ASP.NET AJAX UpdatePanel 컨트롤에 Image 컨트롤(비동기 요청-응답 후에 업데이트할 컨트롤)을 래핑했습니다.

<asp:UpdatePanel ID="up1" runat="server">
  <ContentTemplate>
    <asp:Image ID="Image1" runat="server" ImageUrl="~/5.JPG" 
               (other attributes omitted) />
  </ContentTemplate>
  <Triggers>
    <asp:AsyncPostbackTrigger ControlID="Button1" EventName="Click" />
    <asp:AsyncPostbackTrigger ControlID="Button2" EventName="Click" />
  </Triggers>
</asp:UpdatePanel>

여기까지가 전부입니다! XMLHTTP 개체 만들기, 비동기 응답 수신, 오류 처리, 브라우저 간 차별화와 같은 복잡한 세부 사항은 모두 프레임워크에서 자동으로 처리합니다.

4개 방향 컨트롤 모두가 아니라 Button1(North) 및 Button2(South) 컨트롤에 대한 클릭 이벤트에만 AJAX 비동기 요청/응답을 설정했다는 점을 유의하십시오. 이번에 소개한 테스트 기법이 비동기 요청과 동기 요청에 모두 적용된다는 점을 보여 주기 위해 이렇게 처리했습니다. 이 기사에 포함된 코드 다운로드를 사용하여 해당 응용 프로그램을 직접 시험해 보면 North/South 요청(AJAX)과 East/West 요청(비 AJAX) 간에 상당한 성능 차이가 있음을 알 수 있습니다.

대부분의 기존 테스트 자동화 기법은 AJAX 웹 응용 프로그램에서 작동하지 않습니다. 웹 응용 프로그램 기능을 테스트하는 가장 기본적인 방법은 사용자 입력으로 생성된 요청에 해당하는 HTTP 요청을 프로그래밍 방식으로 웹 서버에 보내고 HTTP 응답을 가져온 다음 해당 응답을 검사하여 성공/실패 결과를 확인하는 것입니다. AJAX 응용 프로그램에서는 특수한 XMLHTTP 요청을 사용하기 때문에 이 방식이 통하지 않습니다.

또 다른 일반적인 방식은 JavaScript로 Internet Explorer DOM을 조작하여 웹 서버로 요청을 보내고 onload 이벤트(클라이언트에서 해당 응답이 수신 및 로드되었음을 알림)가 발생할 때까지 대기한 다음 JavaScript와 Internet Explorer DOM을 사용하여 웹 페이지의 새 상태를 검사하여 성공/실패 결과를 확인하는 것입니다. 이 방식의 문제는 AJAX가 비동기로 작동하므로 클라이언트에서 응답을 수신했음을 알리는 onload 이벤트를 사용할 수 없다는 점입니다. 그러나 이 방식을 수정하여 AJAX 응용 프로그램의 간단한 테스트 자동화 프로그램을 작성할 수 있습니다. 응답 수신을 알리는 onload 이벤트를 사용하는 대신에 응용 프로그램에서 예상되는 상태 변화를 감시한 다음 callback 함수로 컨트롤을 이전하는 코드를 작성하면 됩니다.


테스트 자동화

그림 2의 스크린샷에서 볼 수 있는 AJAX 응용 프로그램 테스트 프로그램 시스템은 3개 파일로 구성됩니다. 그림 4는 이 프로그램의 전체 구조에 대한 블록 다이어그램을 보여 줍니다. TestHarness.aspx 프로그램 자체는 2개의 프레임으로 구성되는 간단한 웹 페이지입니다. 오른쪽 프레임에는 테스트 대상 AJAX 웹 응용 프로그램이, 왼쪽 프레임에는 테스트 대상 웹 응용 프로그램을 조작하고 검사하기 위해 최소한의 UI와 모든 JavaScript 코드가 포함된 테스트 시나리오 페이지가 있습니다. 테스트 프로그램과 테스트 시나리오 페이지에 기본 HTML을 사용할 수도 있지만 필자는 일관성을 위해 3개 페이지를 모두 .aspx 파일로 만들기로 했습니다. TestHarness.aspx 페이지의 전체 소스는 다음과 같습니다.

<html>
<head>
  <title>Test Harness for AJAX Web Apps</title>
</head>
  <frameset cols="45%,*">
    <frame src="http://localhost/AjaxTest/TestScenario001.aspx"
      name="leftFrame">
    <frame src="http://localhost/AjaxApplication/Default.aspx"
      name="rightFrame">
  </frameset>
</html>

사용자 삽입 이미지
그림 4
 테스트 프로그램 구조

여기서 볼 수 있듯이 기본 테스트 프로그램 페이지는 하나의 컨테이너 파일일 뿐입니다. 테스트 시나리오는 asyncCall 프로그램 정의 함수를 호출하여 시작됩니다. 그러면 테스트 대상 응용 프로그램은 웹 서버로 비동기 XMLHTTP 요청을 보내고 delay 프로그램 정의 함수도 호출합니다. delay 함수는 웹 서버에서 해당 요청을 처리하고 비동기 응답을 반환하는 동안 순환하면서 웹 응용 프로그램 상태를 빈번히 조사합니다. 클라이언트에서 응답이 수신되고 응용 프로그램 상태가 업데이트된 후에는 delay 함수가 웹 페이지 상태 변화를 찾으며 새 비동기 호출이 수행될 수 있습니다.


asyncCall 내부

AJAX 테스트 자동화 기법의 핵심은 함께 작동하는 한 쌍의 프로그램 정의 함수, 즉 asyncCall과 delay입니다. asyncCall 메서드는 다음과 같습니다.

function asyncCall(action, checkFunc, arg, callback, pollTime)
{
  numTries = 0;
  action();
  window.setTimeout("delay(" + checkFunc + ", " + "'" + arg +
    "'" + ", " + callback + ", " + pollTime + ")", pollTime);
}

여기서는 5개의 인수를 전달합니다. 첫 번째 action 매개 변수는 Internet Explorer DOM을 사용하여 비동기 XMLHTTP 요청을 시작하는 동작을 실행하는 특정 루틴에 대한 함수 포인터입니다. 두 번째 checkFunc 매개 변수는 웹 응용 프로그램 상태가 비동기 응답이 완료되었음을 나타내는 경우 true를 반환하는 루틴에 대한 함수 포인터입니다. 세 번째 arg 매개 변수는 checkFunc 함수로 전달되는 인수입니다. 네 번째 callback 매개 변수는 비동기 응답이 완료되었을 때 호출할 루틴에 대한 함수 포인터입니다. 마지막 pollTime 매개 변수는 delay 파트너 함수의 호출 간에 대기할 시간(밀리초)을 지정합니다.

asyncCall 함수 내에서 먼저 numTries 전역 카운터를 0으로 설정합니다. 비동기 응답이 발생했음을 나타내는 상태를 찾기 위한 시도를 지정한 횟수만큼 수행한 후 종료할 수 있도록 이 변수를 사용하여 delay 함수를 시작한 횟수를 추적합니다. 그 다음 action 인수를 호출하여 비동기 요청이 발생되도록 합니다. 함수 이름에 붙는 괄호는 함수를 호출하는 구문 체계입니다.

진짜 트릭은 지금부터입니다. 내부 window.setTimeout 함수를 호출합니다. setTimeout 함수는 두 가지 인수를 받습니다. 첫 번째 인수는 한 번만 실행하기 위한 JavaScript 문이고, 두 번째 인수는 첫 번째 인수를 실행하기 전에 대기하는 시간(밀리초)입니다. asyncCall 함수의 코드를 보면 setTimeout에 대한 첫 번째 인수가 다음과 같이 확인됨을 알 수 있습니다.

delay(checkFunc, 'arg', callback, pollTime)

두 번째 인수는 pollTime입니다. 간단히 말해서 pollTime(밀리초)만큼 대기한 후에 delay 프로그램 정의 함수를 호출하는 것입니다.

asyncCall 함수 호출은 다음과 같습니다.

asyncCall(clickNorth, imgIsTwo, "2", clickSouth, 200);

이 호출은 "clickNorth라는 함수를 호출한 다음 시간 지연 루프로 이동하여 200밀리초마다 '2' 인수로 imgIsTwo 함수를 호출하고, 최종적으로 이 함수에서 true를 반환하면 clickSouth라는 함수로 컨트롤을 이전한다"로 해석될 수 있습니다.

asyncCall 메서드의 정의에서 arg를 둘러싼 작은따옴표 문자가 중요합니다. asyncCall 정의에 작은따옴표가 없으면 해당 매개 변수는 다음과 같이 참조로 전달됩니다.

asyncCall(doThis, findX, "X", doThat, 200);

이러한 호출은 X 변수가 정의되지 않았다는 오류를 일으킵니다. 작은따옴표가 있으면 이 시나리오에 맞게 값에 따라 해당 매개 변수가 전달됩니다.

asyncCall 내부에서 window.setTimeout 함수를 호출하는 흥미로운 다른 방법도 있습니다. 일반적으로는 다음과 같이 작성합니다.

window.setTimeout("delay(" + checkFunc + ", " + "'" + arg +
    "'" + ", " + callback + ", " + pollTime + ")", pollTime);

이 대신 JavaScript의 익명 함수 기능을 사용하여 다음과 같이 작성할 수 있습니다.

window.setTimeout(
    function(){delay(checkFunc, arg, callback, pollTime);}, 
    pollTime);

따옴표 문자와 문자열 연결이 제거되었으므로 이 코드는 비익명 방식보다 조금 더 깔끔합니다. 또한 비익명 방식의 경우 브라우저가 새 스크립트 환경을 만들어 해당 스크립트를 처리해야 하므로 이 코드가 조금 더 효율적입니다. 그러나 arg 인수와 checkFunc 인수를 구분하는 작은따옴표 문자가 없으면 arg 인수가 값에 따라 전달되는지가 명확하게 드러나지 않습니다. 간단한 주석을 첨부하면 이로 인한 혼란을 방지할 수 있을 것입니다.


delay 함수의 내부

asyncCall 함수의 파트너인 delay 함수는 다음과 같습니다.

function delay(checkFunc, arg, callback, pollTime)
{
  ++numTries;

  if (numTries > maxTries) finish();
  else if (checkFunc(arg)) callback();
  else window.setTimeout("delay(" + checkFunc + ", " + "'" + arg +
      "'" + ", " + callback + ")", pollTime);
}

delay 함수는 호출하는 asyncCall 함수로 전달된 마지막 4개 인수와 정확히 일치하는 4개 인수를 받아들입니다.

delay 함수 내에서 먼저 numTries 전역 카운터를 증분합니다. 이 카운터는 delay 함수를 시작한 횟수를 추적하거나 비동기 응답의 완료 여부를 알기 위해 웹 응용 프로그램 상태를 확인한 횟수를 추적하는 데 사용됩니다. 이 전역 카운터가 전역 maxTries 상수보다 크면 웹 응용 프로그램이 서버측에서 제한 시간을 초과했거나 XMLHTTP 응답이 잘못된 것입니다. 따라서 상황을 파악하기 위해 finish 함수로 컨트롤을 이전합니다.

지연이 제한 시간을 초과하지 않은 경우 checkFunc 함수를 호출하여 올바른 응답을 나타내는 조건이 true인지 테스트합니다. checkFunc 함수에서 true를 반환하면 callback 함수를 호출하여 테스트 시나리오를 계속 실행할 수 있습니다. 그러나 checkFunc 함수에서 false를 반환하면 계속 대기하다가 내부 setTimeout 함수를 다시 호출하여 pollTime(밀리초) 동안 대기한 후 delay 함수를 다시 호출합니다.

delay 함수는 자체를 직접 호출하지 않으므로 재귀적이라고 하긴 어렵지만 setTimeout 함수를 통해 간접적으로 자체를 호출하므로 자체 참조적입니다. 이 함수의 실제적 효과는 컨트롤을 finish 함수(지연 루프에서 최대 횟수를 초과한 경우)나 callback 함수(웹 응용 프로그램 상태가 true가 된 경우)로 이전하는 대기 루프입니다. 이 방식은 결과만 놓고 보면 명확하지만 해결 방법을 직접 확인하기 전에는 명확하게 보이지 않습니다. 물론 다른 방식들도 있지만 이 방법이 실제로 간단하고 효과적이라는 점이 입증되었습니다.

asyncCall 함수에서와 마찬가지로 명명된 함수 인수를 사용하여 window.setTimeout 함수를 호출하는 대신 다음과 같이 익명 함수 기능을 사용할 수 있습니다.

window.setTimeout(
    function(){delay(checkFunc, arg, callback);}, 
    pollTime);

테스트 페이지 작성

asyncCall 및 delay 함수가 처리되므로 이제 테스트 시나리오 페이지를 작성할 수 있습니다. 테스트 시나리오의 전체 구조는 그림 5에 나와 있습니다.

테스트 시나리오 페이지의 <body> 섹션은 제목, 주석을 표시하기 위한 ID "comments"가 있는 textarea, 자동화를 시작하기 위한 단추 컨트롤 등으로 구성됩니다. <head> 섹션에는 모든 JavaScript 코드가 포함됩니다. delay 함수를 시작하는 최대 횟수를 지정하기 위해 전역 maxTries 상수를 선언하여 초기화하고 비동기 응답이 완료되었음을 알리는 조건을 확인합니다. numTries 전역 변수는 delay 함수를 시작한 횟수를 추적합니다. polling 전역 상수는 delay 함수에 대한 연속 호출 간 지연 시간을 설정합니다.

테스트 대상 AJAX 웹 응용 프로그램의 복잡성에 따라 maxTries 및 polling 값을 수정해야 할 수도 있습니다. 여기서는 호출 간 200밀리초 지연 시간으로 10회이므로 총 2초인데, 이는 웹 서버에서 XMLHTTP 요청을 처리하고 클라이언트에 응답을 반환하기에 충분하지 않을 수 있습니다.

테스트 시나리오는 runTest 함수를 호출하는 것으로 시작됩니다.

function runTest()
{
  try
  {
    logRemark("Test Scenario #001");
    logRemark("Starting test run\n");
    step1(); // starting at 5, go N to 2
  }
  catch(ex) { logRemark("Fatal error: " + ex); }
  }
}

여기서는 logRemark 프로그램 정의 함수로 프로그램 본문의 <textarea>에 몇 가지 메시지를 표시하고 step1 함수로 컨트롤을 이전하기만 하면 됩니다. 테스트 실행 중에 발생되는 예외가 포착되도록 간단한 try/catch 블록에 코드를 래핑했습니다. 필자가 사용하는 logRemark 함수는 다음과 같이 매우 간단합니다.

function logRemark(comment)
{
  var currComment = document.all["comments"].value;
  var newComment = currComment + "\n" + comment;
  document.all["comments"].value = newComment;
}

여기서는 ID가 "comments"인 <textarea> 요소의 현재 내용을 검색하고 현재 내용에 줄바꿈 문자와 새로운 주석 텍스트를 추가하며 <textarea> 내용을 업데이트된 주석 집합으로 대체합니다. 효율적이지는 않지만 간단한 테스트 자동화에서는 단순성이 효율성보다 더 중요한 고려 사항인 경우가 많습니다.

step1 함수는 다음과 같이 테스트 대상 AJAX 웹 응용 프로그램의 조작을 시작합니다.

function step1()
{
  logRemark("Clicking North, waiting for '2'");
  asyncCall(clickNorth, checkImageSrc, "2", step2, polling);
}

주석을 기록한 다음, 이번 테스트 자동화의 핵심인 asyncCall 함수를 호출합니다. 이 호출은 clickNorth 함수를 호출한 후 200밀리초 간격으로 확인하면서 checkImgSrc("2")에서 true를 반환할 때까지 시간 지연 루프를 호출한 다음 step2 함수로 컨트롤을 이전하는 역할을 합니다. 시간 지연 루프 호출이 10회(maxTries)를 초과하면 컨트롤이 finish 함수로 이전됩니다.

clickNorth 함수는 다음과 같이 매우 간단합니다.

function clickNorth()
{
  var btnNorth = parent.rightFrame.document.all["Button1"];
  if (!btnNorth) throw "Did not find btnNorth";
  btnNorth.click();
}

여기서는 Internet Explorer DOM을 사용하여 Button1 컨트롤에 대한 참조를 가져온 다음 click 메서드를 호출합니다. 자동화 프로그램과 AJAX 응용 프로그램이 각각 다른 프레임에 있기 때문에 테스트 스크립트에서 응용 프로그램의 컨트롤에 액세스하려면 상위 키워드를 사용하여 한 수준 "위로" 이동한 다음 응용 프로그램 컨테이너의 프레임 ID를 사용해야 합니다. 필자는 document.all 컬렉션을 사용하여 웹 페이지 컨트롤에 대한 참조를 가져오는 것을 선호하지만 다음과 같이 getElementById 메서드를 사용하는 것을 선호하는 사람들도 있습니다.

var btnNorth = parent.rightFrame.document.getElementById("Button1");

checkImageSrc 함수는 테스트 대상 AJAX 응용 프로그램의 Image1 컨트롤이 비동기 응답으로 업데이트되면 이를 자동화 프로그램에 알립니다.

function checkImageSrc(target)
{
  try
  {
    var s = parent.rightFrame.document.all["Image1"].src;
    return s.indexOf(target) >= 0;
  }
    // traps case where Image1 is not yet loaded
    // logRemark("Error in checkImageSrc(): " + ex);
  catch(ex) { return false; }
}

여기서는 속성에 대상 문자열이 있는지 확인하기 위해 기본적으로 Image1 컨트롤의 src 속성만 검사합니다. 예를 들어 지도 2에 사용한 파일 이름인 "~/2.jpg"와 같이 "2"가 이미지 src 속성에 있으면 checkImageSrc("2") 호출은 true를 반환합니다. 자신만의 AJAX 자동화 프로그램을 작성할 때는 적절한 확인 함수를 만들어야 합니다. 예를 들어 웹 응용 프로그램에서 ID가 "TextBox1"인 TextBox 컨트롤을 업데이트한다고 가정해 봅시다. 이때 사용할 수 있는 확인 함수는 다음과 같습니다.

function checkTextBoxValue(target)
{
  try
  {
    var s = parent.rightFrame.document.all["TextBox1"].value;
    return s.indexOf(target) >= 0;
  }
    // traps case where TextBox1 is not yet loaded
    // logRemark("Error in checkTextBoxValue(): " + ex);
  catch(ex) { return false; }
}

checkImageSrc 함수에는 try/catch 블록이 있습니다. try/catch는 대부분 예기치 않은 오류 조건을 포착하는 데 사용되지만 여기서는 일반 함수 논리의 일부로 이 블록을 사용하고 있습니다. 이는 웹 페이지를 다시 그리는 동안에는 Image1 컨트롤에 대한 참조를 얻을 수 없으며 이를 시도할 경우 예외가 발생한다는 개념입니다. 이는 반드시 웹 응용 프로그램의 상태가 잘못되었음을 의미하지는 않으며 단지 불완전한 상태임을 의미할 수도 있으므로 예외를 포착하여 false를 반환하려는 것입니다. logRemark 호출에 대한 주석을 풀고 폴링 시간을 매우 짧게 하면(예: 10밀리초) 이 동작을 관찰할 수 있습니다.

try/catch를 일반 함수 논리의 일부로 사용하는 것은 대개 좋지 않은 코딩 스타일로 간주되지만, 여기에서는 단순함으로 얻는 이득이 try/catch를 일반 논리 흐름에 사용할 이유가 되기에 충분합니다.

이 테스트 시나리오 스크립트에서는 step2, step3, step4 및 step5 함수를 step1 함수와 거의 동일하게 정의합니다. step2 함수는 clickEast 함수를 호출하고 "3"이 지도 표시 영역에 나타날 때까지 대기한 다음 step3 함수를 호출합니다. step3 함수는 clickSouth 함수를 호출하고 "6"이 나타날 때까지 대기하며, 나머지 함수들도 같은 방식입니다. step5 함수는 다음과 같이 간략한 중간 단계의 finish 함수인 step6을 호출합니다.

function step6()
{
  finish();
}

여기서는 컨트롤을 실제 finish 함수로 이전합니다(그림 6 참조). finish 함수는 delay 함수 호출에 허용된 최대 횟수를 초과한 상황도 처리한다는 점을 상기하십시오.

먼저 numTries 전역 카운터가 maxTries 전역 상수를 초과한 상황을 처리합니다. 이러한 상황이 발생하는 경로는 두 가지입니다. AJAX 응용 프로그램의 논리는 올바르지만 응용 프로그램에서 페이지 상태를 업데이트하는 데 너무 많은 시간을 소모하는 경우나 응용 프로그램의 논리가 잘못되어 확인 함수에서 정확한 상태를 확인하지 못하는 경우입니다. 여기서는 테스트 시나리오 실패에 대한 간단한 해결 방안을 임의로 선택했습니다.

finish 함수의 두 번째 논리 분기는 응용 프로그램의 최종 상태를 확인하여 성공/실패 결과를 판단합니다. 여기서는 "5"에서 시작한 다음 North, East, South, South 및 West 컨트롤을 연속하여 클릭하는 경우 최종 지도 이미지가 "8"이 맞는지 확인합니다.


테스트 프로그램 확장

여기서 제시하는 테스트 자동화 시스템은 매우 간단하며 사용자가 수정할 수 있도록 설계되었습니다. 보통 Magazine 기사와 마찬가지로 이 기사에서도 기본 개념에 집중하기 위해 대부분의 오류 확인 과정을 생략했습니다. 그러나 사용자는 오류 트랩을 자유롭게 추가하고 싶을 것입니다. 테스트 자동화는 본질적으로 다루기 까다로우며 오류는 예외보다는 규칙에 가깝습니다.

테스트 프로그램은 완전하게 자동화되지는 않은 상태입니다. 자동화 프로그램을 시작하려면 Run Test 단추를 직접 클릭해야 합니다. 프로그램을 완전하게 자동화할 수 있는 방법에는 여러 가지가 있습니다. 한 가지 간단한 방법은 다음과 같이 onload 처리기를 테스트 프로그램 프레임에 추가하는 것입니다.

<frame src="http://localhost/AjaxApplication/Default.aspx"
  name="rightFrame"
  onload="leftFrame.launch();" >

이 경우 이 테스트 시나리오 코드의 launch 함수는 다음과 같이 정의됩니다.

var started = false;
function launch()
{
  if (!started) runTest();
}

또한 이 칼럼에서 설명한 runTest 함수를 다음 문으로 시작하도록 편집합니다.

started = true;

테스트 대상 AJAX 웹 응용 프로그램이 포함된 오른쪽 프레임이 로드되면 launch 함수가 호출됩니다. launch가 처음 실행되면 started 전역 변수가 false가 되며 컨트롤이 runTest 함수로 이전됩니다 그러면 runTest 함수에서 started 변수를 true로 설정한 다음 자동화 프로그램을 실행합니다. 이후 정기적인 HTTP 요청/응답 동작에 대해 웹 응용 프로그램의 후속 페이지가 로드되며 launch 함수가 호출되지만 started 전역 변수가 true이므로 컨트롤은 runTest 함수로 다시 이전되지 않습니다. 완전히 자동화된 테스트 프로그램을 사용하면 여러 개의 프로그램 페이지와 시나리오 페이지를 만든 후, 다음과 같은 문을 BAT 파일에 배치하고 Windows 작업 스케줄러를 통해 자동화 프로그램을 시작하여 여러 시나리오를 실행할 수 있습니다.

iexplore http://localhost/AjaxTest/TestHarness01.aspx
iexplore http://localhost/AjaxTest/TestHarness02.aspx

필자의 간단한 시스템에서는 시스템을 편리하게 매개 변수화할 수 없다는 것이 단점입니다. 테스트 시나리오 페이지의 일반 JavaScript 코드를 별도의 단일 .js 파일 안에 쉽게 넣을 수 있지만 단일 테스트 프로그램 페이지에서 여러 테스트 시나리오를 실행하는 작업을 편리하게 조율할 방법이 없습니다. 즉, 불가능한 것은 아니지만 쉬운 작업이 아닙니다.

AJAX 테스트 자동화 프로그램의 또 다른 확장은 테스트 시나리오 결과 저장 프로세스를 자동화하는 것입니다. 여기에 제시된 시스템에서는 결과를 기록하지 않지만 여러 가지 방법으로 기록할 수 있습니다. 한 가지 방식은 다음과 같이 Form 요소를 테스트 시나리오 페이지에 배치하고 테스트 시나리오 결과를 보관하는 텍스트 필드를 폼 안에 넣은 후 다음과 같이 서버로 결과를 게시하는 것입니다.

<form name="resultForm" method="Post" action="saveResults.aspx">
  <p>Result: <input type="text" name="result"></p>
  <p><input type="submit" name="saver" value="Save Results"></p>
</form>
finish 함수 내에서 이 코드 행을 따라 result 필드에 pass/fail 값을 제공할 수 있습니다.
if (is.indexOf("8") >= 0)
{
  logRemark("\n*Pass*");
  theForm.result.value = "Pass";
}
else
{
  logRemark("\n*FAIL*");
  theForm.result.value = "Fail";
}

이제 Save Results 단추를 클릭하여 시나리오 결과를 수동으로 저장하거나 시나리오 코드에 다음 코드를 넣어 저장 프로세스를 자동화할 수 있습니다.

document.all["theForm"].submit();

요약

필자가 제시한 테스트 프로그램은 AJAX 웹 응용 프로그램을 테스트하는 여러 가지 방법 중 하나일 뿐입니다. 이 칼럼을 읽고 나면 필자의 간단한 방식은 물론 이보다 정교한 프로그램을 사용한 AJAX 웹 응용 프로그램 테스트도 별 어려움 없이 수행할 수 있을 것입니다. ASP.NET AJAX 프레임워크의 릴리스에 힘입어 AJAX 웹 응용 프로그램의 확산 속도도 더 빨라질 것으로 예상됩니다. 간단한 테스트 자동화 프로그램을 작성할 수 있는 능력의 중요성도 점차 높아지고 있는 만큼 여러분의 기술 지식에 유용한 도구로 추가될 것입니다.

2007/02/12 21:06 2007/02/12 21:06

댓글0 Comments (+add yours?)

트랙백0 Tracbacks (+view to the desc.)

창의적인 코딩

View Comments

출처 : MSDN Magazine "창의적인 코딩"


1920년대에  할리우드는 곤경에 처했습니다. 스캔들로 얼룩지고 영화는 섹스와 폭력이 난무하여 정부가 개입할 수도 있는 위험 수준에 이르렀습니다. 영화관들은 Hays Office를 만들어 자체 감사를 벌이고 실버 스크린을 정리하는 방식으로 대처했습니다. Hays Office는 적합한 언어에서 적절한 감정 표현에 이르기까지 모든 사항을 규제하는 규칙을 제정하고 비정부 영화 감독 기관의 중추적인 역할을 수행했습니다.

1930년대 중반, 영화 작가와 감독들은 창의적인 방식을 찾아냄으로써 이러한 제약에 대처했습니다. 이러한 해결책 중 하나는 로맨스, 희극, 풍자가 결합된 스크루볼 코미디라 불리는 새로운 장르였습니다. 스크루볼 코미디에서는 외설적인 키스 대신 로맨스가 영화의 상당 부분을 차지했으므로 문제되지 않았으며 또 다른 장점은 대화가 너무 빠르고 우회적이어서 감시인이 들을 수 없다는 것이었습니다. 그 결과 작가와 감독이 원하는 외설적인 내용을 자유롭게 표현할 수 있었다면 절대로 나올 수 없었던 It Happened One Night, Twentieth Century, Bringing Up Baby와 같은 고전 영화가 만들어졌습니다.

자유가 무제한 보장되는 상황이 아니라 엄격한 제약이 따르는 상황에서 창의성이 꽃을 피우는 경우가 자주 있습니다. 예술가는 물론 엔지니어도 이러한 상황에 익숙합니다. 아폴로 13호가 달에 가는 도중에 산소 탱크가 폭발했을 때 이 문제를 해결하기 위해 뛰어난 창의성과 천재성이 요구되었으며 결국 이러한 창의성은 수중에 있는 제한된 자료에서 나왔습니다.

필자는 최근에 지금까지 경험한 것 중에서 가장 난해하고 흥미로운 프로그래밍을 수행하면서 이러한 모순을 상기했습니다. 이때는 적합한 프로그래밍 언어를 사용하고 있지도 않았습니다. 나중에 필자는 Microsoft® Windows® Presentation Foundation의 중요한 부분을 차지하는 XAML(Extensible Application Markup Language)로 이 프로그래밍을 진행하게 되었습니다.

XAML을 사용하면 레이아웃을 표현하고 그래픽과 애니메이션을 표시하는 Windows Presentation Foundation의 강력한 클래스에 액세스할 수 있습니다. XAML은 선언적 프로그래밍 언어로 분류될 수도 있습니다. 그러나 XAML은 보다 친숙한 프로그래밍 언어와 비교하면 기본적인 프로그래밍 기능조차 없는 언어임이 사실입니다. 루프와 조건이 없는 것은 그렇다 치고 숫자를 더하거나 곱하는 방법조차 없습니다.

그러나 XAML의 제한된 기능만으로 문제를 해결하고 "XAML 혹성에서 살아남기"위해 필자는 더욱 창의적으로 생각했습니다. 필자는 XAML로 행렬을 곱하는 복합 그래픽 변환을 정의하여 이를 통해 필요한 모든 곱셈 및 덧셈 기능을 제공할 수 있다는 사실을 알게 되었으며 ListBox를 사용하여 여러 요소를 보관한 다음, 데이터 바인딩을 통해 해당 요소를 인덱싱하는 방식으로 XAML에서 배열을 시뮬레이트할 수 있다는 것도 발견했습니다.

필자의 가장 만족스러운 성과는 XAML 시계 응용 프로그램입니다. 시계를 표시하기 위해 원형으로 눈금이 필요했습니다. 일반적으로 시계에는 12개의 큰 눈금과 48개의 작은 눈금이 있지만 for 루프를 사용하지 않을 경우 이러한 눈금을 만들려면 60개의 별도 XAML 요소가 필요합니다. 그러나 이러한 반복 작업을 수행하기 위해 시간을 허비할 수는 없었습니다. 이 문제로 며칠 동안 고심한 결과 드디어 해결책을 찾아냈습니다. 점선을 사용하여 두 개의 원을 그리는 방식으로 눈금을 정교하게 만들 수 있었습니다. 문제를 해결한 것은 물론이고 대부분의 다른 시계 응용 프로그램처럼 60개가 아니라 두 개의 그래픽 개체만 사용하여 작업을 마쳤습니다.

XAML 작성은 재미있지만 난해하며 지적 능력을 확장하기도 합니다. 그러나 몇몇 사람들은 XAML 작성이 정도를 벗어난 것이라고 생각합니다. XAML은 원래 사람이 작성하기 위한 것이 아니기 때문입니다. "XAML은 도구를 위한 것이다"라는 한 Microsoft 블로거의 주장이나 XAML 릴리스 몇 개월에 전에 '도구가 아니라 사람을 위한 것'이라는 이유로 일부 XAML 기능이 제거된 점을 생각하면 이를 쉽게 알 수 있습니다. 물론 Visual Studio®와 Microsoft Expression® Interactive Designer와 같은 XAML 생성 도구도 창의성을 자극할 수 있지만 코드 작성을 위한 창의성에는 도움이 되지 않습니다.

미래의 프로그래머는 XAML 구문을 배우기 위해 더욱 노력할까요? 아니면 단지 Visual Studio가 단추와 콤보 상자의 레이아웃을 저장하기 위해 생성하는 "신기한 XML 기능"으로 취급할까요?

대화식 디자이너와 코드 생성기는 프로그래머의 무기고라 할 수 있습니다. 이러한 기능을 통해 많은 시간을 절약할 수 있기 때문입니다. 그러나 우리가 누구인지는 잊지 맙시다. 우리는 프로그래머입니다. 우리에게는 강력한 코드를 작성할 수 있는 능력이 있습니다. 최소의 코드를 사용하여 최대의 효과를 거둘 수 있어야 합니다. 창의력을 발휘한다면 XAML과 같은 언어를 사용하더라도 애초에 의도된 것과는 전혀 다른 뛰어난 결과를 만들 수 있을 것입니다.

2007/02/12 20:51 2007/02/12 20:51

댓글0 Comments (+add yours?)

트랙백0 Tracbacks (+view to the desc.)

Newer Entries Older Entries