유경상 (ksyu@tysystems.com)
컨트롤 계층 구조 생성
<그림 1>과 같은 컨트롤 계층 구조는 언제 생성되는 것일까? 데이터그리드의 컨트롤 계층 구조를 생성하기 위해서는 몇 가지 정보가 필요하다. 첫째로 데이터 아이템의 갯수가 필요하다. 헤더, 풋터, 페이저 등을 제외한 순수한 데이터를 위한 DataGridItem의 갯수가 컨트롤 계층 구조에서 필수 조건이 된다. 두 번째로 필요한 것은 컬럼의 갯수 및 각 컬럼의 타입(BoundColumn, ButtonColumn 등)이며 세 번째는 페이징 관련 정보(AllowPaging, PageSize 등의 속성)이다. 이들 정보가 있으면 컨트롤 계층 구조를 만들 수 있다.
먼저 데이터 아이템 갯수에 대해 생각해보자. 아니 생각해 볼 것도 말 것도 없이 데이터 아이템 갯수는 데이터 바인딩이 이뤄질 때 알아낼 수 있다. 데이터 바인딩을 수행하기 위해서는 DataSource 속성이 설정돼야 하고 데이터소스 속성으로부터 데이터 갯수를 확인할 수 있다. 나머지 컬럼 정보와 페이징 관련 정보는 데이터그리드 컨트롤이 생성될 때 알 수 있다. 이들은 디폴트 값도 가지고 있으므로 아무런 문제도 되지 않는다. 게다가 태그에 명시된 여러 특성들은 모두 데이터그리드 클래스의 속성 값으로 설정되는 C#(VB.NET) 코드로 변환된다. 즉 이러한 초기화는 Init 과정에서 이미 처리된 상태이다. 따라서 데이터 아이템의 갯수만 알면 컨트롤 계층 구조를 만들 수 있다.
일반적으로 데이터그리드로 작업할 때 데이터소스에 데이터 셋 혹은 데이터 테이블을 설정하고 곧바로 DataBind 메쏘드를 호출하여 바인딩을 수행한다. 따라서 DataBind 메쏘드가 호출된 후에 자식 컨트롤의 계층 구조가 만들어 진다. 구체적으로 컨트롤 계층 구조를 생성하는 데이터그리드 클래스의 메쏘드는 CreateControlHierarchy 메쏘드이다. 대개의 경우 CreateChildControl() 메쏘드에서 자식 컨트롤을 생성하는 것이 일반적이지만 데이터그리드나 데이터리스트 컨트롤 공히 CreateChildControl 메쏘드는 별반 일을 수행하지 않고 CreateControlHierarchy 메쏘드를 호출하며 CreateControlHierarchy 메쏘드에서 실제 작업들을 수행하도록 되어 있다.
자, 그렇다면 꼭 데이터 바인딩이 수행돼야만 컨트롤의 계층 구조가 생성되는 것인가? 그렇지는 않다. 우리가 데이터그리드의 EnableViewState를 true로 설정해 놓고 LinkButton 등을 이용하여 포스트 백(Post back)을 발생하더라도 데이터그리드는 컨트롤 계층 구조를 유지한다. 데이터소스 속성이 설정되거나 DataBind 메쏘드가 호출되지 않더라도 말이다. 이렇게 데이터그리드가 상태를 유지하는 것은 데이터그리드가 데이터 아이템의 갯수를 ViewState에 기록해 두기 때문이다. 데이터그리드가 ViewState를 로드하는 과정에서 컨트롤의 계층 구조가 생성되며, 이때 ViewState에서 데이터 아이템의 갯수를 취하며 나머지 컬럼 정보/페이징 정보들은 앞서 언급한 대로 Init 과정에서 이미 초기화가 완료됐다. 따라서 포스트 백이 발생하는 경우에는 LoadViewState 메쏘드의 처리 과정에서 컨트롤의 계층 구조는 생성되는 것이다.
그렇다면 ViewState가 disable되어 있다면 어떻게 되는가? 데이터 바인딩 과정이야 ViewSate와 별반 관계가 없으니 그렇다 치고, 포스트 백이 발생하는 경우라면 데이터 아이템의 갯수를 알아낼 수 없으므로 당연히 컨트롤 계층 구조 역시 생성되지 않는다. 컨트롤 계층 구조가 생성되지 않는다는 말은 <그림 1>에서 나타나는 DataGridTable 컨트롤도, DataGridItem 컨트롤도, TableCell 컨트롤도 생성되지 않는다. 물론 부모 컨트롤이 없으니 페이징을 위한 링크 버튼이나 헤더를 위한 DataGridItem 역시 생성되지 않음은 물론이다. 왜 ViewState를 disable하면 페이징이 수행되지 않는가에 대한 약간의 힌트를 여기서 얻을 수 있을 것이다. 그러나 여전히 궁금증은 남는다. 자식 컨트롤이 생성되지 않음과 페이징이나 정렬이 되지 않는 것과 무슨 관계일까? 페이징이나 정렬은 이벤트에 의존한다. 즉 데이터그리드는 페이징/정렬을 수행할 수 있도록 이벤트를 생성하여 페이지에게 기회를 줄 뿐이다. 다음부터 설명할 데이터그리드의 이벤트 처리 방식에서 그 해답을 찾아보자.
이벤트 처리
일반적으로 웹 컨트롤의 이벤트 발생은 포스트 백과 관계가 있다. TextBox 컨트롤의 Changed 이벤트는 포스트 백된 텍스트 박스의 값이 이전 값과 비교하여 변경된 경우 발생하며, LinkButton 컨트롤은__doPostBack 자바 스크립트의 수행 결과로서 포스트 백이 발생한 경우에 Click 이벤트를 발생시킨다. 이렇게 포스트 백과 관련된 이벤트를 처리하기 위해 대개의 웹 컨트롤은 IPostBackDataHandler 인터페이스나 IPostBackEventHandler 인터페이스를 구현한다. TextBox 컨트롤과 같이 포스트 백된 데이터를 속성 값으로 할당하는 류의 ‘처리’를 하는 컨트롤들(ListBox 컨트롤, DropDownList 컨트롤, CheckBox 컨트롤, RadioButtonList 컨트롤 등)은 IPostBackDataHandler 인터페이스를 구현한다. 한편 클릭 류의 행동(behavior)을 보이는 컨트롤들(Button 컨트롤, LinkButton 컨트롤, ImageButton 컨트롤, Calendar 컨트롤)은 IPostBackEventHandler 인터페이스를 구현한다. 이들 인터페이스의 멤버나 사용방법 등은 MSDN을 참조하자.
데이터그리드 역시 포스트 백과 연관이 있다. 데이터그리드의 아이템이 Edit 중일 때는 사용자의 입력이 포스트 백되며, 페이징을 위해 링크 버튼을 클릭하면 역시 클릭 류의 이벤트가 발생한다. 하지만 데이터그리드 컨트롤은 IPostBackDataHandler 인터페이스도 IPostBackEventHandler 인터페이스도 구현하지 않는다. 그렇다면 페이징을 위한 링크가 클릭될 때 어떻게 데이터그리드의 PageIndexChanged 이벤트가 발생하는 것일까?
데이터그리드의 이벤트들은 대부분 자식 컨트롤에게 위임되어 있다. 즉 이벤트를 발생하는 주체는 데이터그리드가 아니라 데이터그리드의 자식 컨트롤인 것이다. 그렇다면 어떻게 이벤트 핸들러는 데이터그리드의 속성일까? 이는 이벤트 버블링이라 부르는 이벤트 전파 기법을 통해 이뤄진다. 이벤트 버블링은 자식 컨트롤이 이벤트를 부모 컨트롤로 전파하는 기법이다. 자식 컨트롤은 이벤트에 대해 RaiseBubbleEvent 메쏘드를 호출하고 부모 컨트롤은 OnBubbleEvent 메쏘드를 오버라이드한다. 자식 컨트롤이 RaiseBubbleEvent를 호출하면 부모 컨트롤의 OnBubbleEvent를 호출하도록 되어 있다. 만약 부모 컨트롤의 OnBubbleEvent 메쏘드가 true를 반환하면 버블링은 중지되며 false를 반환하면 다시 상위 컨트롤로 이벤트는 계속 전파된다. OnBubbleEvent는 실제로 모든 웹 컨트롤의 베이스 클래스인 System.Web.UI.Control 클래스의 가상 메쏘드로서 특별히 이 메쏘드를 오버라이드하지 않으면 false를 반환한다(<리스트 2>). 이벤트 버블링을 통해 자식 컨트롤의 이벤트는 상위로 전파될 수 있는 것이다.
데이터그리드의 PageIndexChanged, SelectedIndexChanged, SortCommand, EditCommand, UpdateCommand, CancelCommand, DeleteCommand 이벤트는 모두 이 이벤트 버블링을 통해 발생된다. 실제 이벤트를 수신하는, 즉 IPostBackEventHandler를 구현하는 클래스는 LinkButton 혹은 Button 클래스다. 이들은 Command 이벤트에 대해 이벤트를 버블링하며 데이터그리드 클래스의 OnBubbleEvent 메쏘드는 버블링된 이벤트를 검사하여 적절한 이벤트로 디스패치한다.
페이징에 사용되는 링크 버튼을 예로 들어 보자. 페이저 DataGridItem은 하나의 TableCell 컨트롤을 생성하며 이 컨트롤에 대한 초기화는 DataGrid 클래스의 InitializePager 메쏘드에서 수행된다. 이 메쏘드에서는 다음과 같은 코드로서 LinkButton을 생성하고 TableCell 컨트롤에 추가한다.
만약 사용자가 LinkButton을 클릭하면 연결된 Click 이벤트 핸들러가 없으므로 아무런 작업도 하지 않을 것이다. 다만 이벤트 버블링에 의해 이벤트는 데이터그리드 클래스의 OnBubbleEvent 메쏘드가 호출될 것이며 이 OnBubbleEvent 메쏘드는 CommandName 속성, CommandArgument 속성을 이용하여 어떠한 이벤트인가를 판별해 내고 이벤트를 발생시킬 것이다.
데이터그리드 진단
이제 데이터그리드를 진단하기 위해 필요한 사항들을 모두 알아봤다. 완벽하지는 않지만 말이다. 모든 것을 다 알려주면 독자들도 재미없어 할 것 같아서 듬성듬성 틈을 두었다. 이제 앞서 언급했던 데이터그리드의 문제점을 진단해 보자.
많은 웹 컨트롤
데이터그리드의 구조는 간략하고 강력하게 구조화되어 있다. 즉 많은 일을 자식 컨트롤에게 위임함으로써 상당히 구조적인 방법으로 기능들을 구현하고 있다. 소위 말하는 객체 지향적인 방법을 충실히 따라 설계됐고 구현됐다고 볼 수 있는 것이다. 하지만 이 때문에 문제가 발생한다. 데이터그리드가 생성하는 웹 컨트롤의 숫자는 매우 많다. 데이터 아이템의 수가 20(PageSize = 20 정도로 생각하면 되겠다)이고 컬럼이 10개라고 했을 때 생성되는 컨트롤의 갯수는 200개가 훌쩍 넘어 버린다.
왜 웹 컨트롤의 갯수가 많으면 문제가 되는가? 앞서 <표 1>에서 나타냈듯 웹 컨트롤은 일련의 제어 흐름을 가진다. 모든 컨트롤에 대해 OnInit, LoadViewState, OnLoad, OnPreRender, SaveViewState, Render 메쏘드가 호출된다. 비록 아무런 일도 하지 않더라도 컨트롤 클래스가 이들에 대한 기본 구현을 제공하므로 일정 프로세싱 파워를 소비하게 된다. 가랑비에 옷 젖는다는 말이 있듯 이렇게 많은 컨트롤을 자식 컨트롤로 가지고 있는 데이터그리드는 오버헤드를 갖지 않을 수 없다.
거대한 ViewState
데이터그리드의 ViewState는 크다. 데이터 아이템이 10여 개가 넘고 컬럼이 10여 개 정도 되면 10KB가 넘는 ViewState를 생성하기도 한다. 심지어는 HTML 태그들 보다 ViewState가 더 큰 페이지도 있을 수 있다. 이렇게 ViewState가 큰 이유는 데이터그리드가 데이터 아이템의 갯수, 컬럼 정보 등 필수적인 데이터를 ViewState에 기록할 뿐만 아니라, HeaderStyle, FooterStyle 등 각종 스타일까지도 ViewState에 기록한다. 더욱 좋지 못한 것은 자식 컨트롤까지 ViewState에 자신의 상태정보를 기록한다는 것이다. 실제로 데이터그리드는 자식 컨트롤의 상태 정보에 관여하지 않는다. 어떤 셀의 데이터가 포스트 백 사이에 유지되고 있다면 그것은 TableCell이 스스로 ViewState에 자신의 상태를 기록한 것이다. 따라서 ViewState의 크기는 눈덩이처럼 불어나게 된다. ViewState를 웹 페이지에 렌더링하기 위해서는 일련의 직렬화(serialization)화 작업을 수행해야 하며, 이를 다시 Base64 인코딩을 수행해야 한다. 게다가 ViewState가 임의로 변경되었는가를 확인하기 위해 해시 작업 역시 수행된다. ViewState가 크면 클수록 처리에 필요한 CPU 파워는 증가하기 마련이다.
컨트롤 계층 구조 생성
<그림 1>과 같은 컨트롤 계층 구조는 언제 생성되는 것일까? 데이터그리드의 컨트롤 계층 구조를 생성하기 위해서는 몇 가지 정보가 필요하다. 첫째로 데이터 아이템의 갯수가 필요하다. 헤더, 풋터, 페이저 등을 제외한 순수한 데이터를 위한 DataGridItem의 갯수가 컨트롤 계층 구조에서 필수 조건이 된다. 두 번째로 필요한 것은 컬럼의 갯수 및 각 컬럼의 타입(BoundColumn, ButtonColumn 등)이며 세 번째는 페이징 관련 정보(AllowPaging, PageSize 등의 속성)이다. 이들 정보가 있으면 컨트롤 계층 구조를 만들 수 있다.
먼저 데이터 아이템 갯수에 대해 생각해보자. 아니 생각해 볼 것도 말 것도 없이 데이터 아이템 갯수는 데이터 바인딩이 이뤄질 때 알아낼 수 있다. 데이터 바인딩을 수행하기 위해서는 DataSource 속성이 설정돼야 하고 데이터소스 속성으로부터 데이터 갯수를 확인할 수 있다. 나머지 컬럼 정보와 페이징 관련 정보는 데이터그리드 컨트롤이 생성될 때 알 수 있다. 이들은 디폴트 값도 가지고 있으므로 아무런 문제도 되지 않는다. 게다가 태그에 명시된 여러 특성들은 모두 데이터그리드 클래스의 속성 값으로 설정되는 C#(VB.NET) 코드로 변환된다. 즉 이러한 초기화는 Init 과정에서 이미 처리된 상태이다. 따라서 데이터 아이템의 갯수만 알면 컨트롤 계층 구조를 만들 수 있다.
일반적으로 데이터그리드로 작업할 때 데이터소스에 데이터 셋 혹은 데이터 테이블을 설정하고 곧바로 DataBind 메쏘드를 호출하여 바인딩을 수행한다. 따라서 DataBind 메쏘드가 호출된 후에 자식 컨트롤의 계층 구조가 만들어 진다. 구체적으로 컨트롤 계층 구조를 생성하는 데이터그리드 클래스의 메쏘드는 CreateControlHierarchy 메쏘드이다. 대개의 경우 CreateChildControl() 메쏘드에서 자식 컨트롤을 생성하는 것이 일반적이지만 데이터그리드나 데이터리스트 컨트롤 공히 CreateChildControl 메쏘드는 별반 일을 수행하지 않고 CreateControlHierarchy 메쏘드를 호출하며 CreateControlHierarchy 메쏘드에서 실제 작업들을 수행하도록 되어 있다.
자, 그렇다면 꼭 데이터 바인딩이 수행돼야만 컨트롤의 계층 구조가 생성되는 것인가? 그렇지는 않다. 우리가 데이터그리드의 EnableViewState를 true로 설정해 놓고 LinkButton 등을 이용하여 포스트 백(Post back)을 발생하더라도 데이터그리드는 컨트롤 계층 구조를 유지한다. 데이터소스 속성이 설정되거나 DataBind 메쏘드가 호출되지 않더라도 말이다. 이렇게 데이터그리드가 상태를 유지하는 것은 데이터그리드가 데이터 아이템의 갯수를 ViewState에 기록해 두기 때문이다. 데이터그리드가 ViewState를 로드하는 과정에서 컨트롤의 계층 구조가 생성되며, 이때 ViewState에서 데이터 아이템의 갯수를 취하며 나머지 컬럼 정보/페이징 정보들은 앞서 언급한 대로 Init 과정에서 이미 초기화가 완료됐다. 따라서 포스트 백이 발생하는 경우에는 LoadViewState 메쏘드의 처리 과정에서 컨트롤의 계층 구조는 생성되는 것이다.
그렇다면 ViewState가 disable되어 있다면 어떻게 되는가? 데이터 바인딩 과정이야 ViewSate와 별반 관계가 없으니 그렇다 치고, 포스트 백이 발생하는 경우라면 데이터 아이템의 갯수를 알아낼 수 없으므로 당연히 컨트롤 계층 구조 역시 생성되지 않는다. 컨트롤 계층 구조가 생성되지 않는다는 말은 <그림 1>에서 나타나는 DataGridTable 컨트롤도, DataGridItem 컨트롤도, TableCell 컨트롤도 생성되지 않는다. 물론 부모 컨트롤이 없으니 페이징을 위한 링크 버튼이나 헤더를 위한 DataGridItem 역시 생성되지 않음은 물론이다. 왜 ViewState를 disable하면 페이징이 수행되지 않는가에 대한 약간의 힌트를 여기서 얻을 수 있을 것이다. 그러나 여전히 궁금증은 남는다. 자식 컨트롤이 생성되지 않음과 페이징이나 정렬이 되지 않는 것과 무슨 관계일까? 페이징이나 정렬은 이벤트에 의존한다. 즉 데이터그리드는 페이징/정렬을 수행할 수 있도록 이벤트를 생성하여 페이지에게 기회를 줄 뿐이다. 다음부터 설명할 데이터그리드의 이벤트 처리 방식에서 그 해답을 찾아보자.
이벤트 처리
일반적으로 웹 컨트롤의 이벤트 발생은 포스트 백과 관계가 있다. TextBox 컨트롤의 Changed 이벤트는 포스트 백된 텍스트 박스의 값이 이전 값과 비교하여 변경된 경우 발생하며, LinkButton 컨트롤은__doPostBack 자바 스크립트의 수행 결과로서 포스트 백이 발생한 경우에 Click 이벤트를 발생시킨다. 이렇게 포스트 백과 관련된 이벤트를 처리하기 위해 대개의 웹 컨트롤은 IPostBackDataHandler 인터페이스나 IPostBackEventHandler 인터페이스를 구현한다. TextBox 컨트롤과 같이 포스트 백된 데이터를 속성 값으로 할당하는 류의 ‘처리’를 하는 컨트롤들(ListBox 컨트롤, DropDownList 컨트롤, CheckBox 컨트롤, RadioButtonList 컨트롤 등)은 IPostBackDataHandler 인터페이스를 구현한다. 한편 클릭 류의 행동(behavior)을 보이는 컨트롤들(Button 컨트롤, LinkButton 컨트롤, ImageButton 컨트롤, Calendar 컨트롤)은 IPostBackEventHandler 인터페이스를 구현한다. 이들 인터페이스의 멤버나 사용방법 등은 MSDN을 참조하자.
데이터그리드 역시 포스트 백과 연관이 있다. 데이터그리드의 아이템이 Edit 중일 때는 사용자의 입력이 포스트 백되며, 페이징을 위해 링크 버튼을 클릭하면 역시 클릭 류의 이벤트가 발생한다. 하지만 데이터그리드 컨트롤은 IPostBackDataHandler 인터페이스도 IPostBackEventHandler 인터페이스도 구현하지 않는다. 그렇다면 페이징을 위한 링크가 클릭될 때 어떻게 데이터그리드의 PageIndexChanged 이벤트가 발생하는 것일까?
데이터그리드의 이벤트들은 대부분 자식 컨트롤에게 위임되어 있다. 즉 이벤트를 발생하는 주체는 데이터그리드가 아니라 데이터그리드의 자식 컨트롤인 것이다. 그렇다면 어떻게 이벤트 핸들러는 데이터그리드의 속성일까? 이는 이벤트 버블링이라 부르는 이벤트 전파 기법을 통해 이뤄진다. 이벤트 버블링은 자식 컨트롤이 이벤트를 부모 컨트롤로 전파하는 기법이다. 자식 컨트롤은 이벤트에 대해 RaiseBubbleEvent 메쏘드를 호출하고 부모 컨트롤은 OnBubbleEvent 메쏘드를 오버라이드한다. 자식 컨트롤이 RaiseBubbleEvent를 호출하면 부모 컨트롤의 OnBubbleEvent를 호출하도록 되어 있다. 만약 부모 컨트롤의 OnBubbleEvent 메쏘드가 true를 반환하면 버블링은 중지되며 false를 반환하면 다시 상위 컨트롤로 이벤트는 계속 전파된다. OnBubbleEvent는 실제로 모든 웹 컨트롤의 베이스 클래스인 System.Web.UI.Control 클래스의 가상 메쏘드로서 특별히 이 메쏘드를 오버라이드하지 않으면 false를 반환한다(<리스트 2>). 이벤트 버블링을 통해 자식 컨트롤의 이벤트는 상위로 전파될 수 있는 것이다.
<리스트 2> 컨트롤 클래스의 RiaseBubbleEvent와 OnBubbleEvent 메쏘드
public class Control : IComponent, IDisposable, IParserAccessor,
IDataBindingsAccessor
{
protected void RaiseBubbleEvent(object source, EventArgs args)
{
Control parent = this.Parent;
while (parent != null) {
if (parent.OnBubbleEvent(source, args))
return;
parent = parent.Parent;
}
}
protected virtual bool OnBubbleEvent(object source, EventArgs args)
{
return false;
}
}
public class Control : IComponent, IDisposable, IParserAccessor,
IDataBindingsAccessor
{
protected void RaiseBubbleEvent(object source, EventArgs args)
{
Control parent = this.Parent;
while (parent != null) {
if (parent.OnBubbleEvent(source, args))
return;
parent = parent.Parent;
}
}
protected virtual bool OnBubbleEvent(object source, EventArgs args)
{
return false;
}
}
데이터그리드의 PageIndexChanged, SelectedIndexChanged, SortCommand, EditCommand, UpdateCommand, CancelCommand, DeleteCommand 이벤트는 모두 이 이벤트 버블링을 통해 발생된다. 실제 이벤트를 수신하는, 즉 IPostBackEventHandler를 구현하는 클래스는 LinkButton 혹은 Button 클래스다. 이들은 Command 이벤트에 대해 이벤트를 버블링하며 데이터그리드 클래스의 OnBubbleEvent 메쏘드는 버블링된 이벤트를 검사하여 적절한 이벤트로 디스패치한다.
페이징에 사용되는 링크 버튼을 예로 들어 보자. 페이저 DataGridItem은 하나의 TableCell 컨트롤을 생성하며 이 컨트롤에 대한 초기화는 DataGrid 클래스의 InitializePager 메쏘드에서 수행된다. 이 메쏘드에서는 다음과 같은 코드로서 LinkButton을 생성하고 TableCell 컨트롤에 추가한다.
// cell 변수는 TableCell 컨트롤을 나타내고, pageNumber는 버튼의 페이지 번호 문자열이다.
btn = new DataGridLinkButton();
btn.Text = pageNumber;
btn.CommandName = "Page";
btn.CommandArgument = pageNumber;
btn.CausesValidation = false;
cell.Controls.Add(btn);
btn = new DataGridLinkButton();
btn.Text = pageNumber;
btn.CommandName = "Page";
btn.CommandArgument = pageNumber;
btn.CausesValidation = false;
cell.Controls.Add(btn);
만약 사용자가 LinkButton을 클릭하면 연결된 Click 이벤트 핸들러가 없으므로 아무런 작업도 하지 않을 것이다. 다만 이벤트 버블링에 의해 이벤트는 데이터그리드 클래스의 OnBubbleEvent 메쏘드가 호출될 것이며 이 OnBubbleEvent 메쏘드는 CommandName 속성, CommandArgument 속성을 이용하여 어떠한 이벤트인가를 판별해 내고 이벤트를 발생시킬 것이다.
데이터그리드 진단
이제 데이터그리드를 진단하기 위해 필요한 사항들을 모두 알아봤다. 완벽하지는 않지만 말이다. 모든 것을 다 알려주면 독자들도 재미없어 할 것 같아서 듬성듬성 틈을 두었다. 이제 앞서 언급했던 데이터그리드의 문제점을 진단해 보자.
많은 웹 컨트롤
데이터그리드의 구조는 간략하고 강력하게 구조화되어 있다. 즉 많은 일을 자식 컨트롤에게 위임함으로써 상당히 구조적인 방법으로 기능들을 구현하고 있다. 소위 말하는 객체 지향적인 방법을 충실히 따라 설계됐고 구현됐다고 볼 수 있는 것이다. 하지만 이 때문에 문제가 발생한다. 데이터그리드가 생성하는 웹 컨트롤의 숫자는 매우 많다. 데이터 아이템의 수가 20(PageSize = 20 정도로 생각하면 되겠다)이고 컬럼이 10개라고 했을 때 생성되는 컨트롤의 갯수는 200개가 훌쩍 넘어 버린다.
왜 웹 컨트롤의 갯수가 많으면 문제가 되는가? 앞서 <표 1>에서 나타냈듯 웹 컨트롤은 일련의 제어 흐름을 가진다. 모든 컨트롤에 대해 OnInit, LoadViewState, OnLoad, OnPreRender, SaveViewState, Render 메쏘드가 호출된다. 비록 아무런 일도 하지 않더라도 컨트롤 클래스가 이들에 대한 기본 구현을 제공하므로 일정 프로세싱 파워를 소비하게 된다. 가랑비에 옷 젖는다는 말이 있듯 이렇게 많은 컨트롤을 자식 컨트롤로 가지고 있는 데이터그리드는 오버헤드를 갖지 않을 수 없다.
거대한 ViewState
데이터그리드의 ViewState는 크다. 데이터 아이템이 10여 개가 넘고 컬럼이 10여 개 정도 되면 10KB가 넘는 ViewState를 생성하기도 한다. 심지어는 HTML 태그들 보다 ViewState가 더 큰 페이지도 있을 수 있다. 이렇게 ViewState가 큰 이유는 데이터그리드가 데이터 아이템의 갯수, 컬럼 정보 등 필수적인 데이터를 ViewState에 기록할 뿐만 아니라, HeaderStyle, FooterStyle 등 각종 스타일까지도 ViewState에 기록한다. 더욱 좋지 못한 것은 자식 컨트롤까지 ViewState에 자신의 상태정보를 기록한다는 것이다. 실제로 데이터그리드는 자식 컨트롤의 상태 정보에 관여하지 않는다. 어떤 셀의 데이터가 포스트 백 사이에 유지되고 있다면 그것은 TableCell이 스스로 ViewState에 자신의 상태를 기록한 것이다. 따라서 ViewState의 크기는 눈덩이처럼 불어나게 된다. ViewState를 웹 페이지에 렌더링하기 위해서는 일련의 직렬화(serialization)화 작업을 수행해야 하며, 이를 다시 Base64 인코딩을 수행해야 한다. 게다가 ViewState가 임의로 변경되었는가를 확인하기 위해 해시 작업 역시 수행된다. ViewState가 크면 클수록 처리에 필요한 CPU 파워는 증가하기 마련이다.
"ASP.NET" 카테고리의 다른 글
- ASP.NET Execution Model (4)2006/07/30
- 풍성한 ASP.NET 개발환경의 이해 1 - 4 (0)2006/03/17
- 풍성한 ASP.NET 개발환경의 이해 1 - 3 (0)2006/03/17
- 풍성한 ASP.NET 개발환경의 이해 1 - 2 (0)2006/03/17
- 풍성한 ASP.NET 개발환경의 이해 1 - 1 (0)2006/03/17

수안이의 컴퓨터 연구실



Leave your greetings.