1. INNER JOIN 은 교집합
   A 테이블과 B 테이블을 INNER JOIN 했을때 ( INNER JOIN ON A.aa = B.aa )
  aa칼럼 값이 A테이블과 B 테이블에 동시에 있는 행 출력.

2. OUTER JOIN 은 부분 합집합(기본 LEFT OUTER JOIN)
   A 테이블과 B 테이블을 OUTER JOIN 했을때 ( OUTER JOIN ON A.aa = B.aa )
  A 테이블의 aa 칼럼이 모두 출력

출처 : http://cafe.daum.net/aspdotnet/OW7m/29 

페이지 안에 UpdatePanel 로 값을 넘겨주는 부분을 기술하고자 합니다. 이는 버튼으로는 충분하지

않습니다. 먼저 hidden TextBox 를 하나 추가하고 doPostBack 메소드를 호출하기 전에 그 값을 update 합니다.


hidden TextBox 추가하기


TextBox 는 값을 저장하는데 필요한 부분이며, 페이지에 보이지 않게 하기 위해서 CSS 로 숨겼습니다.

<asp:TextBox ID="text" runat="server" style="display:none;"></asp:TextBox>
<asp:Button ID="button" runat="server" 0nClick="button_Click" style="display:none;"/>


UpdatePanel 을 위한 비동기 trigger 등록하기

<asp:UpdatePanel runat="server" ID="UpdatePanel1" >
<ContentTemplate>
<asp:Label ID="label" runat="server"></asp:Label>
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="button" EventName="Click" UpdateMode="Conditional" />
</Triggers>
</asp:UpdatePanel>





button 의 Click 이벤트에 AsyncPostBackTrigger 가 등록되어 있는것을 보실 수 있습니다.

발생된 이벤트로 의해 textbox 값으로 수정하게 될 부분을 label 에 추가합니다.
UpdateMode 를 꼭 Conditional 로 등록하는것을 잊지 마세요. 만약에 그 부분을 잊어 버리게 되면

UpdatePanel 이 trigger 가 발생될때나 postback 이 안쪽에 있을때 refresh 가 될것입니다.

UpdatePanel 가 refresh 할 자바스크립트 함수를 추가 합니다.

<a href="java_script:UpdPanelUpdate('Value passed 자바스크립트')">Update the Panel</a>

function UpdPanelUpdate(value)
{
var obj = document.getElementById("<%= text.ClientID %>");
obj.value=value;
__doPostBack("<%= button.ClientID %>","");
}
</script>

컨트롤의 ClientID 프로퍼티를 사용하면 컨트롤이 마스터페이지 안에 있다고 해도 정확한 ID 값을 얻을 수 있습니다. 이는 웹컨트롤이나 Repeater 또는 ID 가 변경될때 마다 .... 말이죠.

버튼이 postback 이 될때 hidden textbox 에 값을 설정합니다.


UpdatePanel 에 내용을 update 하기


그리고 마지막으로 클라이언트 단에서 자바스크립트 메소드로 인해 설정된 값을 기본으로 UpdatePanel 의 내용이 update 됩니다.

protected void button_Click(object sender, EventArgs e)
{
label.Text = text.Text;
}


이는 hidden textbox 에 설정된 값으로 UpdatePanel 안에 있는 label 이 text 로 인해 변경됩니다.

그리고 비동기 postback 을 통해서 전달합니다.


출처 : http://uzys.tistory.com/438

Invoke, MethodInvoker, BeginInvoke - EndInvoke

 

  UI Control들은 폼 구동시 실행되는 하나의 쓰레드에서 구동된다. 따라서 사용자가 실행시킨 쓰레드는 별도로 실행 되기 때문에 이 메인 쓰레드에 적절한 마샬링 없이 다른쓰레드에서 직접 접근하면 다른 쓰레드를 침범하는 것이다. (Cross Thread Problem) 이런 경우에는 프로그램이 개발자가 설계한대로 잘 동작하지 않을 수 있다.(Race Condition,DeadLock)  따라서, 안전하게 동작하게 하기위하여 .Net 환경에서는 Invoke를 제공하고 있다.

 본 내용을 무시한 채 프로그램을 작성하면 InvalidOperationException을 발생시키고 . Debug 창에서 "컨트롤이 자신이 만들어진 스레드가 아닌 스레드에서 액세스되었습니다."라는 메세지가 표시 된다. 하지만 디버깅 환경이 아니라면 프로그램은 겉보기에 정상 동작하는 것 처럼 보일 수 있으므로 조심해야 된다. !

 

난 대충 좀 안되도 상관 없다고 생각하시는 분들은 이 예외를 비활성화 할 수도 있습니다.

 

CheckForIllegalCrossThreadCalls 속성 값을 false로 설정하여 이 예외를 비활성화할 수 있습니다. 그러면 컨트롤이 Visual Studio 2003에서와 같은 방식으로 실행됩니다.

 

 자 그럼 쓰레드에서 안전하게 Windows Form control을 제어하게 하는 방법에 대해 알아보자

 

InvokerRequired  속성은 Invoke메쏘드를 호출해야되는지 알려준다.

 

Invoke

 

 쓰레드에서 폼 컨트롤 하기 위해서는 별도의(SetTextononTextBox1) 메쏘드를 만들어 사용하면 좋다. 예를들어, Textbox에 값을 입력한다면 아래와 같은 'SetTextonTextBox1' 메쏘드를 만들어서 사용하면 Cross thread 환경이든 내부 쓰레드 사용환경에서든 둘다 사용할 수 있다.

 Invoke를 사용할 때 인자를 넘겨줘야 하는 메쏘드 필요하면 할때는 반드시 delegate를 선언한 후에 사용하여야 한다.

 

  1.         delegate void SetTextCallback(string txt);                    
            private void SetTextonTextBox1(string txt)
            {
                if (this.textBox1.InvokeRequired)
                {
                    this.Invoke(new SetTextCallback(SetTxtCB), new object[] { txt }); //그냥 txt를 넘겨줘도 된다
                }
                else
                {
                    this.textBox1.Text += txt;
                }
            }
            private void SetTxtCB(string txt)
            {
                this.textBox1.Text+=txt;
            }

 

인자가 없는 메쏘드를 호출할 때는 간단하게 MethodInvoker를 사용하면 좋다.

 

  1.         private void SetTextonTextBox1(string txt)
            {
                if (this.textBox1.InvokeRequired)
                {
  2.                this.Invoke(new MethodInvoker( delegate { this.textBox1.Text+=txt; }) );
  3.             }
                else
                {
                    this.textBox1.Text += txt;
                }
            }

 

BeginInvoke , EndInvoke

 

 우선 BeginInvoke를 설명하기 전에 Windows Form 의 Control.BeginInvoke 메쏘드와 Delegate.BeginInvoke 메쏘드에 대해서 차이점을 설명하고 본 단락에서는 Winfows Form Control의 BeginInvoke만 설명하도록 하겠다.

 Delegate.BeginInvoke는 Asynchronous Delegate를 만들어서 콜백을 하는 것이라고 생각하면 되겠다. 다시말해서, CLR에서 관리하는 쓰레드풀에서 해당 메쏘드를 큐잉한다. 쉽게 얘기해서 별도의 쓰레드를 만들어서 Delegate를 실행한다고 보면 된다.  장점은 IAsyncResult를 이용해서 Object 결과값을 넘겨 받을 수 있다. 보다 자세한 사항은 이곳을 참조!

  Windows Form의 Control.BeginInvoke는 Control 내부 핸들이 작성된 쓰레드(메인쓰레드)에서 지정된 대리자를 비동기식으로 실행한다. 비동기식이므로 실행을 대기하지 않고 BeginInvoke는 즉시 Return 한다.

 차이점을 정리 하자면 Control.BeginInvoke는 실행코드의 GUI Thread에 작성된 코드이고 Delegate.BeginInvoke는 쓰레드풀 쓰레드에 사용된다. 또한 Control.BeginInvoke의 경우는 EndInvoke를 호출하지 않아도 되나 Delegate.BeginInvoke의 경우는 반드시 Delegate.EndInvoke를 호출해줘야 한다 안그러면 메모리 릭이 발생한다.

 

 

  1. public delegate void InvokeDelegate();
  2. private void Invoke_Click(object sender, EventArgs e)
    {
       myTextBox.BeginInvoke(new InvokeDelegate(InvokeMethod));
    }
    public void InvokeMethod()
    {
       myTextBox.Text = "Executed the given delegate";
    }
  3.  

 

 Endinvoke

 References
  1. http://msdn.microsoft.com/ko-kr/library/ms171728.aspx
  2. http://xmlangel.textcube.com/6
  3. http://jongkok4.net/entry/펌c-UI-쓰레드-마샬링-Invoke-BeginInvoke?TSSESSIONjongkok4net=6e5ec00b34c31e0126e9a64412f7a627
  4. http://timl.net/2008/01/begininvoke-methodinvoker-and-anonymous.html
  5. http://shiman.wordpress.com/2008/09/10/c-net-delegates-asynchronous-invocation-begininvoke-method/
  6. http://msdn.microsoft.com/ko-kr/library/0b1bf3y3(VS.80).aspx
  7. http://kristofverbiest.blogspot.com/2007/02/don-confuse-controlbegininvoke-with.html
  8. http://www.albahari.com/threading/#_Introduction
  9. http://www.yoda.arachsys.com/csharp/threadstart.html 

.NET 환경에서 개인화 주소 구현

 

1.개요

 

닷넷 환경에서 개인화 주소를 어떻게 개발하고 어떤 이점들이 있는지 알아보겠습니다.

블로그나 기타 사이트들을 보면 개인화 주소를 사용하는 것을 볼 수 있습니다.

http://www.domain.com/blogid/postno형태의 구조를 가지는 개인화 주소도 있고,

http://www.blogid.com/postno형태의 구조를 가지는 개인화 주소도 있습니다.

이리저리 알아본 결과 이런 형태의 개인화 주소를 fancy url 이라고 합니다.

자바에서 주로 쓰이는 용어인 듯한데 용어의 의미가 닷넷이나 자바에 구애받지 않아 fancy url 이라는 용어로 사용할 것입니다.

이 문서는 fancy url의 의미를 알아보고 fancy url을 사용하는 이유와 어떻게 fancy url을 구현하는지에 대해서 알아보겠습니다.

 

2. Fancy Url

 

2.1. 의미

 

URL 주소를 표시할 때 특수 문자나 복잡한 문자열을 사용하지 않고 단순히 영문자와 숫자

그리고 / 문자로 구분하여 이를 짧게 표기하는 방식을 말합니다.

확장자나 GET 방식의 쿼리 스트링에 대한 표시를 단순화 하는 URL 표기법입니다.

 

2.2. 이점

 

l  URL의 단순 명료함

유저가 GET 방식의 복잡한 쿼리 스트링을 외워서 원하는 컨텐츠에 접근하기란 불가능에 가깝다고 볼 수 있습니다.

 

l  검색 엔진에서의 우선순위

대부분의 검색 엔진은 복잡한 쿼리스트링이 표시된 웹 페이지보다 fancy url 의 우선순위가 높다고 합니다.

, 같은 컨텐츠의 내용이라도 쿼리 스트링을 포함한 url보다 fancy url이 더 상위에 노출됩니다.

 

3. Fancy Url 구현

 

3.1. 원리

 

예를 들어 http://www.domain.com/blogid 라는 개인화된 주소가 있을 때 이 페이지의 실제 URL주소는 http://www.domain.com/blog/view.aspx?bid=blogid 입니다.

Front 단에서 fancy url 형태인지를 판별하여 fancy url이라면 blogid 값을 얻어

http://www.domain.com/blog/view.aspx?bid=blogid형태로 보여주게 됩니다.

주소창의 주소유지를 위해 frameset 이나 iframe 등으로 처리하여 웹 페이지에 보여주게 됩니다.

 

3.2. 구현

 

3.2.1. IIS 설정

 

fancy url은 확장자 없는 주소이기 때문에 IIS로 요청이 발생할 때 404 에러를 발생시킵니다.

그렇다고 모든 확장자에 대한 확장자 매핑을 해주게 된다면 isapi 필터를 통해 이미지 파일들이나 js, css, html 등등 서버에서 다 처리를 해줘야 하므로 서버에 부하가 많아지게 됩니다.

서버의 성능이나 여러 상황들을 가정했을 때 웹 서버에서 들어온 요청이 404 에러일 경우 Fancy Url인지 아닌지를 체크하는 방식이 제일 깔끔한 것 같아 이런 방식으로 개발했습니다.

IIS에서 404에러일 경우 특정 페이지(aspx)로 리디렉션 시켜주면 서버에서 처리를 하게 됩니다.

web.config에서 설정할 수도 있지만 보통 로컬에서 테스트할 때 CustomError 속성을 RemoteOnly로 하기 때문에

에러 페이지가 발생하여 디버깅 환경이 힘듭니다.

 

IIS 실행 ?적용할 사이트의 속성 클릭 ? 사용자 지정 오류 탭 ? 404 수정 ? NullPage.aspx ? 적용

실제로 NullPage.aspx 페이지는 서버에 존재하지 않는 페이지입니다.

필자가 개발중엔 사이트의 Fancy Url Global.asax에서 처리하기 때문에 NullPage.aspx 페이지로 리디렉션해도

어차피 isapi 필터를 통해 웹서버에 요청하게 되고 Global.asax를 수행하게 됩니다.

Global에서 처리하지 않고 실제로 NullPage.aspx 페이지를 생성하여 처리해도 무방합니다.

사이트의 성격이 fancy url의 접근도가 낮다면 NullPage.aspx에서 구현하는 것이 바람직하지만,

개발중인 사이트는 접근도가 많기 때문에 Global에서 체크하여 처리하도록 하였습니다.

자신의 사이트의 성격에 맞게 개발하시면 될 듯 합니다.

 

3.2.2. Global.asax

 

실질적으로 Fancy Url 을 담당하는 놈이 Global.asax Application_BeginRequest 이벤트입니다.

개발중인 사이트의 Fancy Url 구조는 http://www.domain.com/blogid/postno 이기 때문에

“/” 문자 배열로 받아 저장하여 요청 들어온 Url을 분석합니다.

만일 “/” 문자열이 없다면 이는 Fancy Url 이 아닌 일반 쿼리스트링을 포함한 Url이 될 것이고

하나라면 www.domain.com/blogid 형태의 Fancy Url이 될 것입니다.

두개라면 www.domain.com/blogid/postno 형태겠죠..

 

설명만으로는 힘든 부분이 많아 소스를 발췌하여 설명드리겠습니다.

 

 

ArrayList pathList = new ArrayList();

string path = Request.RawUrl;

string[] arrayPath = path.Split('/');

for (int i = arrayPath.Length - 1; i >= 0; i--)

{

             if (arrayPath[i] != string.Empty && arrayPath[i] != "ePlayon.Batoo.WEB")

             {

                           if (arrayPath[i].IndexOf(".") == -1 && arrayPath[i] != "jsdebug")

                           {

                                        pathList.Add(arrayPath[i]);

                           }

                           else

                           {

                                        break;

                           }

             }

}

 

위 소스는 현재 요청 URL 을 분석하여 pathList 라는 배열리스트에 FancyUrl 구조를 담습니다.

앞서 말했듯이 pathList의 카운트가 0이라면 일반 URL이고 1이면 블로그, 2라면 포스트 요청을 수행하게 됩니다.

 

 

if (pathList.Count == 1)

{//FancyUrl

             LockerRoomManager manager = new LockerRoomManager();

             string lno = manager.GetLockerRoomGuidByUrl(pathList[0].ToString());

 

             if (lno != string.Empty)

             {

                           StringBuilder retHtml = new StringBuilder();

                           retHtml.Append("<html xmlns='http://www.w3.org/1999/xhtml' >\n");

                           retHtml.Append("\t<head>\n");

                           retHtml.Append("\t\t<title>" + pathList[0].ToString() + " ?쎌빱?/title>\n");

                           retHtml.Append("\t\t<frameset rows='0,*' border=0>\n");

                           retHtml.Append("\t\t\t<frame src='#' name=main>\n");

                           retHtml.Append("\t\t\t<frame src='../LockerRoom/PostList.aspx?lno=" + lno + "'>\n");

                           retHtml.Append("\t\t</frameset>\n");

                           retHtml.Append("\t</head>\n");

                           retHtml.Append("\t<noframes>\n");

                           retHtml.Append("\t\t<body>\n");

                           retHtml.Append("\t\t\t<div></div>\n");

                           retHtml.Append("\t\t</body>\n");

                           retHtml.Append("\t</noframes>\n");

                           retHtml.Append("</html>\n");

                           Response.Write(retHtml.ToString());

                           Response.End();

             }

             else

             {

                           if (path.IndexOf(".asmx") == -1)

                           {

                                        Response.Redirect("~/Error/404.html");

                           }

             }

}

 

위 소스는 블로그 Fancy Url일 경우 처리하는 로직입니다.

블로그 아이디를 얻어와서 DB에 해당 블로그의 아이디가 있는지 검사합니다.

있다면 StringBuilder 객체로 html 코드를 생성하여 뿌려줍니다.

Frameset을 이용했는데 이를 이용함으로써 주소창의 주소가 계속 유지할 수 있습니다.

만일 블로그 아이디가 없다면 404에러 페이지로 리디렉션 시킵니다.

 

포스트 Fancy Url도 블로그 Fancy Url과 형태가 같습니다.

이렇게 함으로써 정상적으로 Fancy Url의 처리를 완료 할 수 있습니다.

 

4. 마무리

Fancy Url을 개발하면서 많은 삽질을 해야 했습니다.

처음에 쉽게 구현이 될 줄 알았는데 처음 생각과 달리 HttpHandler HttpModule을 사용하여 개발하려고도 해봤고 실제 이렇게 개발하고 나니 프로젝트 관리에 문제가 있어 처음대로 Global로 선회하였습니다.

 

 

--------------------------------------------

written by 허둥사마

http://tit99hds.egloos.com/

-------------------------------------------- 


■ 간단한 Session 생성해보자.

     1. aspx 파일을 생성

 

     2. Label 생성

 

     3. 디자인화면에서 더블클릭해서 코딩을 하자.

        private void Page_Load(object sender, System.EventArgs e)
        {
             Label1.Text = Session.SessionID;
             Session.Timeout = 5; // 5분 뒤 소멸하겠다는 뜻.
             Session["num"] = 100;  // 값을 입력하고..
             Label1.Text += Session["num"].ToString();
// 값을 꺼내오자  

        }

 

     4. 브라우저를 띄워보자.

        

 

 

 

 

 

■ Session 을 내 컴이 아닌 다른 컴에 저장시켜보자.

     클라이언트 100명이 서버에 달라붙어서 작업을 하고 있었어.

     순간적으로 전기가 나갔고 1분만에 복구를 한다면..

     기본 사람들이 전부 '로그아웃'이 되어버려..

 

     근데 Session을 다른 컴에 옮겨놓으면 전기가 나가도 Session 이 유지되지.

     다만 전원이 나간 경우에 새로들어온 사용자들은 불편을 느끼겠지.

 

     ▶ 이게 세션을 분리시키는 첫번째 방법이야.

 

 

     ▶ 이번에는 세션을 DB에 저장을 시키는 두 번째 방법이야. 꾸준한 기록관리가 가능하지..(이런 방법이 많지는 않어.)

         DB 에 세션을 보관한다고 치면 웹서버가 여러개더라도 상관이 없어. 공유를 시켜놓으면 가능하다는 거지.

 

    

    ※ Web.config를 간단히 살펴보면

        sessionState

             mode = "Off | InProc | StateServer | SQLServer";  // 세션이 필요없다면 끄라는거야. 세션은 서버메모리에 상주하거든.

                                     ↑ InProc는 서버메모리에 '세션'을 올려다 쓰기에 가장 빠르고 보안측면에서 가장 좋아.

                                         안정성면에 있어서는 떨어져. 웹서버가 죽으면 세션도 같이 죽어버리거든..

              

                                     ↑ StateServer 로 할려면 관리도구에서  를 선택하고

                                        를 선택한 다음..

                                        

                                         '시작'을 클릭...

                                         웹서비스에만 문제가 있어도 세션이 유지가 되는 이점이 있어.

                                         의도적으로 웹서비스를 중지하려면  에서

                                          을 중지했다가 다시 시작버튼을 누리면 되고...

 

                                         즉, 웹서비스가 중지되었다고 시작되면 세션의 정보가 원래는 날라가게 되어있지.

                                              글을 쓰다가 웹서비스중단되면 쓰던 글이 다 날라간다는 뜻이야..

                                         근데,  녀석으로 셋팅을 한다면

                                              웹서비스가 죽더라도 클라이언트는 모르고 '세션'도 죽지않고 살아있다는 거야..

                                              '세션'을 관리하는 프로세서는 살아있기 때문에 가능한거야..

 

 

                                     ↑ SQLServer 를 사용할려면 쿼리분석기를 일단 실행시켜.

                                        C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322 에 위치한  를 복사해서

                                        쿼리분석기에서 실행시켜보면 먼가 생성이 된것을 확인 할 수 있어.

                                        요점은.... '세션'을 DB에 저장시켜놓고 관리하다는 거지.

 

 

 

▶▶ 솔루션탐색기에서  을 열어보자.

 

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
   
  <system.web>

    <!--  동적 디버깅 컴파일
          ASPX 디버깅을 사용하려면 compilation debug="true"로 설정합니다. 이렇게 하지 않고
          해당 값을 false로 설정하면 이 응용 프로그램의 런타임 성능이 향상됩니다.
          컴파일된 페이지에 디버깅 기호(.pdb 정보)를 삽입하려면 compilation debug="true"로 설정합니다.
          이렇게 하면 파일 크기가 커져서 실행 속도가 느려지므로
          디버깅하는 경우에만 이 값을 true로 설정하고
          디버깅하는 경우 외에는 모두 false로 설정합니다. 자세한 내용은 ASP.NET 파일 디버깅에 관한
          설명서를 참조하십시오.
    -->

    <compilation
         defaultLanguage="c#"
         debug="true"
    />

    <!--  사용자 지정 오류 메시지
          사용자 지정 오류 메시지를 사용하려면 customErrors mode="On" 또는 "RemoteOnly"로 설정하고 사용하지 않으려면 "Off"로 설정합니다.
          처리할 각 오류에 <error> 태그를 추가하십시오.

          "On" 항상 사용자에게 친숙한 메시지가 표시됩니다.
          "Off" 항상 자세한 ASP.NET 오류 정보가 표시됩니다.
          "RemoteOnly" 로컬 웹 서버에서 액세스하지 않는 사용자에게만 사용자에게 친숙한
           메시지가 표시됩니다. 이 설정은 원격 클라이언트에서 응용 프로그램의 자세한 정보가
           표시되지 않도록 보안을 강화할 때 사용하면 좋습니다.
    -->
    <customErrors
    mode="RemoteOnly"
    />

    <!--  인증
          이 섹션에서는 응용 프로그램의 인증 정책을 설정합니다. 가능한 모드로는 "Windows",
          "Forms", "Passport" 및 "None"이 있습니다.

          "None"을 지정하면 인증이 수행되지 않습니다.
          "Windows"를 지정하면 IIS가 인증 설정에 따라 인증을 수행합니다(기본, 다이제스트 또는 Windows 통합 인증).
           익명 액세스는 IIS에서 비활성화시켜야 합니다.
          "Forms"를 지정하면 사용자에게 자격 증명을 입력할 수 있는 사용자 지정 양식(웹 페이지)를
           표시하여 사용자를 응용 프로그램에서 인증합니다. 사용자 자격 증명 토큰은 쿠키에 저장됩니다.
          "Passport"를 지정하면 인증이 Microsoft에서 제공하는 중앙 집중식 인증 서비스를 통해 수행되며,
           이 서비스는 멤버 사이트에 대해 단일 로그온과 핵심 프로필 서비스를 제공합니다.
    -->


    <authentication mode="Windows" />

 <!--  권한 부여
           이 섹션에서는 응용 프로그램의 권한 부여 정책을 설정합니다. 사용자 또는 역할에 따라 응용 프로그램 리소스에 대한
          액세스를 허용하거나 거부할 수 있습니다. 와일드카드 "*"는 모든 사용자를 의미하고 "?"는 인증되지 않은
          익명의 사용자를 의미합니다.
    -->

    <authorization>
        <allow users="*" /> <!-- 모든 사용자를 허용합니다. -->
            <!--  <allow     users="[쉼표로 구분된 사용자 목록]"
                             roles="[쉼표로 구분된 역할 목록]"/>
                  <deny      users="[쉼표로 구분된 사용자 목록]"
                             roles="[쉼표로 구분된 역할 목록]"/>
            -->
    </authorization>

 

    <!--  응용 프로그램 수준 추적 로깅
          응용 프로그램 수준 추적을 사용하면 응용 프로그램 내의 모든 페이지에 대한 추적 로그 출력이 가능합니다.
          응용 프로그램 추적 로깅을 사용하려면 trace enabled="true"로 설정합니다. pageOutput="true"이면
          각 페이지의 아래쪽에 추적 정보가 표시됩니다. 그렇지 않으면
          웹 응용 프로그램 루트에서 "trace.axd" 페이지를 찾아서 응용 프로그램 추적 로그를
          볼 수 있습니다.
    -->

    <trace
        enabled="false"
        requestLimit="10"
        pageOutput="false"
        traceMode="SortByTime"
  localOnly="true"
    />

 

 

    <!--  세션 상태 설정
          기본적으로 ASP.NET에서는 쿠키를 사용하여 특정 세션에 속한 요청을 식별합니다.
          쿠키를 사용할 수 없는 경우 세션 식별자를 URL에 추가하여 세션을 추적할 수 있습니다.
          쿠키를 사용하지 않으려면 sessionState cookieless="true"로 설정합니다.

                     쿠키를 안쓴다면 브라우져 주소창을 보면 get방식으로 데이터를 주고받어..

                     근데, 거의 이렇게 쓸 일이 없다고 보면 되고..
    -->

    <sessionState
            mode="InProc"
            stateConnectionString="tcpip=127.0.0.1:42424"
            sqlConnectionString="data source=127.0.0.1;Trusted_Connection=yes"
            cookieless="false"
            timeout="20"
    />

 

    <!--  전역화
          이 섹션에서는 응용 프로그램의 전역화 설정을 설정합니다.
    -->

    <globalization
            requestEncoding="utf-8"
            responseEncoding="utf-8"
   />
  
 </system.web>

</configuration>


ASP.NET의 계층적 데이터 바인딩

Fritz Onion
DevelopMentor

적용 대상 :
Microsoft ASP.NET

요약 : 3차원 이상인 계층적 성격의 데이터 원본에 ASP.NET 데이터 바인딩을 수행하는 기술에 대해 알아봅니다.

HierarchicalDataBindingSample.msi를 다운로드하십시오.

목차

소개
데이터 바인딩
계층적 데이터
계층적 데이터베이스 데이터에 바인딩
XML 데이터에 바인딩
중첩된 컨트롤 액세스
DataGrid 및 DataList 계층적 바인딩
제한 및 효율성
결론

소개

ASP.NET은 데이터를 서버 쪽 컨트롤에 바인딩하기 위한 편리한 아키텍처를 제공하며 이러한 컨트롤은 설계된 표시 형식에 상관없이 클라이언트에 렌더링됩니다. ASP.NET에서 데이터 바인딩 예제 대부분은 데이터베이스에 대한 쿼리의 결과로 얻어진 플랫 데이터 소스에 대한 바인딩 예제입니다. 대부분의 응용 프로그램에서 이 유형의 데이터 바인딩이 가장 일반적이기는 하지만 간단한 2차원 공간에 데이터가 적합하지 않고 표준 데이터 바인딩 기술로 충분하지 않은 경우가 있습니다.

이 기사는 3차원 이상인 계층적 성격의 데이터 소스에 데이터 바인딩을 수행하는 기술에 대해 설명합니다.

데이터 바인딩

ASP.NET의 데이터 바인딩은 서버의 데이터를 서버 쪽 컨트롤로 바인딩하는 프로세스로 이때 해당 데이터를 특정 형태로 클라이언트에 렌더링합니다. 데이터 바인딩에는 서버 쪽 컨트롤이 DataSource 속성과 DataBind() 메서드를 지원하고 컨트롤이 바인딩되는 데이터 소스가 IEnumerable 인터페이스를 구현해야 한다는 유일한 제약 조건이 따릅니다.

참고 이 제약 조건에는 DataSetDataTable이라는 주목할 만한 예외가 있습니다. 둘 다 직접 바인딩될 수 있으므로 기본 테이블의 기본 DataView에 바인딩하며 DataViewIEnumerable을 구현합니다. DataSetDataTable은 흔히 데이터 바인딩에서 데이터 원본으로 사용되므로 이러한 특징은 사용의 편리함을 위한 것입니다.

데이터를 컨트롤에 바인딩하려면 데이터 소스를 컨트롤의 DataSource 속성에 할당하고 해당 DataBind() 메서드를 호출합니다.

예를 들어 ArrayListItem 클래스의 전체 인스턴스를 반환하는 다음 데이터 소스가 있다고 가정합니다.

  public class Item
  {
    private string _name;
    public Item(string name) { _name = name; }

    public string Name { get { return _name; } }
  }

  public class TestDataSource
  {
    public static ArrayList GetData()
    {
      ArrayList items = new ArrayList();
      for (int i=0; i<10; i++)
      {
        Item item = new Item("item" + i.ToString());
        items.Add(item);
      }
      return items;
    }
  }

ArrayListIEnumerable을 구현하므로 TestDataSource 클래스의 GetData() 메서드 결과는 바인딩을 위한 유효한 데이터 소스입니다. 데이터를 바인딩할 서버 쪽 컨트롤로 Repeater를 사용하며 이때 열거할 수 있는 데이터 소스의 각 항목을 렌더링하는 방법을 설명하는 ItemTemplate을 제공해야 합니다. 아래 샘플은 해당 텍스트가 바인딩 대상인 Item 클래스 인스턴스의 Name 속성으로 설정된 CheckBox 컨트롤을 렌더링합니다.

<asp:Repeater Runat="server" ID="_itemsRepeater" 
              EnableViewState="false">
  <ItemTemplate>
    <asp:CheckBox Runat="server" 
 Text='<%# DataBinder.Eval(Container.DataItem, "Name") %>'
  />
    <br/>
  </ItemTemplate>
</asp:Repeater>

이제 마지막으로 Repeater에 대해 실제 데이터 바인딩을 수행합니다. 이 작업은 일반적으로 다음과 같이 Page 파생 클래스의 Load 처리기에서 수행됩니다.

    private void Page_Load(object sender, EventArgs e)
   {
      _itemsRepeater.DataSource = TestDataSource.GetData();
      _itemsRepeater.DataBind();
    }

다음은 이 페이지의 샘플 렌더링을 보여 줍니다.

그림 1. 데이터 바인딩 페이지

계층적 데이터

위에서 살펴본 첫 번째 데이터 소스 샘플은 데이터 수준이 하나뿐인 플랫입니다. 이제 다음과 같이 데이터 소스의 각 항목에 하위 항목 컬렉션을 추가한다고 가정합니다.

public class Item
{
  string _name;
  ArrayList _subItems = new ArrayList();

  public Item(string name) { _name = name; }

  public string    Name     { get { return _name;     } }
  public ArrayList SubItems { get { return _subItems; } }
}

이어서 다음과 같이 인위적인 데이터 소스 채우기 메서드인 GetData에서 각 항목에 5개의 하위 항목을 추가합니다.

public class TestDataSource
{
  public static ArrayList GetData()
  {
    ArrayList items = new ArrayList();
    for (int i=0; i<10; i++)
    {
      Item item = new Item("item" + i.ToString());
      for (int j=0; j<5; j++)
      {
        Item subItem = new Item("subitem" + j.ToString());
        item.SubItems.Add(subItem);
      }
      items.Add(item);
    }
    return items;
  }
}

이제 데이터 구조는 최상위 항목 컬렉션 아래에 하나의 수준이 있는 계층입니다. 이전에 수행했던 데이터 바인딩이 계속 제대로 작동하지만 렌더링 시에 하위 항목을 무시하여 첫 번째 데이터 수준만 표시합니다. 모든 데이터를 적절히 표시하기 위해 이 지점에서 각 항목의 하위 항목에서 중첩된 데이터 바인딩을 수행해야 합니다. 논리적으로 기존 RepeaterItemTemplate에 데이터 바인딩된 다른 컨트롤을 배치하여 최상위 Repeater에 의해 나열된 각 ItemSubItems 컬렉션에 바인딩해야 한다는 의미입니다. 중첩된 Repeater를 추가하여 .aspx 파일에서 선언적으로 이 작업을 수행할 수 있습니다. 중첩된 RepeaterDataSource 속성에 바인딩되는 ItemSubItems 컬렉션을 올바르게 매핑하는 부분이 유일하게 까다로운 작업입니다. 이 작업을 수행하려면 다음과 같이 중첩된 RepeaterDataSource 속성을 데이터 바인딩 식으로 선언적으로 설정하여 결과적으로 SubItems 컬렉션이 되게 합니다.

<asp:Repeater Runat="server" ID="_itemsRepeater" 
              EnableViewState="false">
  <ItemTemplate>
    <asp:CheckBox Runat="server" 
    Text='<%# DataBinder.Eval(Container.DataItem, "Name") 
      %>' 
         />
    <asp:Repeater Runat="server" ID="_subitemsRepeater"
                  EnableViewState="false"
         DataSource=
   '<%# DataBinder.Eval(Container.DataItem, "SubItems") %>'>
         <ItemTemplate>
           <br/>???
           <asp:CheckBox Runat="server"
                         Text=
      '<%# DataBinder.Eval(Container.DataItem, "Name") %>' 
        />
         </ItemTemplate>
    </asp:Repeater>

    <br/>
  </ItemTemplate>
</asp:Repeater>

데이터 소스가 이미 최상위 Repeater에 바인딩되었으므로 코드 숨김 클래스를 변경할 필요가 없습니다. 최상위 컬렉션에서 Item당 한 번씩 데이터 바인딩이 중첩됩니다. 이렇게 데이터 바인딩된 중첩 컨트롤 쌍을 읽을 때는 가장 근접한 컨트롤이 데이터 바인딩 식(<%# %>)의 범위를 지정한다는 점에 유의해야 합니다. 이 예제에서 처음 두 개의 데이터 바인딩 식은 최상위 Repeater의 외부 데이터 바인딩으로 범위가 지정되며 최상위 컬렉션의 현재 항목을 확인합니다. 세 번째 데이터 바인딩 식은 내부 Repeater로 범위가 지정되며 바인딩된 현재 ItemSubItems 컬렉션에 있는 요소를 확인합니다. 다음은 이 페이지의 렌더링을 보여 줍니다.

그림 2. Repeater 샘플에 대한 데이터 바인딩을 포함한 페이지의 렌더링

이 중첩된 데이터 바인딩은 한 수준으로만 제한되지 않고 임의의 수준으로 확장될 수 있습니다. 데이터 바인딩된 컨트롤의 중첩이 데이터 소스의 컬렉션 중첩과 일치하고 데이터 소스 형태가 규칙적이면 바인딩이 작동합니다. 예를 들어 데이터 수준을 하나 더 포함하도록 데이터 소스를 확장하여 기존 SubItems 컬렉션의 각 Item에 고유한 SubItems 컬렉션을 제공할 수 있습니다.

public class TestDataSource
{
  public static ArrayList GetData()
  {
    ArrayList items = new ArrayList();
    for (int i=0; i<10; i++)
    {
      Item item = new Item("item" + i.ToString());
      for (int j=0; j<5; j++)
      {
        Item subItem = new Item("subitem" + j.ToString());
        item.SubItems.Add(subItem);
        for (int k=0; k<4; k++)
        {
          Item subsubItem = 
               new Item("subsubitem" + k.ToString());
          subItem.SubItems.Add(subsubItem);
        }
      }
      items.Add(item);
    }
    return items;
  }
}

이번에도 DataSource 속성이 두 번째 수준 Repeater에 의해 현재 열거되는 항목의 SubItems 속성에 바인딩되는 중첩 Repeater를 하나 더 추가하기만 하면 중첩된 새 데이터를 표시할 수 있습니다.

<asp:Repeater Runat="server" ID="_itemsRepeater"
              EnableViewState="false">
  <ItemTemplate>
    <asp:CheckBox Runat="server" 
 Text='<%# DataBinder.Eval(Container.DataItem, "Name") %>'
   />
    <asp:Repeater Runat="server" ID="_subitemsRepeater"
         EnableViewState="false"
         DataSource=
    '<%# DataBinder.Eval(Container.DataItem, "SubItems") %>'
      >
      <ItemTemplate>
        <br/>???
        <asp:CheckBox Runat="server"
             Text=
       '<%# DataBinder.Eval(Container.DataItem, "Name") 
         %>'/>
        <asp:Repeater Runat="server" EnableViewState="false"
             DataSource=
    '<%# DataBinder.Eval(Container.DataItem, "SubItems") 
      %>'>
          <ItemTemplate>
            <br/>??????
            <asp:CheckBox Runat="server"
                          Text=
      '<%# DataBinder.Eval(Container.DataItem, "Name") %>' 
        />
          </ItemTemplate>
        </asp:Repeater>
      </ItemTemplate>
    </asp:Repeater>
    <br />
  </ItemTemplate>
</asp:Repeater>

다음은 이 페이지의 렌더링 일부를 보여 줍니다.

그림 3. 중첩된 추가 Repeater를 보여 주는 페이지

계층적 데이터베이스 데이터에 바인딩

계층적 데이터 바인딩을 수행하는 방법에 대한 기본 사항을 이해했으므로 좀 더 실제적인 응용 방법을 살펴볼 차례입니다. 데이터 바인딩을 수행하면 대부분 데이터베이스 쿼리 결과에 대한 바인딩도 수행되므로 이번에는 데이터베이스에서 검색한 계층적 데이터를 사용합니다. 계층적 데이터는 일반적으로 테이블 간의 일대다 관계를 사용하여 관계형 데이터베이스에 저장됩니다. 예를 들어 기본 SQL Server 및 Microsoft Access 설치에서 사용할 수 있는 샘플 Northwind 데이터베이스에는 Customers 테이블과 Orders 테이블 간의 일대다 관계가 존재합니다. 마찬가지로 Orders 테이블과 Order Details 테이블 간에 일대다 관계가 존재합니다. 다음 그림에서는 이러한 관계를 보여 줍니다.

그림 4. 테이블 관계

이 데이터를 쿼리할 수 있는 여러 가지 방법이 있지만 세 테이블 모두의 내용을 DataSet로 가져오고 DataSet에서 관계를 정의하는 기능을 사용하여 데이트를 계층적으로 추출하면 가장 간단하게 데이터베이스 왕복 수를 1회로 줄일 수 있습니다. 다음 Load 처리기는 이 작업을 수행한 다음 결과 DataSet를 ID가 _customerRepeater인 Repeater에 바인딩합니다.

private void Page_Load(object sender, EventArgs e)
{
  string strConn = 
       "server=.;trusted_connection=yes;database=northwind";

  string strSql  = "SELECT CustomerID, CompanyName FROM " +
                   " Customers; "                         +
                   "SELECT OrderID, CustomerID, "         + 
                   "        EmployeeID FROM Orders;"      +
                   "SELECT OrderID, Products.ProductID,"  +
                   "ProductName, Products.UnitPrice FROM" +
                   " [Order Details], Products WHERE "    +
                   " [Order Details].ProductID = "        +
                   "Products.ProductID";

  SqlConnection conn = new SqlConnection(strConn);
  SqlDataAdapter da = new SqlDataAdapter(strSql, conn);
  da.TableMappings.Add("Customers1", "Orders");
  da.TableMappings.Add("Customers2", "OrderDetails");

  _ds = new DataSet();

  da.Fill(_ds, "Customers");

  _ds.Relations.Add("Customer_Order", 
        _ds.Tables["Customers"].Columns["CustomerID"], 
        _ds.Tables["Orders"].Columns["CustomerID"]);
  _ds.Relations[0].Nested = true;
  _ds.Relations.Add("Order_OrderDetail", 
        _ds.Tables["Orders"].Columns["OrderID"], 
        _ds.Tables["OrderDetails"].Columns["OrderID"]);
  _ds.Relations[1].Nested = true;

  _customerRepeater.DataSource = _ds.Tables["Customers"];
  _customerRepeater.DataBind();
}

데이터가 DataSet에 로드되면 설정된 관계를 사용하여 데이터를 계층적으로 탐색할 수 있습니다. 예를 들어 Customers 테이블에 있는 행이 DataSet에 입력된 경우 "Customer_Order" 문자열로 GetChildRows()를 호출하여 해당 고객과 관련된 Orders 테이블에서 행 컬렉션을 검색할 수 있습니다. 마찬가지로 Orders 테이블의 행에서 "Order_OrderDetail" 문자열로 GetChildRows를 호출하여 주어진 주문과 관련된 모든 Order Detail 항목을 검색하면 해당 주문과 관련된 모든 Order Detail 항목을 찾을 수 있습니다. DataRowView 클래스의 CreateChildView 메서드를 사용하면 더 효과적입니다. 이 메서드는 특정 관계의 모든 행을 표시하는 DataView를 반환합니다.

이제 데이터를 바인딩할 준비가 되었으므로 알맞은 수준으로 중첩되어 데이터 바인딩된 컨트롤을 추가해 데이터를 렌더링해야 합니다. 사용자 지정 데이터 구조를 사용하는 이전 예제와 거의 마찬가지로 여기서 바인딩할 데이터도 두 수준으로 중첩된 데이터입니다. 즉, 각 하위 수준의 데이터를 렌더링하려면 두 개의 중첩된 컨트롤이 필요합니다. 좀더 구체적으로 설명하자면 DataSetCustomers 테이블에 바인딩하기 위한 하나의 최상위 Repeater, 각 고객과 연관된 모든 Orders에 바인딩하기 위한 하나의 중첩된 Repeater, 각 주문과 연관된 모든 Order Detail 항목에 바인딩하기 위한 또 다른 중첩된 Repeater가 필요합니다. 중첩된 두 RepeaterDataSource는 부모 행에서 적절한 관계 이름으로 CreateChildView를 호출한 결과가 됩니다. Repeater 선언의 단일 식에서 DataView를 만드는 대신 관계 이름과 부모 행을 가져오고 DataView를 반환하는 코드 숨김 클래스에서 함수를 정의하는 것이 편리합니다.

protected DataView GetChildRelation(object dataItem, 
                                  string relation)
{
  DataRowView drv = dataItem as DataRowView;
  if (drv != null)
    return drv.CreateChildView(relation);
  else 
    return null;
}

해당 함수와 데이터 소스가 적절히 배치된 경우 .aspx 파일에서 구분선과 공백을 사용하는 간단한 레이아웃의 Repeater 컨트롤 선언을 작성할 수 있습니다.

<asp:Repeater Runat="server" ID="_customerRepeater" 
              EnableViewState="false">
  <ItemTemplate>
    Customer:
    <%# DataBinder.Eval(Container.DataItem, "CustomerID") %>
      
    <%# DataBinder.Eval(Container.DataItem,"CompanyName") %>
    <br />
    <asp:Repeater runat="server" EnableViewState="false"
         DataSource=
            '<%# GetChildRelation(Container.DataItem, 
                                  "Customer_Order")%>'
    >
      <itemTemplate>
           
       Orderid:<b>
       <%#DataBinder.Eval(Container.DataItem, "OrderID")%> 
       </b><br/>
       <asp:Repeater runat="server" EnableViewState="false"
            DataSource=
                '<%# GetChildRelation(Container.DataItem, 
                                     "Order_OrderDetail")%>'
       >
         <itemTemplate>
               
               
           <b><%# DataBinder.Eval(Container.DataItem, 
                                  "ProductName") %></b>
           $<%# DataBinder.Eval(Container.DataItem, 
                                "UnitPrice") %> <br/>
         </itemTemplate>
       </asp:Repeater>
     </itemTemplate>
   </asp:Repeater>
 </ItemTemplate>
</asp:Repeater>

XML 데이터에 바인딩

오늘날 대부분의 시스템에서 사용되는 대표적인 계층적 데이터 형식이 XML이라는 점에서 계층적 데이터를 설명할 때 XML을 빼 놓을 수 없습니다. ASP.NET에는 서버 컨트롤을 XML 데이터에 바인딩하기 위한 몇 가지 옵션이 있습니다. 그 중 하나는 XML 데이터를 DataSet로 읽어 들인 다음 앞에서 설명한 기술을 사용하는 것이고, 또 하나는 .NET의 XML API를 사용하여 데이터를 직접 로드한 다음 로드한 데이터에서 열거할 수 있는 클래스에 대해 바인딩하는 것입니다. 가장 흥미로운 마지막 옵션은 XML 문서에 XSL 변환을 적용하여 스스로 렌더링하는 특수 Xml 웹 컨트롤을 사용하는 것입니다.

XmlDocument 클래스는 .NET에서 XML DOM의 구현을 제공하며 데이터 바인딩을 지원하는 컨트롤에 대해 바인딩하기 위해 직접 사용할 수 있습니다. XmlDocument에서 DOM을 탐색하는 데 사용되는 기본 클래스는 문서의 요소를 나타내는 XmlNode입니다. 다행히 XmlNode 클래스는 IEnumerable을 구현하여 자식 위의 열거자를 반환하므로 임의의 XmlNode를 데이터 바인딩의 데이터 소스로 사용할 수 있습니다. 또한 문서가 실제로 자식을 가진 단일 노드에 불과하여 XmlDocumentXmlNode에서도 파생되므로 탐색이 매우 용이합니다. 예를 들어 다음 XML 문서가 'publishers.xml'에 저장되었다고 가정합니다.

<publishers>
  <publisher name="New Moon Books" city="Boston"
             state="MA" country="USA">
    <author name="Albert Ringer   ">
      <title name="Is Anger the Enemy?" />
      <title name="Life Without Fear" />
    </author>
    <author name="John White   ">
      <title name="Prolonged Data Deprivation " />
    </author>
    <author name="Charlene Locksley   ">
      <title name="Emotional Security: A New Algorithm" />
    </author>
    <author name="Marjorie Green   ">
      <title name="You Can Combat Computer Stress!" />
    </author>
  </publisher>
  <publisher name="Binnet and Hardley" city="Washington" 
             state="DC" country="USA">
    <author name="Sylvia Panteley   ">
      <title name="Onions, Leeks, and Garlic" />
    </author>
    <author name="Burt Gringlesby   ">
      <title name="Sushi, Anyone?" />
    </author>
    <author name="Innes del Castillo   ">
      <title name="Silicon Valley Gastronomic Treats" />
    </author>
    <author name="Michel DeFrance   ">
      <title name="The Gourmet Microwave" />
    </author>
    <author name="Livia Karsen   ">
      <title name="Computer Phobic AND Non-Phobic" />
    </author>
  </publisher>
  <!-- ... -->
</publishers>

다음과 같이 페이지의 Load 처리기에서 이 파일을 XmlDocument 클래스로 로드하고 최상위 publishers 요소를 Repeater에 바인딩할 수 있습니다.

   private void Page_Load(object sender, EventArgs e)
   {
      XmlDocument doc = new XmlDocument();
      doc.Load(Server.MapPath("~/Publishers.xml"));

      _rep1.DataSource = doc.FirstChild;
      _rep1.DataBind();
    }

이제 XML 문서에서 데이터를 추출하여 클라이언트로 렌더링하기 위해 필요한 중첩 Repeater를 작성하는 방법을 알아보아야 합니다. 앞의 두 예제를 바탕으로 이 데이터를 거의 같은 방식으로 모델링할 수 있습니다. 문서에 세 개 수준의 데이터(게시자, 작성자 및 제목)가 있을 경우 게시자 Repeater 안에 작성자 Repeater가 중첩되고 작성자 Repeater 안에 제목 Repeater가 중첩된 세 개의 Repeater 컨트롤을 정의합니다. 이 배열은 다음과 같습니다.

<asp:Repeater id="_rep1" runat="server"
              EnableViewState="false">
  <itemTemplate>
    Publisher: <%# ((XmlNode)Container.DataItem).
                    Attributes["name"].Value %><br/>
    <asp:Repeater runat="server" EnableViewState="false"
         DataSource='<%# Container.DataItem %>' >
      <itemTemplate>
        ??Author: <%# 
          ((XmlNode)Container.DataItem)
                            .Attributes["name"].Value 
                              %><br/>
        <asp:Repeater runat="server" EnableViewState="false"
                   DataSource='<%# Container.DataItem %>' >
           <itemTemplate>
              ????<i>
           <%# ((XmlNode)Container.DataItem).
                         Attributes["name"].Value %>
           </i><br />
           </itemTemplate>
         </asp:Repeater>
      </itemTemplate>
    </asp:Repeater>
    <hr />
  </itemTemplate>
 </asp:Repeater>

위 샘플은 다음과 같이 렌더링됩니다.

그림 5. 데이터 바인딩 테스트

XML 데이터에 바인딩하는 프로세스는 앞의 두 예제와 전혀 다릅니다. 먼저 선언적 DataSource 식이 매우 간단하다는 점에 주의하십시오(Container.DataItem). 이는 데이터 바인드의 각 수준에 있는 데이터 소스가 단순히 자식 위의 열거자를 구현하는 XmlNode이기 때문입니다. 또한 현재 데이터 항목에서 데이터를 추출하기 위해 Container.DataItemXmlNode로 캐스팅하여 해당 특성을 추출해야 한다는 점에 주의하십시오. 사용하기에 편리한 DataBinder.Eval() 메서드는 XML 소스가 아니라 데이터베이스 소스에서 작동하므로 이 경우에는 대개 소용이 없습니다.

일반적으로 데이터 바인딩 컨트롤을 사용하여 임의의 XML 데이터를 바인딩하는 것은 다소 번거로운 작업입니다. 앞의 예제에서는 데이터베이스 테이블 집합에서 추출한 데이터를 사용하기 때문에 매우 규칙적이고 구조가 잘 짜여져 데이터 구조와 일치하는 중첩된 컨트롤 집합을 정의할 수 있습니다. 데이터가 불규칙하거나 계층적이지 않을 경우에는 이 작업이 어려워집니다. 예를 들어 다음 XML 문서를 가정합니다.

<animals>
  <animal>
    <name>Dog</name>
   <sound>woof</sound>
   <hasHair>true</hasHair>
  </animal>
  <animal>
    <name>Cat</name>
   <sound>meow</sound>
   <hasHair>true</hasHair>
  </animal>
  <animal>
    <name>Pig</name>
   <sound>oink</sound>
   <hasHair>false</hasHair>
  </animal>
</animals>

앞의 예제와 동일한 기술을 사용할 경우 최상위 Repeater 한 개를 정의하여 다른 중첩된 Repeater와 함께 각 animal 요소를 열거해 animal의 각 하위 요소를 표시할 수 있습니다.

<asp:Repeater ID="_animalRep" Runat="server" 
              EnableViewState="false">
   <ItemTemplate>
     <asp:Repeater Runat="server" EnableViewState="false"
                   DataSource='<%# Container.DataItem %>' >
       <ItemTemplate>
         <%# ((XmlNode)Container.DataItem).InnerText %><br 
           />
       </ItemTemplate>
     </asp:Repeater>
     <hr />
   </ItemTemplate>
 </asp:Repeater>

그러나 여기서는 요소 이름을 전혀 사용하지 않고 각 자식 노드의 내용만 렌더링하므로 자연스러운 방법이 아닙니다. 요소가 이름일 경우에는 특정 방법으로 렌더링하고 요소가 소리일 경우에는 다른 방법으로 렌더링하도록 Repeater에 쉽게 지시할 수 있는 방법은 없습니다. 대신 많은 조건식을 작성하여 원하는 방법으로 XML을 렌더링합니다.

이때 ASP.NET의 데이터 바인딩 컨트롤은 임의의 XML 문서에 바인딩되지 않는다는 점을 기억해야 합니다. 대신 기존 XML 변환 언어 XSL을 사용하여 XML을 렌더링하는 것이 훨씬 더 편리합니다. ASP.NET을 사용하면 XML 컨트롤을 통해 이 작업을 편리하게 수행할 수 있으며 페이지 일부분에 대해서도 마찬가지입니다. ASP.NET은 XML 문서와 XSL 변환을 입력으로 가져오며 변환을 문서에 적용하여 렌더링합니다. animal XML 문서의 경우 다음과 같은 animal.xsl을 작성할 수 있습니다.

<xsl:transform 
   version="1.0" 
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="animal">
    <hr />
   <xsl:apply-templates />
  </xsl:template>

  <xsl:template match="name">
    <i><xsl:value-of select="." /></i><br/>
  </xsl:template>

  <xsl:template match="sound">
    <b><xsl:value-of select="." />!!!</b><br/>
  </xsl:template>

  <xsl:template match="hasHair">
    Has hair? <xsl:value-of select="." /><br/>
  </xsl:template>

</xsl:transform>

그런 다음 페이지에서 다음과 같이 XML 컨트롤에 대한 입력을 지정할 수 있습니다.

<asp:Xml Runat="server" 
         DocumentSource="animals.xml" 
         TransformSource="animals.xsl" />

그러면 다음과 같이 렌더링됩니다.

그림 6. animals.xsl의 렌더링

중첩된 컨트롤 액세스

지금까지의 예제에서는 사용자의 데이터를 수집하는 작업 없이 데이터 표시에만 초점을 맞추었습니다. 계층적으로 바인딩되어 여러 수준으로 중첩된 컨트롤에서 데이터를 검색할 경우 동적으로 만들어진 컨트롤 계층을 탐색하고 이러한 컨트롤 상태를 검색해야 하므로 상당히 번거로울 수 있습니다. 데이터 바인딩된 컨트롤에 포함된 컨트롤에 변경 알림 처리기를 추가하는 옵션을 사용하는 것이 더 편리합니다. 알림 처리기가 발생하면 컨트롤과 연결된 데이터를 추출할 수 있습니다.

이 기술을 보여 주기 위해 Repeater를 사용자 지정 데이터에 바인딩하고 각 항목과 하위 항목에 대한 확인란을 렌더링하는 첫 번째 예제를 사용할 수 있습니다. 사용자가 확인란 중 하나를 선택하고 페이지를 제출할 경우 선택되었다는 사실이 페이지 아래쪽에서 Label에 간단하게 인쇄됩니다. 이 경우의 .aspx 파일은 다음과 같습니다.

<asp:Repeater Runat="server" ID="_itemsRepeater" 
              EnableViewState="False">
  <ItemTemplate>
    <asp:CheckBox Runat="server" 
         Text='<%# DataBinder.Eval(Container.DataItem, 
                   "Name") %>'
            OnCheckedChanged="OnCheckedItem" />
    <asp:Repeater Runat="server" ID="_subitemsRepeater" 
                  EnableViewState="False"
         DataSource='<%# DataBinder.Eval(Container.DataItem, 
                                         "SubItems") %>'>
      <ItemTemplate>
        <br/>???
        <asp:CheckBox Runat="server"
             Text='<%# DataBinder.Eval(Container.DataItem, 
                                       "Name") %>'
             OnCheckedChanged="OnCheckedItem" />
        <asp:Repeater Runat="server" EnableViewState="False"
          DataSource='<%# 
            DataBinder.Eval(Container.DataItem, 
                                          "SubItems") %>'>
          <ItemTemplate>
            <br/>??????
            <asp:CheckBox Runat="server"
               Text='<%# DataBinder.Eval(Container.DataItem, 
                                         "Name") %>' 
               OnCheckedChanged="OnCheckedItem"/>
          </ItemTemplate>
        </asp:Repeater>
      </ItemTemplate>
    </asp:Repeater>
    <br />
  </ItemTemplate>
</asp:Repeater>

<asp:Button Runat="server" Text="Submit" />

<asp:Label EnableViewState="False" Runat="server"
           ID="_message" />

이제 클라이언트에서 페이지를 게시할 때마다 선택한 상태에서 선택하지 않은 상태로 변경되거나 그 반대로 변경된 각 항목에 대해 OnCheckedItem 처리기가 한 번씩 호출됩니다. 이벤트 처리기에 대한 발신자 매개 변수를 확인하면 클라이언트에서 변경한 컨트롤을 확인할 수 있습니다. 확인란 상태가 변경되었음을 나타내는 메시지를 페이지에 기록하는 처기리 샘플 구현은 다음과 같습니다.

protected void OnCheckedItem(object sender, EventArgs e)
{
  CheckBox cb = sender as CheckBox;
  if (cb.Checked)
      _message.Text += string.Format("{0} was checked<br 
        />", 
                                     cb.Text);
  else 
     _message.Text += string.Format("{0} was 
       unchecked<br/>", 
                                    cb.Text);
}

DataGrid 및 DataList 계층적 바인딩

지금까지의 예제에서는 간단한 설명을 위해 Repeater 컨트롤에 초점을 맞추었습니다. 그러나 DataListDataGrid 컨트롤을 둘 다 사용하여 계층적 데이터 바인딩을 수행할 수 있습니다. 실제로 데이터의 바인딩 세부 사항은 사용되는 컨트롤에 상관없이 동일합니다. 예를 들어 중첩된 DataList와 함께 Repeater를 사용함으로써 이러한 세부 사항을 혼합 및 일치시킬 수 있습니다. 이 기사의 코드 샘플에는 DataListDataGrid 클래스에 Repeater를 사용하는 것을 보여 주는 샘플이 포함되어 있습니다.

DataList 클래스는 테이블의 각 셀이 결과 집합의 행 렌더링이 되는 테이블을 렌더링합니다. DataList를 사용하여 계층적으로 바인딩하면 최상위 컨트롤의 셀이 중첩된 컨트롤에 의해 렌더링되는 전체 테이블을 포함하는 중첩된 테이블 렌더링이 됩니다. 아래 샘플은 세 개 수준의 계층적 데이터 바인딩을 사용하는 Northwind 데이터로 채워진 DataSetDataList에 렌더링합니다. 이 경우 최상위 셀 하나만 표시됩니다.

그림 7. Northwind 데이터로 채워진 DataSet

DataGrid 클래스는 테이블의 각 행이 결과 집합의 행 렌더링이 되는 테이블을 렌더링합니다. DataGrid를 사용하여 계층적으로 바인딩해도 중첩된 테이블 렌더링이 되지만 DataList와 달리 사용자가 직접 해당 열을 템플릿 열로 만들고 중첩된 DataGrid를 템플릿 열 정의의 일부로 추가하여 중첩된 테이블이 포함되는 셀을 결정합니다. 아래 샘플은 세 개 수준의 계층적 데이터 바인딩을 사용하는 Northwind 데이터로 채워진 DataSetDataGrid에 렌더링합니다. 이 경우 최상위 행 하나만 표시됩니다.

그림 8. Northwind 데이터로 채워진 DataSet

제한 및 효율성

ASP.NET의 데이터 바인딩 메커니즘은 플랫 데이터 소스를 바인딩하기 위해 설계되었으며 이 메커니즘을 사용하여 계층적 바인딩을 수행할 수는 있지만 본래 계층적 데이터를 렌더링하는 최선의 방법이 아닐 수 있다는 점에 주의해야 합니다. 데이터 소스의 형태는 규칙적이어야 합니다. 예를 들어 어떤 위치에서는 수준이 두 개이고 다른 위치에서는 네 개나 다섯 개인 데이터 소스에는 바인딩할 수 없습니다. XSL은 계층적 형태가 불규칙한 데이터를 렌더링하는 데 적합하기 때문에 데이터를 XML로 변환하고 XSL 변환을 ASP XML 컨트롤과 함께 사용하는 것이 최선의 옵션입니다.

이 기사의 모든 샘플에서는 데이터 바인딩된 각 컨트롤의 EnableViewState 플래그가 false로 설정되어 있습니다. ViewState는 같은 페이지에 대한 여러 POST 요청 간의 컨트롤 대신 상태를 저장하는 데 사용됩니다. 기본적으로 ASP.NET의 모든 서버 쪽 컨트롤은 POST 요청 간의 모든 상태를 보유하므로 모든 컨트롤의 상태를 보존할 수 있어 편리합니다. 그러나 페이지의 렌더링 크기가 현저히 증가할 수도 있으므로 상태 보존 기능을 사용하지 않을 경우 이러한 컨트롤에 대해 ViewState를 해제해야 합니다. 기본적으로 데이터 바인딩된 각 컨트롤과 모든 자식 컨트롤의 전체 내용이 ViewState에 저장되므로 이 기술을 사용할 경우 ViewState가 지나치게 증가하기 쉽습니다. 페이지에 대한 첫 번째 GET 요청인지 아니면 동일한 페이지에 대한 후속 POST인지 여부에 상관없이 데이터 바인딩된 컨트롤이 각 요청과 함께 데이터 소스에 다시 바인딩되므로 대부분의 경우 이 ViewState의 내용은 전혀 사용되지 않습니다. 따라서 특별한 이유가 없을 경우 데이터 바인딩된 모든 컨트롤에서 EnableViewState를 flase로 설정하는 것이 바람직하며 특히 이 문서에 설명된 계층적 데이터 바인딩 기술을 사용하는 경우에 더욱 그렇습니다. Repeater 예제에 있는 중첩된 CheckBox 컨트롤의 경우와 마찬가지로 데이터 바인딩된 컨트롤의 ItemTemplate 안에 중첩된 서버 쪽 컨트롤에는 ViewState를 사용하는 것이 좋습니다. 단, 데이터 바인딩된 실제 컨트롤 자체에서는 사용하지 않아야 합니다. 단적인 예로 DataSet에 바인딩되는 샘플의 모든 Repeater 컨트롤에 뷰 상태를 사용하면 ViewState 필드가 250,000자까지 증가합니다. 페이지로 렌더링되어 표시되는 문자 수가 100,000자 단위인 것과 대조적입니다.

결론

이 기사에서는 템플릿이 지정되고 데이터 바인딩된 컨트롤을 중첩하여 데이터 소스를 동적으로 할당하여 계층적 데이터에 바인딩하는 기술을 보여 줍니다. 이 기술은 데이터베이스의 테이블 관계에 존재하는 데이터와 같이 규칙적이고 구조화된 계층적 데이터를 렌더링하는 데 매우 유용합니다. 이 기술을 사용하여 다른 계층적 데이터 소스를 렌더링할 수도 있지만 데이터가 모든 면에서 규칙적이지 않을 경우에는 번거로운 작업입니다. 이 경우에는 대개 XML 데이터 소스와 함께 XSL을 사용하는 것이 더 간단하며 렌더링을 세부적으로 제어할 수 있습니다.

출처 : http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=13&MAEULNO=6&no=285&page=1

private string _sCookieId = string.Empty;
    private string sCookieId
    {
        get{return _sCookieId;}
        set
        {            //사용자 정보 얻기
            if (Page.User.Identity.IsAuthenticated == true)
            {
                _sCookieId = Page.User.Identity.Name + "/" + value;
            }
            else
            {
                string sRemoteIP = (string)HttpContext.Current.Request.UserHostAddress;
                _sCookieId = sRemoteIP + "/" + value;
            }
        }
    }

    // 쿠키를 이용하여 조회수를 1증가 시키는 메서드.
    private void IncreaseReadCountCookie(string TableName, string boardid)
    {
        bool dupliChk = false;
        string c_read_idx = string.Empty;
       
        sCookieId = boardid;

        if (Request.Cookies[TableName] != null)
            c_read_idx = Request.Cookies[TableName]["READ"];   
        else
            c_read_idx = string.Empty;
       
        Response.Cookies[TableName].Value = TableName;
        Response.Cookies[TableName].Expires = DateTime.Now.AddDays(1);

        string [] arrRead = c_read_idx.Split(',');
        for (int i = 0; i < arrRead.Length; i++)
        {
            if (sCookieId == arrRead[i].ToString().Trim()) dupliChk = true;
        }

        if (dupliChk == true)
        {
            Response.Cookies[TableName]["READ"] = c_read_idx;
        }
        else
        {
            //// 조회수 1 증가.
            SqlCommand cmd = new SqlCommand("board_UPDATE_BOARDREADCOUNT", DbConn.GetConn());
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.Parameters.AddWithValue("@boardid", boardid);
            cmd.Connection.Open();
            cmd.ExecuteNonQuery();
            cmd.Connection.Close();

            Response.Cookies[TableName]["READ"] = c_read_idx + "," + sCookieId;
        }
    }

    // 세션을 이용하여 조회수를 1증가 시키는 메서드.
    private void IncreaseReadCountSession(string TableName, string boardid)
    {
        sCookieId = boardid;
       
        //해당 게시물의 ID와 IP의 조합으로 세션이 생성되었는지 따진다.
        if (Session[sCookieId] == null)
        {
            SqlCommand cmd = new SqlCommand("board_UPDATE_BOARDREADCOUNT", DbConn.GetConn());
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.Parameters.AddWithValue("@boardid", boardid);
            cmd.Connection.Open();
            cmd.ExecuteNonQuery();

             //중복방지를 위한 세션 생성 Session[IP/BID]
            Session[sCookieId] = "1";
        }
    }

첨부파일을 참조에 추가해서 사용하면 되며 사용 샘플 코드는 아래와 같다.

 

투명화 범위를 웹 컬러 포멧(#efefef 형태)  으로 지정할 수 있으며 처리 결과물의 경로를 지정할 수 있다. 

 

결과물은 png 형태로 저장한다.

 

도장이나 개인 사인등의 배경을 투명화 처리하는데 사용할 수 있다.

 

그레이 스케일 이미지의 경우 범위 지정을 좀 더 섬세히 하면 깔끔한 결과물을 얻을 수 있다.

 

결과 예제

 

원본 이미지 : 

     

 

결과 이미지 :

 

using System;

using System.Data;

using System.Configuration;

using System.Collections;

using System.Web;

using System.Web.Security;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.Web.UI.WebControls.WebParts;

using System.Web.UI.HtmlControls;

using SSICOM_GDI_PLUS;

 

namespace gdiplus_exam

{

    public partial class _Default : System.Web.UI.Page

    {

        protected void Page_Load(object sender, EventArgs e)

        {

            string sOrgPath = @"c:\\ssssss.bmp"//원본 파일경로

            string sTargetPath = @"e:\"; //결과물 저장폴더

 

            SSICOM_GDI_PLUS.IMAGE_CONVERT sic = new IMAGE_CONVERT();

 

            /// <param name="sFileName">원본 파일명</param>

            /// <param name="sColorStr">투명화 비교 시작 값 (web color format)</param>

            /// <param name="eColorStr">투명화 비교 종료 값 (web color format)</param>

            /// <param name="sTargetPath">결과물 저장폴더(png 확장자를 가진 원본파일이름)</param>

// #aaaaaa 부터 #ffffff 까지 영역의 색상을 투명화 처리 한다.

            string sConvertedimagePath = sic.fctConvertToTransparent(sOrgPath, "#aaaaaa", "#ffffff", sTargetPath);

 

            Response.Write("<img src='" + sOrgPath + "'>");

            Response.Write("<img src='" + sConvertedimagePath + "'>");

        }

    }

}

 

 

 

처리 메소드 소스

 

using System.Drawing;

using System.Drawing.Imaging;

using System.IO;

 

상기 3개의 네임 스페이스를 추가하여야 한다.

 

        /// <summary>

        /// 특정 컬러를 투명화 처리한다.

        /// </summary>

        /// <param name="sFileName">원본 파일명</param>

        /// <param name="sColorStr">투명화 색상 범위 시작 값 (web color format)</param>

        /// <param name="eColorStr">투명화 색상 범위 종료 값 (web color format)</param>

        /// <param name="sTargetPath">결과물 저장폴더(png 확장자를 가진 원본파일이름)</param>

        /// <returns></returns>

        public string fctConvertToTransparent(string sFileName, string sColorStr, string eColorStr, string sTargetPath)

        {

            // html 포멧 컬러일 경우 #이 선행되어야 한다. 

            if (!sColorStr.StartsWith("#"))

                sColorStr = "#" + sColorStr;

 

            if (!eColorStr.StartsWith("#"))

                eColorStr = "#" + eColorStr;

 

            // 색상 정보를 컬러 오브젝트로 선언

            Color sColor =  ColorTranslator.FromHtml(sColorStr);

            Color eColor = ColorTranslator.FromHtml(eColorStr);

 

            FileInfo fi = new FileInfo(sFileName);

 

            System.Drawing.Color tmpColor;

 

            // 결과물 경로용

            string sResultFile = string.Empty;

 

            // 원본 파일을 읽어 비트맵을 생성

            using (System.Drawing.Bitmap BaseBitmap = (System.Drawing.Bitmap)System.Drawing.Bitmap.FromFile(sFileName))

            {

                // 결과물을 생성하기 위한 비트맵

                using (System.Drawing.Bitmap NewBitMap = new System.Drawing.Bitmap(BaseBitmap.Width, BaseBitmap.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb))

                {

                    // BaseBitmap 을 픽셀단위로 읽으면서 해당 픽셀의 색 정보가 지정한 색상 범위에 속하는 경우라면

                    // 투명 색상으로 지정한다. 그게 아니라면 원래 색상을 그대로 유지한다.

                    // 이런 형태로 NewBitMap 의 동일한 좌표에 점을 찍는 형태로 구현되었다.

 

                    for (int x = 0; x < BaseBitmap.Width; x++)

                    {

                        for (int y = 0; y < BaseBitmap.Height; y++)

                        {

                            // 특정 픽셀의 색상 정보를 갖고온다.

                            tmpColor = BaseBitmap.GetPixel(x, y);

 

                            if (!

                                (

                                ((tmpColor.R >= sColor.R) && (tmpColor.R <= eColor.R)) &&

                                ((tmpColor.G >= sColor.G) && (tmpColor.G <= eColor.G)) &&

                                ((tmpColor.B >= sColor.B) && (tmpColor.B <= eColor.B))

                                )

                               )

                            {

                                NewBitMap.SetPixel(x, y, tmpColor);

                            }

                        }

                    }

 

                    sResultFile = sTargetPath + fi.Name.Substring(0, fi.Name.Length - fi.Extension.Length) + DateTime.Now.Ticks.ToString() + ".png";

                    NewBitMap.Save(sResultFile, System.Drawing.Imaging.ImageFormat.Png);

                }

            }

 

            fi = null;

 

            return sResultFile;

        }


[ASP.NET]
JavaScript 에서 C# 함수호출하기 - PageMethod

JavaScript 에서 C# 함수를 호출하는 방법은
 - ASP.NET AJAX - Web Service 호출
 - ASP.NET Ajax Extentions - PageMethod 호출
 - jQuery를 이용한 WebService 호출
 - Get, Post를 이용한 방법(Non Ajax)
4가지가 있다.

호출 방법에 대해 자세히 참조가 필요 하다면,
참조 : http://blog.naver.com/myfancy/140091157970
를 참조하자.


여기선 ASP.NET Ajax Extentions - PageMethod 호출에
대해서만 간단하게 알아보자.


Ajax 를 통해 PageMethod를 호출하기 위해선

[.aspx 파일]

.aspx 파일에 ScriptManager 를 추가시켜야 한다.

<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="True">       
    </asp:ScriptManager>

바디 부분에 삽입하자.

* 주의 : EnablePageMethods 속성을 True로 설정하는것을 잊지 말도록 하자!

[.cs 파일]

.cs 파일에서는 평소에 쓰듯이
함수를 만들면 된다.

간단 하게 추가해주어야 할것은
using System.Web.Services;
를 추가하고,

[WebMethod]
    public static string HelloWorld()
    {
        return "Hello World";
    }

함수위에 [WebMethod] 를 추가해준다.

이제 자바 스크립트를 통해 호출해 보도록하자!
자바 함수를 쓰듯이 호출하되
함수 앞에 PageMethods.를 추가하자

PageMethods.HelloWorld();

이해가 가지않는다면
다른 글을 찾아 참조 해보도록하자.
검색검색검색검색! 만이 살길이다!! ^-^

출처 카페 > 데브나라 | 블루나라
원문 http://cafe.naver.com/devnara/3

일반적인 페이지 수명 주기 단계

 

1. 페이지 요청

    페이지 요청은 페이지 수명 주기가 시작되기 전에 발생합니다. 사용자가 페이지를 요청하면 ASP.NET에서는 페이지를 구문 분석하고 컴파일하여 페이지 주기를 시작할지 여부 또는 페이지를 실행하지 않고 캐시된 버전의 페이지를 응답으로 보낼 수 있는지 여부를 결정합니다.

 

2. 시작

    시작 단계에서는 RequestResponse 같은 페이지 속성이 설정됩니다. 이 단계에서 페이지는 요청이 다시 게시인지 아니면 새 요청인지를 확인하여 IsPostBack 속성을 설정합니다. 또한 시작 단계에서는 페이지의 UICulture 속성도 설정됩니다.

 

3. 페이지 초기화

    페이지 초기화 단계에서는 페이지의 컨트롤을 사용할 수 있으며 각 컨트롤의 UniqueID 속성이 설정됩니다. 또한 테마가 페이지에 적용됩니다. 현재 요청이 다시 게시인 경우에는 다시 게시 데이터가 아직 로드되지 않았고 컨트롤 속성 값이 뷰 상태의 값으로 복원되지 않았습니다.

 

4. 로드

    로드 단계에서는 현재 요청이 다시 게시인 경우 뷰 상태 및 컨트롤 상태에서 복구된 정보와 함께 컨트롤 속성이 로드됩니다.

 

5. 유효성 검사

    유효성 검사 단계에서는 모든 유효성 검사기 컨트롤의 Validate 메서드가 호출되어 각 유효성 검사기 컨트롤 및 페이지의 IsValid 속성을 설정합니다.

 

6. 다시 게시 이벤트 처리

    요청이 다시 게시인 경우 이벤트 처리기가 호출됩니다.

 

7. 렌더링

    렌더링 단계에서는 뷰 상태가 페이지에 저장된 다음, 페이지에서 각 컨트롤을 호출하여 페이지에 대한 Response 속성의 OutputStream에 렌더링된 출력을 적용합니다.

 

8. 언로드

    페이지가 완전히 렌더링되어 클라이언트에 전달되고 삭제할 준비가 되면 언로드가 호출됩니다. 이 단계에서는 ResponseRequest 같은 페이지 속성이 언로드되고 정리 작업이 수행됩니다.

 

 

수명 주기 이벤트

 

1. Page_PreInit

   IsPostBack 속성을 사용하여 페이지가 처음으로 처리되는 것인지 여부를 확인합니다.

   동적 컨트롤을 만들거나 다시 만듭니다.

   마스터 페이지를 동적으로 설정합니다.

   Theme 속성을 동적으로 설정합니다.

   프로필 속성 값을 읽거나 설정합니다.

 

2. Page_Init

   컨트롤 속성을 읽거나 초기화합니다.

 

3. Page_Load

    컨트롤 속성을 읽고 업데이트합니다.

 

4. Control events

    응용 프로그램별 처리 과정을 수행합니다.

    페이지에 유효성 검사기 컨트롤이 포함되어 있으면 처리를 수행하기 전에 페이지 및 각 유효성 검사기 컨트롤의 IsValid 속성을 확인합니다.

   특정 이벤트(예: Button 컨트롤의 Click 이벤트)를 처리합니다.

 

5. Page_PreRender

    페이지 내용을 최종 변경합니다.

 

6. Page_Unload

    다음을 포함하여 최종 정리 작업을 수행합니다.

    열려 있는 파일 및 데이터베이스 연결을 닫습니다.

    로깅 또는 기타 요청 관련 작업을 마칩니다.

 
 

일반적인 응용 프로그램 수명 주기

 

응용 프로그램의 수명 동안 ASP.NET은 Global.asax 파생 HttpApplication 인스턴스의 풀을 유지 관리합니다. 응용 프로그램이 HTTP 요청을 받을 때 ASP.NET 페이지 프레임워크는 이러한 인스턴스 중 하나를 할당하여 해당 요청을 처리합니다. 이러한 특정 HttpApplication 인스턴스는 할당된 요청의 전체 수명을 관리해야 하며 요청이 완료된 후에만 이 인스턴스를 다시 사용할 수 있습니다.

 

 

 

 

1. 사용자가 웹 서버에서 응용 프로그램 리소스를 요청합니다.

   브라우저에서 웹 서버(ASP.NET 응용 프로그램의 경우에는 대개 IIS)에 요청을 보내면 ASP.NET 응용 프로그램의 수명 주기가 시작됩니다. ASP.NET은 웹 서버에서 ISAPI 확장입니다. 웹 서버에서는 요청을 받으면 요청된 파일의 확장명을 검사하고 요청을 처리할 ISAPI 확장을 확인한 다음 적절한 ISAPI 확장에 요청을 전달합니다. ASP.NET에서는 .aspx, .ascx, .ashx, .asmx 등의 매핑된 파일 이름 확장명을 처리합니다.

 

2. ASP.NET에서 응용 프로그램에 대한 첫 번째 요청을 받습니다.

   ASP.NET에서 응용 프로그램 리소스에 대한 첫 번째 요청을 받으면 ApplicationManager라는 클래스에서 응용 프로그램 도메인을 만듭니다. 응용 프로그램 도메인을 사용하면 전역 변수에 대해 각 응용 프로그램을 격리시켜 개별적으로 언로드할 수 있습니다. 응용 프로그램 도메인 내에서는 HostingEnvironment라는 클래스 인스턴스가 만들어집니다. 이를 통해 응용 프로그램이 저장되어 있는 폴더의 이름 등과 같은 응용 프로그램 정보에 액세스할 수 있습니다.

 

3. 각 요청에 대해 ASP.NET 핵심 개체가 만들어집니다.

   응용 프로그램 도메인이 만들어지고 HostingEnvironment 개체가 인스턴스화된 후 ASP.NET에서는 HttpContext, HttpRequestHttpResponse 같은 핵심 개체를 만들고 초기화합니다. HttpContext 클래스에는 현재 응용 프로그램 요청과 관련된 특정 개체(예: HttpRequestHttpResponse 개체)가 포함되어 있습니다. HttpRequest 개체에는 쿠키와 브라우저 정보처럼 현재 요청에 대한 정보가 포함되어 있고 HttpResponse 개체에는 렌더링된 모든 출력과 쿠키처럼 클라이언트에 전달되는 응답이 포함되어 있습니다.

 

4. HttpApplication 개체가 요청에 할당됩니다.

   모든 핵심 응용 프로그램 개체가 초기화된 후 HttpApplication 클래스의 인스턴스를 만들어 응용 프로그램이 시작됩니다. 응용 프로그램에 Global.asax 파일이 있는 경우 ASP.NET에서는 HttpApplication 클래스에서 파생되는 Global.asax 클래스의 인스턴스를 만들고 이 파생 클래스를 사용하여 응용 프로그램을 나타냅니다.

 

5. HttpApplication 파이프라인에 의해 요청이 처리됩니다.

 

 

 

 

 

HttpApplication 이벤트 처리순서

 

1. 브라우저에서 보낸 정보를 검사하고 악의적인 태그가 포함되어 있는지 확인하여 요청의 유효성을 검사합니다. 자세한 내용은 ValidateRequest스크립트 악용 개요를 참조하십시오.

2. Web.config 파일의 UrlMappingsSection 섹션에 URL이 구성되어 있으면 URL 매핑을 수행합니다.

3. BeginRequest 이벤트를 발생시킵니다.

4. AuthenticateRequest 이벤트를 발생시킵니다.

5. PostAuthenticateRequest 이벤트를 발생시킵니다.

6. AuthorizeRequest 이벤트를 발생시킵니다.

7. PostAuthorizeRequest 이벤트를 발생시킵니다.

8. ResolveRequestCache 이벤트를 발생시킵니다.

9. PostResolveRequestCache 이벤트를 발생시킵니다.

10. 요청된 리소스의 파일 이름 확장명에 따라(응용 프로그램의 구성 파일에서 매핑됨) 요청을 처리할 IHttpHandler를 구현하는 클래스를 선택합니다. Page 클래스에서 파생된 개체(페이지)에 대한 요청이고 이 페이지를 컴파일해야 할 경우에는 ASP.NET에서 인스턴스를 만들기 전에 페이지를 컴파일합니다.

11. PostMapRequestHandler 이벤트를 발생시킵니다.

12. AcquireRequestState 이벤트를 발생시킵니다.

13.

PostAcquireRequestState 이벤트를 발생시킵니다.

14. PreRequestHandlerExecute 이벤트를 발생시킵니다.

15. 요청에 대해 적절한 IHttpHandler 클래스의 ProcessRequest 메서드 또는 비동기 버전의 BeginProcessRequest를 호출합니다. 예를 들어 페이지에 대한 요청인 경우 현재 페이지 인스턴스에서 요청을 처리합니다.

16. PostRequestHandlerExecute 이벤트를 발생시킵니다.

17. ReleaseRequestState 이벤트를 발생시킵니다.

18. PostReleaseRequestState 이벤트를 발생시킵니다.

19. Filter 속성이 정의되어 있는 경우 응답 필터링을 수행합니다.

20. UpdateRequestCache 이벤트를 발생시킵니다.

21. PostUpdateRequestCache 이벤트를 발생시킵니다.

22. EndRequest 이벤트를 발생시킵니다.

 

 

 

응용 프로그램 수명 주기 동안 사용되는 일부 이벤트와 메서드

 

1.Application_Start

   ASP.NET 응용 프로그램의 리소스(예: 페이지)가 처음으로 요청되면 호출됩니다. Application_Start 메서드는 응용 프로그램 수명 주기 동안 한 번만 호출됩니다. 이 메서드를 사용하면 데이터를 캐시에 로드하고 정적 값을 초기화하는 등 시작 작업을 수행할 수 있습니다.

  응용 프로그램을 시작하는 동안에는 정적 데이터만 설정해야 합니다. 인스턴스 데이터는 HttpApplication 클래스의 첫 번째 인스턴스에서만 사용할 수 있으므로 설정하지 마십시오.

 

2. Application_ event

   이 항목의 앞부분에 나오는 응용 프로그램 수명 주기 표에 있는 것처럼 응용 프로그램 수명 주기에서 적절한 시점에 발생합니다.

   Application_Error는 응용 프로그램 수명 주기의 모든 단계에서 발생할 수 있습니다.

   요청은 단락(short-circuit)될 수 있으므로 모든 요청에서 항상 발생하는 유일한 이벤트는 Application_EndRequest입니다. 예를 들어 두 개의 모듈에서 Application_BeginRequest 이벤트를 처리하는 경우 첫 번째 모듈에서 예외를 throw하면 두 번째 모듈에 대해 Application_BeginRequest 이벤트가 호출되지 않습니다. 그러나 응용 프로그램에서 리소스를 정리할 수 있도록 Application_EndRequest 메서드가 항상 호출됩니다.

 

3. HttpApplication.Init

   모든 모듈이 만들어진 후에 HttpApplication 클래스의 모든 인스턴스에 대해 한 번 호출됩니다.

 

4. Dispose

   응용 프로그램 인스턴스가 소멸되기 전에 호출됩니다. 이 메서드를 사용하면 관리되지 않는 리소스를 수동으로 해제할 수 있습니다. 자세한 내용은 관리되지 않는 리소스 정리를 참조하십시오.

 

5. Application_End

   응용 프로그램 실행 주기에서 응용 프로그램이 언로드되기 전에 한 번 호출됩니다.

 

 

ASP.NET 최상위 항목이 컴파일되는 순서

 

1. App_GlobalResources

    응용 프로그램의 전역 리소스가 컴파일되고 리소스 어셈블리가 빌드됩니다. 응용 프로그램의 Bin 폴더에 있는 모든 어셈블리가 리소스 어셈블리에 연결됩니다.

2. App_WebResources

    웹 서비스의 프록시 형식이 생성되고 컴파일됩니다. 그 결과로 생성되는 웹 참조 어셈블리는 리소스 어셈블리에 연결됩니다.

3. Web.config 파일에 정의된 프로필 속성

    응용 프로그램의 Web.config 파일에 프로필 속성이 정의되어 있는 경우 프로필 개체를 포함하는 어셈블리가 생성됩니다.

4. App_Code

    소스 코드 파일이 빌드되고 하나 이상의 어셈블리가 생성됩니다. 코드 어셈블리 및 프로필 어셈블리는 모두 리소스 및 웹 참조 어셈블리에 연결됩니다.

5. Global.asax

    응용 프로그램 개체가 컴파일되고 앞서 생성된 모든 어셈블리에 연결됩니다.

 

 

ASP.NET 폴더 및 항목이 컴파일되는 순서

 

1. App_LocalResources

요청된 항목을 포함하는 폴더에 App_LocalResources 폴더가 들어 있는 경우 로컬 리소스 폴더의 내용이 컴파일되고 전역 리소스 어셈블리에 연결됩니다.

2. 개별 웹 페이지(.aspx 파일), 사용자 정의 컨트롤(.ascx 파일), HTTP 처리기(.ashx 파일) 및 HTTP 모듈(.asmx 파일)

    필요에 따라 컴파일되고 로컬 리소스 어셈블리 및 최상위 어셈블리에 연결됩니다.

    페이지에서 참조하는 개별 테마의 스킨 파일, 마스터 페이지 및 기타 소스 코드 파일은 참조하는 페이지가 컴파일된 다음 컴파일됩니다.

3. 테마, 마스터 페이지, 기타 리소스 파일

 

 

응용 프로그램 다시 시작

 

1. 응용 프로그램의 Bin 폴더에서 어셈블리를 추가, 수정 또는 삭제

2. App_GlobalResources 또는 App_LocalResources 폴더에서 지역화 리소스를 추가, 수정 또는 삭제

3. 응용 프로그램의 Global.asax 파일을 추가, 수정 또는 삭제

4. App_Code 디렉터리에서 소스 코드 파일을 추가, 수정 또는 삭제

5. 프로필 구성을 추가, 수정 또는 삭제

6. App_WebReferences 디렉터리에서 웹 서비스 참조를 추가, 수정 또는 삭제

7. 응용 프로그램의 Web.config 파일을 추가, 수정 또는 삭제

+ Recent posts