2007/03/23 23:25

출처 :MSDN Magazine

사용자 삽입 이미지
Get the sample code for this article.


사용자 삽입 이미지
목차


ASP.NET 2.0에는 여러 가지 데이터 바인딩 시나리오에서 프로세스를 대폭 단순화할 수 있는 다양한 데이터 바인딩 기능이 새로 추가되었습니다. 그러나 매우 일반적인 시나리오인 단순 컨트롤 데이터 바인딩은 별로 달라지지 않았습니다. 이는 폼 기반 웹 응용 프로그램에서는 아주 흔한 시나리오인데, 여전히 대부분의 프로세스가 수동으로 진행되며 상당한 시간이 소요됩니다.

단순 컨트롤 데이터 바인딩이란 컨트롤의 속성에 단일 값을 바인딩하는 프로세스를 의미합니다. 예를 들어 텍스트 상자, 확인란, 라디오 단추, 목록 컨트롤에서 선택한 값 등의 폼 컨트롤을 개별 데이터 값이나 개체 값에 바인딩하는 것입니다. 이 경우 데이터 바인딩을 명확하게 처리하고 일관된 방법으로 오류를 감지하여 표시하는 방식을 사용하면 많은 이점을 얻을 수 있습니다. 이 기사에서는 데이터 또는 개체의 개별 값을 컨트롤 속성에 바인딩할 수 있는 사용자 지정 extender 컨트롤에 대해 설명합니다. extender는 필요에 따라 Web Form의 기존 컨트롤에 대한 디자이너 지원 기능을 제공하도록 확장할 수 있습니다

설명 과정에서 폼 기반 웹 인터페이스의 데이터 바인딩 및 바인딩 해제, 유효성 검사, 오류 처리 및 오류 표시 등에서 일관된 동작을 제공하는 유연하고 직관적인 구현 방법을 예제를 통해 살펴보겠습니다. 또한 컨트롤이 만들어지는 방법을 자세히 살펴보고 몇 가지 흥미로운 이점에 대해서도 설명하겠습니다.

wwDataBinder 컨트롤은 단순 컨트롤 데이터 바인딩을 처리합니다. 구체적으로 이 컨트롤은 "DataRow의 CompanyName 필드를 TextBox의 Text 속성에 바인딩"하거나 "Item.Entity.Pk 속성을 DropDown 목록의 SelectedValue 속성에 바인딩"하는 등의 개념을 나타내는 바인딩을 관리합니다. 즉, 지정한 데이터 값을 지정한 컨트롤 속성에 바인딩하는 프로세스뿐 아니라, 이 값을 다시 기본 데이터 항목으로 바인딩 해제하는 프로세스까지도 관리합니다. 바로 이것이야말로 진정한 양방향 데이터 바인딩입니다.


기본 데이터 바인딩의 문제점

ASP.NET은 기본적으로 인바운드 데이터 바인딩에 대한 지원을 적절한 수준으로 제공합니다. ASP.NET의 네이티브 데이터 바인딩 식은 <%# Eval("FieldName") %> 또는 <%# this.Item.Entity.Description %>과 같은 직접 페이지 수준 식을 사용하여 데이터를 컨트롤 속성에 바인딩합니다. 사용자는 페이지 또는 페이지의 컨트롤에 대해 DataBind 메서드를 호출하여 데이터 바인딩 프로세스를 시작할 수 있습니다. 인바운드 바인딩의 경우 이러한 프로세스가 문제 없이 작동합니다.

그러나 바인딩을 해제하여 데이터를 기본 데이터 필드 또는 속성으로 가져오기는 쉽지 않습니다. ASP.NET 2.0에는 GridView, FormView 및 DetailsView 컨트롤이 추가되었습니다. 이 컨트롤을 사용하면 양방향 데이터 바인딩을 제공하는 <%# Bind("FieldName") %> 구문을 사용할 수 있습니다. 그러나 이러한 컨트롤은 IEnumerable 기반이어야 하는 기본 데이터 원본 컨트롤과 밀접하게 연관되어 있기 때문에 개별 엔터티 개체 등에 대한 바인딩은 지원되지 않습니다.

기본 바인딩 해제 메커니즘의 가장 큰 문제점은 바인딩 해제 오류가 처리되는 방식이라 할 수 있습니다. 입력 필드의 형식에 문제가 있는 경우, 예를 들어 숫자 필드에 영문자가 있거나 개월 수를 22로 설정하는 DateTime 값이 있다면 값을 저장할 때 페이지 수준 예외가 발생합니다. 이 경우 페이지 수준 오류 처리를 통해 예외를 잡을 수는 있지만 첫 번째 예외로 인해 바인딩 해제 프로세스가 중지되므로 해당 정보를 요약할 수 없습니다. 유효성 검사기를 사용하여 오류를 사전에 방지할 수 있겠지만 만일 페이지에 2개 또는 3개의 오류가 있는 경우 이를 처리하기 위해 다시 게시를 여러 차례 수행해야 하며, 이는 UI 측면에서 바람직하지 않습니다.


양방향 데이터 바인딩 컨트롤 소개

보다 유연하고 논리적인 방식으로 단순 데이터 바인딩을 관리하기 위해 wwDataBinder 컨트롤을 만들었습니다. 이 컨트롤은 다음과 같은 여러 가지 유용한 기능을 제공합니다.

  • 명확한 양방향 데이터 바인딩 지원
  • 단일 개체 또는 여러 데이터 값을 컨트롤 속성에 바인딩
  • 개체 또는 데이터 값에 바인딩된 컨트롤 속성을 해제
  • 텍스트에서 형식이 지정된 데이터로의 데이터 변환 처리
  • BindingErrors 컬렉션의 모든 바인딩 오류 관리
  • 사용자 지정 유효성 검사 이벤트 지원
  • 입력에서 필요한 값 확인
  • 컨트롤 옆에 오류 아이콘 표시
  • 컨트롤에 대한 링크가 포함된 메시지 요약 표시
  • 프로그래밍 방식으로 바인딩 오류 추가

이 컨트롤을 사용하려면 ASP.NET 페이지에 갖다 놓기만 하면 됩니다. 이렇게 추가된 컨트롤은 폼에 있는 모든 기존 컨트롤의 extender 컨트롤 역할을 하며그림 1과 같이 Visual Studio®2005 속성 편집기에서 확장된 컨트롤에 DataBindingItem 속성을 추가합니다.

사용자 삽입 이미지

그림 1 데이터 바인딩 기능을 사용하여 컨트롤 확장 (더 크게 보려면 이미지를 클릭하십시오.)

그림 1의 아래쪽에서 볼 수 있듯이 이 컨트롤은 흐릿한 회색 컨테이너로 폼에 표시되며 런타임에 렌더링되지 않습니다. 폼에 표시되는 이유는 wwDataBinder 컨트롤 자체의 속성을 설정할 수 있도록 하기 위해서입니다.

extender 컨트롤인 wwDataBinder는 확장된 각 컨트롤의 구성 값이 들어 있는 DataBindingItems 컬렉션을 포함합니다. 이 항목 컬렉션은 HTML 태그로 유지 관리되며 컨트롤은 다음과 같습니다.

<ww:wwDataBinder ID="DataBinder" runat="server"            OnValidateControl="DataBinder_ValidateControl">   <DataBindingItems>      <ww:wwDataBindingItem runat="server"         ControlId="txtProductName"        BindingSource="Product.Entity"         BindingSourceMember="ProductName" IsRequired="True"         ErrorMessageLocation="RedTextAndIconBelow">      </ww:wwDataBindingItem>      ... more DataBindingItems   </DataBindingItems></ww:wwDataBinder>

이 컬렉션은 DataBinder에 의해 적용되는 개별 바인딩을 설정합니다. wwDataBinder는 기존 컨트롤을 확장하지만 폼에서 특정 컨트롤의 DataBindingItem extender 속성을 명시적으로 설정하기 전까지는 실제로 확장되지 않습니다.

디자인 모드에서 extender는 데이터 항목과 컨트롤 속성 간의 매핑을 제공하는 DataBindingItem 속성을 노출합니다. 이러한 매핑에서 지원되는 데이터 항목은 ADO.NET DataRow 필드와 같은 ADO.NET 데이터 개체입니다. 또는 Page 개체를 통해 참조할 수 있는 개체의 단순 속성과 개체 자체에 바인딩할 수도 있습니다. 예를 들어 this가 Page 개체(모든 바인딩 식이 시작되는 기본 개체)를 나타내는 this.Customer.Entity.CompanyName에 바인딩할 수 있습니다.

바인딩을 수행할 때에는 BindingSource를 사용하여 데이터 항목을 식별합니다. 이 BindingSource는 BindingSource(기본 개체를 설명하는 문자열 속성)와 BindingSourceMember(바인딩할 필드 또는 속성에 대한 매핑을 제공하는 속성)라는 두 가지 속성으로 구성됩니다. 프로그래밍 방식의 액세스에도 BindingSourceObject 속성이 사용될 수 있습니다. 이 경우에는 BindingSource 문자열 대신 참조를 제공하는 데 사용됩니다.

DataRow 개체에 바인딩하는 경우를 예로 들어 보겠습니다. wwDataBinder가 DataRow에 바인딩하려면 Page 참조를 통해 DataRow에 액세스할 수 있어야 this.MyDataRow가 작동합니다. 이 경우 다음 코드를 사용하여 바인딩합니다.

BindingSource: MyDataRowBindingSourceMember: CompanyName

여기에서 CompanyName은 바인딩할 필드의 이름입니다.

또한 this.Customer.DataRow가 작동하며 여기에서 Customer는 DataRow 속성이 있는 비즈니스 개체입니다. 다음과 같이 바인딩합니다.

BindingSource: Customer.DataRowBindingSourceMember: CompanyName

DataTable에는 같은 방법으로 바인딩할 수 없습니다. DataTable 또는 DataSet을 BindingSource 개체로 사용하면 wwDataBinder는 테이블의 첫 번째 DataRow 또는 첫 번째 테이블과 DataSet의 첫 번째 DataRow인 것으로 간주합니다. 다음과 같이 ADO.NET 개체를 보다 직접적으로 바인딩할 수도 있습니다.

BindingSource: MyDataSet.Tables["Products"].Rows[0]BindingSourceMember: CompanyName

wwDataBinder가 바인딩하려면 보호되거나 공개된 것으로 표시된 Page의 속성을 통해 바인딩하려는 개체를 참조할 수 있어야 합니다. 공개 리플렉션은 "보통 신뢰"에서 지원되므로 공개 속성을 더 선호하며, 보호된 속성의 경우에는 리플렉션이 실패합니다. 개인 속성은 바인딩에 사용할 수 없습니다.

ADO.NET 형식 외에 .NET BCL(Base Class Library)의 비즈니스 엔터티 또는 형식에도 바인딩할 수 있습니다. 예를 들어 this.Customer.Entity.CompanyName을 사용하여 CompanyName에 액세스할 수 있는 비즈니스 개체의 경우 BindingSource를 다음과 같이 표현할 수 있습니다.

BindingSource: Customer.EntityBindingSourceMember: CompanyName

또한 Page 개체 멤버에 직접 바인딩할 수도 있습니다. Page에 DateTime RightNow 속성이 있으면 다음과 같이 바인딩됩니다.

BindingSource: thisBindingSourceMember: RightNow

모든 BindingSource는 this 또는 me를 암시적으로 사용하여 Page 개체를 나타내지만 Page 자체를 바인딩 원본으로 사용하지 않는 한 반드시 이를 지정할 필요는 없습니다. BindingSource는 최상위 컨테이너 개체(일반적으로 ASP.NET Page 개체)에 상대적이지만 DataBind 및 Unbind 메서드의 매개 변수로 최상위 컨테이너를 제공할 수 있습니다. 이는 최상위 컨테이너가 페이지가 아니라 UserControl인 사용자 컨트롤에 wwDataBinder를 사용하는 경우에 유용합니다.

BindingSourceMembers는 항상 ints, strings, DateTime, bool 등의 단순 형식입니다. 또는 문자열로 변환되고 양방향 바인딩에 사용할 수 있는 Enum 값이 될 수도 있습니다.

BindingSource는 값을 바인딩할 컨트롤 속성과 데이터 항목(또는 속성) 간의 바인딩을 설정하는 역할을 합니다. 이러한 매핑은 컨트롤 속성에 바인딩하거나 컨트롤 속성에서 데이터 항목이나 속성으로 되돌리는 데 모두 사용됩니다. 인바운드 바인딩은 wwDataBinder.DataBind 메서드 호출을 통해 설정되고 바인딩 해제는 wwDataBinder.Unbind 호출을 통해 수행됩니다. 코드에서 메서드를 명시적으로 호출하기 때문에 이 프로세스는 명확하게 실행됩니다. 이는 바인딩 처리 방법 측면에서 상당한 유연성을 제공합니다.

DataBindingItem은그림 1과 같이 데이터 바인딩 프로세스와 관련한 추가 속성을 노출합니다. 이 BindingProperty는 사용자가 바인딩하는 컨트롤의 필드를 결정합니다. 기본 필드는 Text이지만 CheckBox에 바인딩하는 경우에는 Checked에 바인딩하고 목록 컨트롤에 바인딩하는 경우에는 SelectedValue에 바인딩하는 경우가 많습니다.

데이터 바인딩은 단방향, 양방향 또는 없음으로 설정할 수 있습니다. 일반적으로 텍스트 상자와 같은 입력 컨트롤에는 양방향 바인딩이 사용되는 반면 레이블과 같은 표시 컨트롤에는 단방향 바인딩이 사용됩니다. 없음 설정은 컨트롤이 바인딩 오류 관리에는 참여하지만 값이 바인딩되지는 않도록 하려는 경우에 유용합니다.

wwDataBinder 컨트롤은 바인딩 오류와 바인딩 해제 오류를 자동으로 관리합니다. 컨트롤을 바인딩하거나 바인딩 해제할 수 없는 경우 BindingErrors 컬렉션에 BindingError가 생성되지만 예외가 발생하지는 않습니다. 오류를 확인하려면 이 컬렉션에서 개별 바인딩 오류를 쿼리하고 ToHtml 메서드를 사용하여 오류에 대한 요약 정보를 검색하면 됩니다. 기본적으로 오류가 발생한 각 컨트롤의 옆에는 오류 아이콘이 표시됩니다.그림 2에는 몇 가지 바인딩 오류가 있는 폼의 예제가 나와 있습니다.

사용자 삽입 이미지

그림 2 바인딩 오류 표시 (더 크게 보려면 이미지를 클릭하십시오.)

BindingErrors는 바인딩 또는 바인딩 해제 프로세스 동안 컨트롤에 의해 자동으로 생성되거나 사용자가 작성한 코드를 통해 BindingErrors 컬렉션에 수동으로 추가할 수 있습니다. 자동 BindingErrors는 주로 데이터 변환 오류로 인해 입력 값을 바인딩된 데이터 항목 또는 속성으로 다시 변환할 수 없는 경우에 생성됩니다. 예를 들어그림 2의 Reorder Expected 날짜는 잘못된 날짜 값이므로 데이터 변환 오류로 인해 컨트롤에서 오류가 자동으로 캡처됩니다. 각 DataBindingItem에는 IsRequired 속성도 있습니다. IsRequired로 표시된 항목은 비워 둘 수 없습니다. 해당 항목을 비워 두면 자동으로 바인딩 오류가 생성되어 BindingErrors에 추가됩니다.

이 폼의 나머지 두 가지 오류는 수동 오류입니다. Dairy Products 오류는 wwDataBinder에 연결된 ValidateControl 이벤트를 통해 처리됩니다. 이 이벤트는 바인딩된 컨트롤마다 발생하므로 바인딩 해제된 후에 이벤트 처리기로 각 컨트롤을 검사할 수 있습니다. 코드의 값이 유효하지 않은 것으로 확인되면 BindingItem의 BindingErrorMessage를 프로그래밍 방식으로 설정하고 false를 반환하여 바인더에게 이 DataBindingItem에 오류가 있어 오류 목록에 표시해야 한다는 사실을 알릴 수 있습니다.

마지막 오류는 Page 코드에서 wwDataBinder.AddBindingError를 사용하고 오류 메시지와 Control 인스턴스 또는 ID를 지정함으로써 발생한 순전히 프로그램 오류입니다. 이러한 방법으로 데이터가 바운드된 컨트롤에 임의 오류 메시지를 할당하고 컨트롤에 아이콘을 표시하는 동시에 오류 메시지에도 컨트롤이 표시되도록 할 수 있습니다.

이러한 모든 기능은 완전한 프로그래밍 방식 및 자동 바인딩 오류 생성 메커니즘을 모두 제공하므로 오류 및 오류 아이콘 표시 방법을 유연하게 설정할 수 있습니다. 또한 컨트롤이 자동으로 페이지의 Validator 컨트롤, 오류 메시지, 컨트롤 ID를 선택하여 바인딩 오류 메커니즘에 통합합니다.

컨트롤에 오류가 있으면 기본적으로 해당 컨트롤 옆에 아이콘이 표시됩니다. 아이콘 표시는 옵션이며, DataBinder에 대해 전체적으로 경고 아이콘을 표시하지 않도록 설정하거나 ErrorMessageLocation 속성을 통해 개별 DataBindingItems의 아이콘 위치와 형식을 변경할 수 있습니다. 아이콘을 표시하는 데에는 기본적으로 WebResource가 사용됩니다. WebResource는 컴파일된 컨트롤 어셈블리에 포함된 이미지 리소스를 가리킵니다. 원하는 경우 wwDataBinder의 ErrorIconUrl 속성을 사용하여 이 이미지를 명시적으로 재설정할 수 있습니다.

어셈블리에는그림 2에 나와 있는 것과 같은 오류 요약을 표시하는 데 사용되는 wwErrorDisplay 컨트롤도 포함되어 있습니다. 이 컨트롤은 선택 사항이지만 필자의 경우 일관성을 위해 사용하는 경우가 많습니다. 예제에서는 BindingErrors.ToHtml 메서드의 문자열 출력을 text 속성에 할당했습니다. 컨트롤에는 ShowError 및 ShowMessage와 같은 간편한 메시지 표시 메서드도 있습니다. 이러한 메서드는 아이콘과 함께 한 줄의 메시지를 표시하여 Page에서 메시지가 일관되게 표시되도록 합니다.

오류 요약에는 표시되는 각 오류에 대한 링크가 포함되어 있습니다. 이 링크를 사용하여 오류가 발생한 개별 컨트롤을 손쉽게 찾아 바로 이동하고 수정할 수 있습니다.

앞서 소개한 폼을 실행하는 데 필요한 코드를 살펴보겠습니다. wwDataBinder 컨트롤에 사용된 패턴은 다음과 같은 방식으로 작동합니다. 바인딩하려는 경우 컨트롤 바인딩과 매핑 데이터를 컨트롤 속성에 연결합니다. 그런 다음 페이지 수준 개체 참조에 데이터를 로드하고 DataBind 메서드를 호출하여 폼의 컨트롤을 바인딩합니다.

바인딩을 해제하려는 경우에는 먼저 바인딩된 데이터 개체가 사용 가능하며 로드되어 있는지 확인합니다. 그리고 Unbind 메서드를 호출하여 컨트롤 값을 바인딩 해제하고 데이터로 되돌립니다. BindingErrors를 검사하고 사용자 지정 서버 측 유효성 검사를 처리하여 오류가 있으면 바인딩 오류 및 유효성 검사 오류를 표시합니다. 마지막으로 데이터를 저장합니다.

필자의 예제에서는 샘플 코드가 SQL 구문으로 복잡해지지 않도록내부 DataSet 개체를 로드하는 비즈니스 개체 몇 개만 사용하여 샘플 코드를 간단히 작성했습니다. 이러한 비즈니스 개체는 DataSet의 데이터를 반환합니다. 이러한 데이터 항목에 컨트롤을 직접 바인딩할 수 있으므로 편리하게 예제를 작성할 수 있었습니다.그림 3은 샘플 코드의 전체 페이지 코드를 보여 줍니다.

이 샘플 코드는 OnInit에서 시작하여 LoadLists 메서드를 호출함으로써 폼에 표시된 여러 목록을 로드합니다. 항목, 범주 및 공급자 목록은 목록 내용에 대해 모두 표준 목록 기반 바인딩을 사용하므로 이 코드는 wwDataBinder와 관련이 없습니다.

Item 비즈니스 개체는 Page의 주 개체입니다. this.Item.DataRow 또는 this.Item.Entity로 표현할 수 있는 개별 레코드에 바인딩하겠습니다. 여기에서 DataRow는 표준 ADO.NET DataRow이고 Entity는 각 필드마다 단일 속성이 있는 단순 관리 개체입니다.MSDN®Magazine웹 사이트에서 제공되는 이 기사의 다운로드 파일에는 두 가지 샘플 폼이 포함되어 있습니다. 하나는 DataRow에 대해 바인딩하고 다른 하나는 Entity를 바인딩합니다.

첫 번째 페이지 액세스의 Page_Load에서 Item.Load 메서드는 드롭다운 목록의 첫 번째 제품을 선택하여 사용합니다. Item.Load는 ProductId의 기본 키 매개 변수를 사용하고 단일 레코드가 있는 DataTable/DataRow를 로드합니다. 이러한 기능을 제공하는 비즈니스 개체는 Item.DataRow 속성을 노출합니다. Item.DataRow는 이 폼의 주 BindingSourceObject이므로 컨트롤이 ProductName, StockOnHand 등의 BindingSource Item.DataRow 및 BindingSourceMember에 바인딩됩니다. 항목이 로드되면 다음과 같이 컨트롤에 DataRow의 데이터를 바인딩할 수 있습니다.

this.Item.Load(FirstItemPk);this.DataBinde r.DataBind();

this.DataBinder에는 폼의 컨트롤을 바인딩하는 DataBindingItems의 컬렉션이 들어 있습니다.

DataBinder는 DataBindingItems 컬렉션을 순환하고 개별 DataBind 메서드를 호출하여 데이터 항목을 지정된 컨트롤 속성에 바인딩합니다. 폼의 일부 컨트롤은 목록 바인딩을 사용하여 채워지고 DataBinder에 의해 바인딩되는 드롭다운 목록 컨트롤입니다. DataBinder는 SelectedValue를 바인딩하여 목록에서 항목을 선택합니다.

데이터 바인딩은 IsPostBack이 false인 경우에만 발생하므로 사용자가 값을 변경하고 변경 내용을 저장한 후에는 값이 다시 바인딩되지 않습니다. 폼은 txtProductId_SelectedIndexChanged 이벤트 처리기에서 새 항목이 선택된 경우에만 다시 바인딩됩니다. txtProductId_SelectedIndexChanged 이벤트 처리기는 새 항목을 로드한 다음 DataBinder 속성에 DataBind를 로드하여 모든 컨트롤을 다시 바인딩합니다.

값을 다시 게시하지 않는 컨트롤(레이블 컨트롤 또는 기타 표시 전용 컨트롤)이 있는 경우 DataBinder.GetDataBindingItem(Controlid).DataBind를 명시적으로 호출하여 개별 항목을 바인딩할 수도 있습니다. 각 DataBindingItem은 자체적으로 바인딩 및 바인딩 해제 작업을 수행하는 메서드로, 이 메서드를 호출하면 명시적 바인딩이 트리거됩니다.

이 시점에서 페이지가 바인딩되며 사용자는 원하는 내용을 변경할 수 있습니다. 페이지에서 데이터를 저장할 시점이 되면 컨트롤 값을 바인딩 해제하여 기본 바인딩 원본으로 되돌릴 수 있습니다. 예제에서는 대부분의 컨트롤이 Item.DataRow 개체로 다시 바인딩되지만 단일 DataBinder를 사용하면 손쉽게 여러 개체에 바인딩할 수 있습니다. 각 DataBindingItem은 고유한 BindingSource를 결정합니다.

바인딩 해제의 첫 단계는 바인딩 해제할 개체가 올바른 컨텍스트에 있는지 확인하는 것입니다. 예제에서는 비즈니스 개체에 현재 로드된 항목에 대한 데이터가 있는지 확인해야 합니다. 또한 최초로 여는 경우에 대해서만(!PostBack) 폼을 바인딩했으므로 이제 DataBinder가 바인딩을 되돌릴 수 있도록 데이터를 다시 로드해야 합니다. 항목이 로드되면 DataBinder's Unbind 메서드를 호출하여 컨트롤 값을 데이터 항목 또는 속성으로 되돌릴 수 있습니다. 속성이 DataRow로 바인딩 해제되면 호출이 반환됩니다. 이때 데이터는 아직 저장되지 않은 상태입니다. 즉, 바인딩을 해제하면 데이터 값만 복원되고 데이터베이스로 바로 회수되지는 않습니다.

데이터를 저장하려면 먼저 BindingErrors를 확인하고 비즈니스 개체에 유효성 검사 오류가 없는지 확인해야 합니다. BindingErrors는 컨트롤에 잘못된 값이 있거나 DataBinder가 입력된 값을 형식이 엄격하게 지정된 해당 데이터로 되돌릴 수 없는 경우에 자동으로 생성됩니다. 또한 IsRequired가 true로 설정되어 있는 필드를 비워 둔 경우에도 BindingErrors가 생성됩니다. 마지막으로, 사용자 지정 유효성 검사 처리기를 만든 경우 DataBinder_Validate 메서드에서 DataBindingItem에 대해 false를 반환하는 경우에도 BindingErrors가 생성됩니다.

일반적인 바인딩 해제 단계를 최소한의 코드로 요약하면 다음과 같습니다.

Item.Load(ItemPk);DataBinder.Unbind();if (DataBinder.BindingErrors > 0){    ErrorDisplay.Text = DataBinder.ToHtml();    return;}Item.Save();

이 샘플 코드에는 BindingErrors 관련 기능(사용자 정의 코드와 비즈니스 개체 유효성 검사의 바인딩 오류를 추가하는 기능)을 보여 주기 위해 약간의 코드가 추가되었습니다. 컨트롤에 BindingErrors를 추가하여 바인딩 오류와 응용 프로그램에서 생성된 오류에 대해 일관된 오류 표시 메커니즘을 적용할 수 있도록 하는 DataBinder.AddBindingError 메서드를 주의 깊게 살펴보시기 바랍니다. AddBindingError를 사용하려면 컨트롤이 DataBinder에 바인딩되어 있어야 하지만그림 3의 txtProductId 코드와 같이 런타임에 동적으로 컨트롤을 바인더에 추가한 다음 바인딩 오류를 추가할 수도 있습니다. 이러한 경우 AddBinding을 사용하여 프로그래밍 방식으로 바인딩을 추가한 다음 AddBindingError를 호출하여 오류 메시지를 첨부합니다.

그림 3의 샘플 코드는 비즈니스 개체를 사용하여 DataRow에 바인딩합니다. 코드 다운로드 파일에는 Entity 개체를 사용하여 비즈니스 개체에 바인딩하는 예제도 포함되어 있습니다. 따라서 이 예제에서는 Item.DataRow에 바인딩하는 대신 Item.Entity에 바인딩합니다. 여기서 Entity는 Item 테이블의 속성에 해당하는 속성을 가진 .NET 개체입니다. 이 경우에도 프로세스는 동일하며 Data 소스 문자열만 Item.DataRow가 아니라 Item.Entity로 바뀝니다. wwDataBinder는 런타임에 처리할 데이터 형식을 파악하여 해당 형식을 기준으로 바인딩 또는 바인딩 해제 작업을 수행합니다. 비즈니스 개체를 사용하지 않더라도 wwDataBinder는 ADO.NET 개체에 대해 직접 작업을 수행할 수 있습니다.


Extender 컨트롤 구현

wwDataBinder는 extender 컨트롤로 구현됩니다. 즉, 폼에 있는 기존 컨트롤의 기능을 확장하게 됩니다. extender 컨트롤은 하위 클래스를 사용하지 않고 기존 컨트롤에 새로운 기능을 제공할 수 있는 유용한 메커니즘입니다. 필자도 최근 몇 년 동안은 이 기사에서 설명하는 것과 유사한 데이터 바인딩 방식을 사용했지만 그 이전에는 컨트롤 하위 클래스를 사용하여 이러한 기능을 구현했습니다. 당시에 사용한 wwTextBox, wwCheckBox, wwDropDownList 등의 각 하위 클래스의 경우 여기에서 설명하는 것과 거의 같은 기능을 제공하는 특수한 데이터 바인딩 인터페이스를 구현해야 했습니다. 이러한 방식을 사용하더라도 문제는 없지만 컨트롤을 확장하려면 매번 코드를 추가해야 하기 때문에 확장성이 좋지 않습니다. 결과적으로 코드 관리 효율성 문제나 코드 중복 문제가 발생하게 됩니다.

extender 컨트롤은 모든 기존 컨트롤에 사용할 수 있으므로 유연성이 훨씬 뛰어납니다. extender를 폼에 단일 컨트롤로 추가한 다음 확장된 속성을 사용하여 각 폼의 특정 컨트롤을 추적하는 항목의 컬렉션을 만들면 됩니다. wwDataBinder의 경우 extender 속성은 컨트롤 속성에 값을 바인딩하거나 바인딩 해제하는 데 필요한 속성이 포함된 wwDataBindingItem 개체입니다.그림 1에서 강조 표시된 DataBindingItem 속성은 wwDataBinder extender에 의해 제공되는 확장 속성입니다. extender 속성에 대한 디자이너 지원을 제공하는 것도 좋지만 Visual Studio 웹 디자이너에서는 extender 컨트롤에 대한 디자이너 지원을 구현하기가 다소 까다롭기 때문에 굳이 구현할 필요는 없습니다. 자세한 내용은 나중에 다시 설명하겠습니다.

그림 4는 wwDataBinder 클래스 계층 구조를 보여 줍니다. 이 그림에서 알 수 있듯이 wwDataBinder는 wwDataBindingItem 개체의 컬렉션이 포함된 최상위 컨트롤입니다. 이러한 wwDataBindingItem 컨트롤은 각각 ControlId 속성을 통해 폼의 컨트롤 인스턴스에 매핑됩니다. 그러면 ControlId 속성에 추적된 컨트롤의 ID가 저장되고 폼에서 컨트롤을 찾을 때 기본 참조로 사용됩니다.

사용자 삽입 이미지

그림 4 wwDataBinder Extender 클래스 계층 구조 (더 크게 보려면 이미지를 클릭하십시오.)

데이터 바인딩 extender는 데이터 바인딩 항목을 컬렉션에 추가하는 방식으로 작동합니다. 이때 컬렉션은 다양한 방법으로 채워질 수 있습니다. 즉, 속성 시트에서 extender DataBindingItem 속성을 사용하거나 DataBinder의 기본 컬렉션 편집기를 사용하거나 wwDataBinder 컨트롤의 HTML 태그를 사용하거나 런타임에 wwDataBinder.AddBinding을 호출하는 방법으로 채울 수 있습니다.

extender 메커니즘은 사용이 가장 간편하지만 디자이너에 통합하려면 컨트롤에 대해 구현 코드를 추가해야 합니다. 기본 컬렉션 편집기는 Collection 속성을 구현하고 주 클래스와 컬렉션 속성에 대해 몇 가지 특성을 제공하기 때문에 이 컨트롤을 사용하여 DataBinder에 항목을 추가할 수도 있습니다.

[ProvideProperty("DataBindingItem", typeof(Control))][ParseChildren(true, "DataBindingItems")][PersistChildren(false)][NonVisualControl, Designer(typeof(wwDataBinderDesigner))]public class wwDataBinder : Control, IExtenderProvider  {    [PersistenceMode(PersistenceMode.InnerProperty)]    public wwDataBindingItemCollection DataBindingItems     {        get  { return _DataBindingItems; }    }    wwDataBindingItemCollection _DataBindingItems = null;    ...}

이 코드는 디자이너에서 DataBindingItems의 컬렉션을 속성으로 유지하도록 합니다. DataBindingItems는 디자이너가 페이지를 로드할 때 자동으로 구문 분석되어 컬렉션에 추가됩니다. 또한 이 코드를 통해그림 5와 같이 하위 컨트롤에 대해 기본 컬렉션 편집기를 사용할 수 있습니다. 결국 모든 디자이너 항목 메커니즘에 의해 Visual Studio에서 출력은그림 6과 같이 HTML 태그에 유지됩니다.

사용자 삽입 이미지

그림 5 기본 컬렉션 편집기 (더 크게 보려면 이미지를 클릭하십시오.)

물론 디자이너 지원을 사용하지 않고 이 태그를 수동으로 ASPX 페이지에 입력할 수도 있습니다. 수동 태그와 컬렉션 편집기 중 어느 것을 사용해도 되지만 Control ID를 수동으로 입력하고 데이터 바인딩이 필요한 컨트롤을 기억하기란 성가신 일입니다. 문제는 확장할 컨트롤이 DataBinder에서 완전히 분리된다는 점입니다. 3가지 메커니즘은 각기 차이가 있지만 본질적으로 그 결과는 동일하다는 점을 알아 두어야 합니다.Figure 6의 ASP.NET 컨트롤 태그가 그 결과입니다. 이 태그는 컨트롤에 대해 항상 동일하게 사용되는 메커니즘입니다.

디자이너 extender 공급자를 제공하면 확장되는 컨트롤과 관련하여 데이터 바인딩 정보가 표시되므로 최상의 사용자 환경이 구현됩니다. 그러나 현실적으로는 디자이너 지원이 없다면 이 컨트롤을 사용하는 경우 효율적인 사용자 환경이 제공되지 않습니다. 또한 웹 디자이너는 Windows®Forms 디자이너만큼 효과적으로 extender 컨트롤을 지원하지 않기 때문에 ASP.NET 컨트롤에서 디자이너 지원을 만들기도 쉽지 않습니다. 특히 웹 디자이너는 확장되는 컨트롤의 시작 코드를 자동으로 생성하지 않으며, 속성이 업데이트될 때 Set<확장된 속성> 메서드를 호출하지 않습니다. 따라서 웹 디자이너와 함께 사용할 수 있는 extender 컨트롤을 만들려면 추가 작업이 필요합니다. 이러한 작업을 수행하는 방법을 살펴보겠습니다.

먼저 IExtenderProvider를 구현한 다음 extender 컨트롤에 대해 IExtenderProviderInterface를 구현하여 Visual Studio가 컨트롤을 extender로 인식하도록 합니다. 이 인터페이스는 true 또는 false 값을 반환하는 단일 CanExtend 메서드로 구성됩니다.

public bool CanExtend(object extendee){   if (!this.IsExtender) return false;   // *** Don't extend ourself <g>   if (extendee is wwDataBinder) return false;   if (extendee is Control) return true;   return false;}

클래스에 ProvidePropertyAttribute 설정 - 이 클래스에는 컨트롤 확장에 사용할 각 속성에 대해 ProvideProperty 특성도 설정해야 합니다. 이 경우 wwDataBinder는 DataBindingItem라는 단일 복합 속성을 사용하여 확장합니다.

[ProvideProperty("DataBindingItem", typeof(Control))]

제공된 속성에 대한 Get 메서드 구현 - 디자이너는 Get<extender 속성 이름> 메서드를 호출하여 컨트롤에 제공될 속성의 인스턴스를 가져옵니다. 이 메서드의 코드는 기존 인스턴스를 검색하거나, 컨트롤에 해당하는 인스턴스가 없는 경우 빈 인스턴스를 새로 만들어야 합니다.

public wwDataBindingItem GetDataBindingItem(Control control) {    foreach (wwDataBindingItem Item in this.DataBindingItems) {        if (Item.ControlId == control.ID) return Item;    }    wwDataBindingItem NewItem = new wwDataBindingItem(this);    NewItem.ControlId = control.ID;    NewItem.ControlInstance = control;    this.DataBindingItems.Add(NewItem);    return NewItem;}

이론적으로는 Set 메서드도 구현해야 합니다. 그러나 웹 디자이너는 Set 메서드를 호출하는 경우가 없으며, 바로 이 점이 ASP.NET에서 extender 컨트롤을 사용하기 어려운 이유입니다. Set 메서드는 디자이너가 컨트롤 편집을 마쳤으며 컨트롤 추가 작업을 마무리할 수 있는 상태임을 응용 프로그램에 알리기 위한 메서드입니다. 그러나 이 메서드가 호출되지 않기 때문에 extender 속성이 편집되어 유지가 필요한 시점을 감지하기가 어렵습니다.


디자이너 알림 해결 방법

이러한 알림 문제를 해결하기 위해서는 추가 작업이 필요합니다. 즉, extender 속성의 값이 변경될 때마다 디자이너에 변경 내용을 알려야 합니다. 이렇게 하려면 다음과 같이 주 extender 컨트롤에 메서드를 구현해야 합니다.

internal void NotifyDesigner() {               IDesignerHost Host = this.Site.Container as IDesignerHost;    ControlDesigner Designer =         Host.GetDesigner(this) as ControlDesigner;    PropertyDescriptor Descriptor =         TypeDescriptor.GetProperties(this)["DataBindingItems"];    ComponentChangedEventArgs ccea =         new ComponentChangedEventArgs(this, Descriptor, null,             this.DataBindingItems);    Designer.OnComponentChanged(this, ccea);}

이 메서드는 디자이너가 DataBindingItems 컬렉션을 HTML 태그에 유지하도록 합니다. 마지막 단계로, DataBindingItems 중 하나에서 속성이 변경될 때마다 이 메서드를 호출합니다. 다음은 wwDataBindingItem에 대한 ControlId 속성의 예입니다.

[NotifyParentProperty(true)][TypeConverter(typeof(ControlIDConverter))][Browsable(true)]public string ControlId{    get { return _ControlId; }    set    {        _ControlId = value;        if (this.DesignMode && this.Binder != null)            this.Binder.NotifyDesigner();    }}private string _ControlId = "";

이 예제에서 this.Binder는 wwDataBinder 컨트롤의 참조입니다. 따라서 속성이 설정될 때마다 디자이너에게 알리고 전체 컬렉션이 유지됩니다. 이 방식은 비효율적일 뿐만 아니라 디자이너가 설정할 수 있는 모든 속성에 코드가 필요합니다. 그러나 이 경우 디자이너와 HTML 태그가 항상 동기화된 상태로 유지됩니다. 속성을 올바르게 작성할 수 있는 유일한 방법은 이러한 사전 유지 방식입니다.

Set<속성 extender> 메서드에 대한 호출 부재는 다른 사소한 문제들도 일으킵니다. 먼저 컨트롤이 활성화되면 바로 디자이너가 extender 속성을 초기화하기 때문에 빈 항목이 컬렉션에 추가되는 문제가 있습니다. 즉, 저장 작업에 대한 알림이 없기 때문에 디자이너가 extender 속성에 액세스하면 바로 값이 컬렉션에 추가되어 그대로 유지되므로 빈 항목이 생기게 됩니다. 이 경우 디자인 모드에서 항목이 추가될 때 목록을 정리하는 문제 해결 코드를 클래스에 추가하여 목록에 빈 항목이 둘 이상 추가되지 않도록 할 수 있습니다. 번거롭긴 하지만 바인더가 BindingSource 집합이 없는 항목은 무시하므로 이 방법은 다른 부분에 간섭을 일으키지 않습니다.

두 번째는 좀 더 중대한 문제입니다. 컨트롤의 이름을 변경하면 DataBindingItem과 컨트롤 간의 연결이 제거되고 컨트롤의 설정이 삭제됩니다. 이 경우에도 문제는 디자이너가 이름 변경을 제대로 알리지 않아 코드가 ControlId 변경을 차단하고 데이터 바인딩 항목을 조정할 수 없다는 점입니다. 대신 새 항목이 추가되고 이전 항목이 잘못된 ControlId를 가리키게 됩니다. 폼을 실행하면 바인딩을 해제하는 동안 바인딩을 확인할 수 없기 때문에 오류가 발생합니다. 이 문제는 HTML 태그를 수동으로 변경하고 이전 항목의 ControlId 이름을 바꾸고 새로 추가된 항목을 제거하여 간단히 해결할 수 있습니다.

사소하긴 하지만 이러한 문제들을 통해 Visual Studio의 웹 디자이너가 Extender 속성과 관련하여 몇 가지 문제를 가지고 있으며, 디자이너 지원을 위해서는 이러한 문제를 해결해야 한다는 사실을 잘 알 수 있습니다.

사용자 삽입 이미지

DataBinding 및 오류 관리 방식

wwDataBinder 컨트롤의 핵심 기능은 데이터 바인딩 기능과 오류 관리 기능이라고 할 수 있습니다. 이 컨트롤의 데이터 바인딩 처리 기능은 주로 리플렉션에 의존하는 매우 간단한 방식입니다. 개별 wwDataBindingItem 개체에는 DataBind 및 Unbind 메서드가 있으며, 이 두 가지 메서드가 개별 데이터 또는 속성 값을 대상 컨트롤의 개별 속성에 바인딩하는 작업을 처리합니다. 바인딩, 바인딩 해제 및 오류 감지 작업은 주로 이 두 메서드에서 처리됩니다.

데이터 바인딩 프로세스는 DataBindingItems 컬렉션을 채우는 것으로 시작되는데, 이 단계는 ASP.NET에서 ASPX 페이지를 읽고 페이지의 정의를 DataBindingItems 컬렉션으로 구문 분석할 때 자동으로 수행됩니다. 또한 AddBinding 메서드를 사용하여 프로그래밍 방식으로 항목을 추가할 수도 있습니다. 모든 데이터가 로드되고 바인딩할 준비가 되면 wwDataBinder 개체에서 DataBind 메서드가 호출됩니다.

wwDataBinder.DataBind는 상위 코디네이터이지만 주로 DataBindingItems 컬렉션에 대해 루프를 실행하고 DataBind 메서드를 호출하는 역할을 합니다. 대부분의 작업은 개별 wwDataBindingItem 개체의 DataBind 메서드에서 이루어집니다. 이때 DataBindingItem의 문자열 기반 값을 개체 참조와 속성으로 구문 분석하는 데 리플렉션이 사용됩니다.

이 작업을 수행하는 코드는 복잡하지만 샘플 소스에 포함된 몇 가지 유용한 Reflection 도우미 클래스를 사용하면 복잡한 계층 구조의 개체 참조를 쉽게 검색할 수 있습니다. 예를 들어 개체 참조를 검색하여 값을 할당하는 데 사용된 코드는 다음과 같습니다.

object Source = wwUtils.GetPropertyEx(WebPage,     "Item.Entity.ContactInfo");wwUtils.SetProperty(Source, "Address", "32 Kaiea Place");

기본적으로 계층 구조 구문에 대해서는 리플렉션이 지원되지 않으므로 이 도우미 함수와 해당 SetPropertyEx 함수를 사용하면 복잡한 개체 참조를 간단히 검색하고 할당할 수 있습니다. 이러한 도우미 클래스는 wwDataBinder 프로젝트와 어셈블리에서 제공되는 wwUtils 클래스에 있습니다.

이러한 도우미 클래스를 사용하더라도 원본 개체에 대한 참조를 검색하고 개체의 형식을 확인하고 값을 할당하려면 코드가 길어질 수 있습니다. 또한 개체의 속성을 처리하는 방법과 다르게 처리되는 ADO.NET DataRow, DataTable, DataSet 개체 등의 특정 개체 형식에 대해 여러 가지 추가 검사가 수행됩니다.

DataBind 및 Unbind 메서드는 값을 읽어 대상에 다시 할당하는 프로세스가 핵심입니다. 이 프로세스에서도 컨트롤 또는 속성 이름이 잘못되거나 바인딩을 해제하는 값이 잘못되어 올바른 원본 형식으로 변환되지 않는 등의 여러 가지 문제가 발생할 수 있습니다. 특히 바인딩을 해제하기 위해서는 입력 문자열 값을 형식이 지정된 값으로 되돌리는 문제를 해결해야 합니다. 이 프로세스에서는 열거형과 같은 특수 형식, culture 인식 등 다양한 요소를 고려해야 합니다. wwDataBindingItem DataBind 및 Unbind 메서드는 너무 길어서 여기에서 소개하지는 못하지만 제공된 소스 코드에서 이 두 메서드를 참조하면 핵심적인 바인딩 작업을 확인할 수 있습니다.

이 두 가지 메서드 중 하나에서 바인딩 오류가 발생하면 계층 구조에서 특수한 예외가 발생하여 wwDataBinder.DataBind 또는 Unbind 메서드로 다시 반환됩니다. 그러면 이 두 메서드는 오류를 처리하여 BindingErrors로 변환하고 결과적으로 페이지에 아이콘/메시지를 표시합니다.그림 7은 상위 흐름을 파악할 수 있도록 wwDataBinder.Unbind 메서드를 보여 줍니다.


바인딩 오류 처리

오류가 발생하면 catch되어 내부 HandleUnbindingError 메서드로 전달됩니다. 이 메서드는 구체적인 오류를 찾아 적절한 오류 메시지로 표시합니다. 데이터 바인딩 작업에서는 형식 문제로 인해 값을 바인딩하거나 바인딩 해제할 수 없을 때 발생하는 BindingErrorException을 비롯하여 몇 가지 알려진 예외가 발생합니다. 또한 Validation이 구현되어 있는 경우 false가 반환되면 발생하는 RequiredFieldException 및 ValidationErrorException도 있습니다. 이러한 오류의 경우 표시되는 메시지를 구성하는 데 사용되는 자세한 오류 정보를 제공합니다.

AddBindingError는 BindingError 개체를 만들어 BindingErrors 컬렉션에 추가하고 오류 메시지 텍스트를 작성하며 컨트롤 옆에 오류 아이콘을 삽입하는 핵심 메서드입니다. BindingErrors 컬렉션은 개발자가 오류를 검사(DataBinder.BindingErrors.Count > 0인지 검사)하는 데 사용하는 기본 메커니즘일 뿐만 아니라 사용자에게 표시되는 오류 정보(DataBinder.BindingErrors.ToHtml)를 제공함으로써 바인딩 프로세스에서 중요한 역할을 합니다.

런타임에 동적으로 아이콘을 만드는 데에는 몇 가지 걸림돌이 있었습니다. 처음에는 코드에서 다음과 같이 Literal Control 삽입을 통해 동적 HTML 태그를 페이지에 추가했습니다.

// *** Generate the HTML to be injected next to the controlstring HtmlMarkup = this.GetBindingErrorMessageHtml(BindingItem);if (!string.IsNullOrEmpty(HtmlMarkup))   {    LiteralControl Literal = new LiteralControl();    Control Parent = BindingItem.ControlInstance.Parent;    int CtlIdx = Parent.Controls.IndexOf(BindingItem.ControlInstance);    try      {        Parent.Controls.AddAt(CtlIdx + 1, Literal);    }    catch { }}

그런데 대부분의 경우에는 문제가 없었지만 페이지에 <% %> 스크립트 코드가 있을 때에는 문제가 발생했습니다. 컨테이너에 스크립트 코드가 있으면 새 컨트롤을 동적으로 추가할 수 없습니다. 추가하려고 하면 "해당 컨트롤에 코드 블록(<% .. %>)이 포함되어 있으므로 Controls 컬렉션을 수정할 수 없습니다"라는 오류가 발생합니다.

문제는 컨테이너에 스크립트 식이 있을 때 ASP.NET이 하드 코드된 컨트롤 정의, 계산 식 및 리터럴 텍스트가 포함된 사용자 지정 ParseTree 메서드의 결과를 컨트롤 컨테이너의 Render 코드로 렌더링한다는 점입니다. 결과적으로 Controls 목록을 이동할 수 없게 되고 이 목록에 새 컨트롤을 제대로 추가할 수 없기 때문에 ASP.NET에서 이 오류가 발생합니다. 방금 소개한 코드는 이 오류를 catch하고 컨트롤 옆에 아이콘을 렌더링하지 않습니다. 그러나 이 경우에도 오류 요약에 바인딩 오류가 추가 및 표시됩니다.

이 컨트롤은 일반 데이터 바인딩 컨트롤이어야 하므로 클라이언트 스크립트를 사용하여 문제를 해결했습니다. 즉, 페이지에 새 컨트롤을 삽입하는 대신 ControlId와 HTML 문자열을 입력으로 사용하여 클라이언트 코드에서 HTML을 추가하는 JavaScript 함수를 만들었습니다. 그리고 페이지에 리터럴 컨트롤을 삽입하는 대신 이 JavaScript 함수에 대한 호출을 삽입했습니다.그림 8은 단일 유효성 검사 오류에 대해 생성된 코드를 보여 줍니다.

ASP.NET에서 이 코드는 각 BindingError에 대해 다음 코드와 연결됩니다.

Message = this.GetErrorMessageHtmlMarkup();if (!this._ClientScriptInjectionScriptAdded)   Page.ClientScript.RegisterClientScriptBlock(      this.GetType(), "AddHtmlAfterControl",      @"function AddHtmlAfterControl(ControlId,HtmlMarkup){ ... }");this.Page.ClientScript.RegisterStartupScript(   this.GetType(), Item.ControlId,   string.Format("AddHtmlAfterControl('{0}','{1}');\r\n",   Item.ControlInstance.ClientID, Message), true);

이 코드는 페이지를 로드할 때 호출되도록 페이지의 StartupScript 블록에 리터럴 문자열로 포함되는 개별 AddHtmlAfterControl 클라이언트 측 함수 호출을 생성합니다. 메시지는 전체 HTML 문자열로, 여기에는 오류 메시지에 표시하도록 선택한 형식에 따라 이미지와 대체 텍스트 또는 기타 형식의 데이터가 포함됩니다. 또한 이 코드는 AddHtmlAfterControl 함수에 처음으로 도달하면 해당 함수를 렌더링하여 StartupCode 호출에서 실제로 이 함수를 찾을 수 있도록 합니다. ASP.NET은 서버에서 이러한 클라이언트 측 코드 생성이 쉽게 이루어지도록 합니다.

컨트롤의 또 다른 흥미로운 오류 표시 기능으로는 오류 목록이 있습니다.그림 2를 다시 살펴보면 요약 목록에 표시된 모든 오류에 클릭할 수 있는 링크가 포함되어 있음을 알 수 있습니다. 이 링크를 클릭하면 오류가 발생한 컨트롤로 이동합니다. 또한 코드는 3초 동안 해당 컨트롤을 빨간색 테두리로 강조 표시합니다.

이러한 모든 기능은 오류 메시지로 강제 생성된 JavaScript를 사용하여 처리됩니다. 이러한 오류 생성 프로세스는 모든 바인딩 오류에 대해 루프를 실행하고 오류 목록을 순서 없는 목록으로 작성하는 BindingErrors.ToHtml 메서드에 의해 처리됩니다(그림 9참조). 그러면 각 항목의 오류 메시지가 컨트롤의 ClientId로 이동하는 JavaScript 스크립트 작업 및 HTML을 사용하여 링크로 표시됩니다.

일관된 오류 처리 및 표시 메커니즘은 정교한 응용 프로그램을 가리는 척도가 되므로 데이터 바인딩 관련 오류를 처리하는 일관된 메커니즘을 구현하는 것은 매우 중요합니다. 비즈니스 개체 유효성 검사 오류 등 응용 프로그램의 다른 영역에서 발생하는 오류에도 동일한 오류 표시 메커니즘을 간접적으로 적용하면 더욱 좋습니다. 예를 들어 필자는 비즈니스 개체에 대해 Validate 메서드를 호출할 때 유효성 검사 오류를 생성하는 비즈니스 개체를 응용 프로그램에 사용했습니다. 유효성 검사 오류에 대해 루프를 실행하고 데이터 바인더의 바인딩 오류에 해당 오류를 추가하는 프로세스는 간단합니다.

if (!Item.Validate()){    foreach (ValidationError Error in Item.ValidationErrors)    {        DataBinder.AddBindingError(Error.Message, Error.ControlID);    }}

이렇게 하면 비즈니스 개체의 유효성 검사 오류가 데이터 바인딩 프로세스에 직접 참여하므로 사용자 인터페이스와는 완전히 분리된 작업에서 오류를 생성할 수 있습니다. 물론 그 중에는 완전하게 분리되어 있지 않은 작업도 있습니다. 예를 들어 예제를 살펴보면 비즈니스 개체에서 반환된 ValidationError 개체의 ControlId 속성도 있습니다. 비즈니스 개체에서 일반 ControlId를 유효성 검사 오류에 할당할 수 있습니다. 비즈니스 개체 Validate 메서드는 다음과 같습니다.

public bool Validate()   {    this.ValidationErrors.Clear();    if (this.Entity.UnitPrice < 0)        this.ValidationErrors.Add(            "Unit price can't be smaller than 0",             "txtUnitPrice");    if (this.ValidationErrors.Count > 0)        return false;    return true;}

필자는 컨트롤 이름을 선택적으로 비즈니스 개체 코드의 유효성 검사 오류에 추가합니다. 응용 프로그램에서는 대개 기본 속성 이름과 일치하는 일관된 컨트롤 이름을 사용합니다. 이로 인해 일부 연결이 발생하지만 데이터 바인딩 오류와 유효성 검사 오류를 일정한 방식으로 처리할 수 있습니다. 이러한 연결은 선택 사항이며, 컨트롤 이름을 생략하더라도 유효성 검사 오류를 바인딩 오류에 추가하는 코드는 정상적으로 작동합니다. 이 경우 요약은 그대로 표시되지만 메시지에 컨트롤에 대한 링크가 표시되지 않고 컨트롤 옆에 아이콘도 표시되지 않습니다.


요약

데이터 바인딩은 웹 응용 프로그램에서 중요한 부분을 차지합니다. 이 기사에서 설명한 컨트롤은 사용자를 방해하지 않으면서 다양한 기능을 제공합니다. 또한 기본 ASP.NET 기능으로는 구현하기 어려운 방식으로 데이터 바인딩, 오류 처리, 오류 표시 등 여러 가지 관련 작업을 통합합니다. 단순 컨트롤 데이터 바인딩을 처리하는 다른 방법도 있지만 필자에게는 이 방법이 가장 생산적이었습니다.

독자 여러분도 이 컨트롤을 유용하게 활용하기 바랍니다. 이 컨트롤은 다른 작업에 영향을 미치지 않으면서 현재 ASP.NET 2.0에서 처리하지 어려운 여러 가지 시나리오를 해결할 수 있으므로 다른 부분을 변경하지 않고 사용할 수 있습니다. 이와 같은 extender 컨트롤은 하위 클래스를 사용하지 않고 기존 컨트롤의 기능을 확장할 수 있는 효과적인 수단입니다. 이러한 컨트롤을 사용하여 ASP.NET에서 기존에 제공되는 기능을 확장해 보시기 바랍니다.


사용자 삽입 이미지
NEW:Explorethe sample code online!- or -코드 다운로드 위치:Databinding2006_12.exe(2021KB)

Rick Strahl은 하와이 마우이에 위치한 West Wind Technologies의 대표이사입니다. 이 회사는 .NET, IIS, Visual Studio 관련 웹 및 분산 응용 프로그램 개발, 도구, 교육, 멘토링 등을 전문으로 하는 회사입니다. 자세한 내용은www.west-wind.com을 참조하거나rstrahl@west-wind.com을 통해 저자에게 문의하십시오.
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by mari