클라이언트 스크립으로 서버컨트롤 호출하기 - 1

제가 처음 asp.net을 할 때 asp.net이 asp에서보다 클라이언트측 스크립트를 사용하기가 왜 이이리 어렵냐고 생각한적이 있었습니다. Asp에서는 서버 컨트롤이란 것이 없었기 때문에 서버컨트롤과 클라이언트 측 스크립트를 어떻게 연결시킬까에 대한 고민을 할필요가 없었기때문이죠. (사실 어떻게해야할지 감도 안오더군여)


조금 지나서 서버컨트롤의 ID가 클라이언트측에서 접근할 수 있는 수단이 된다는 것을 알게되었습니다.(강좌를 보시는 분들도 이건 알고계시겠죠?) 그럴지만 역시 클라이언트 스크립트를 사용하기는 싶지가 않더군요. (asp.net이 폼은 나는데 그래도 asp가…-_-;)
꽁수 비슷한 방법으로 간신히 위기를(?) 넘기며 코딩하다가(지금 생각하면 완전 삽질.) 나중에 알게됬는데 역시! asp.net답게 폼 팍팍나고 asp에 비해서 훨신 안전한 방법을 MS에서는 숨겨놨다는 것을 알게되었져. (정말 대발견! 이었습니다.)


이곳 데브피아에 들락거리면서 알게된 것은 의외로 아직까지 저처럼 삽질하고계신 분이 적지않다는 알고 그런 분들을 위해 이 강좌를 쓰기로 했습니다.(여기까지 쓰는데도 벌써 힘들군요)



이제부터 본격적인 강좌에 들어가겠습니다.
한꺼번에 다 설명하면 우리의 두뇌가 활동을 멈추는 사고가 생기니까 몇게씩 나눠서 보도록 하죠.
PostBack을 만드는 메소드부터 보겠습니다. 3종류인데…

public string GetPostBackEventReference(Control);
public string GetPostBackEventReference(Control,string);


오버라이드(Overload)된 두 녀석이 있는데 이 넘들이 바로 서버로 포스트백을 만들어주는 아주 맘에드는 넘들입니다. Asp.net로 만들어진 페이지를 소스보기해서 열어보면 __doPostBack(어쩌구) 이런 함수가 보이는데 바로 서버로 PostBack 하도록 만드는 함수입니다. GetPostBackEventReference 메소드가 하는일이 __doPostBack(어쩌구) 란 클라이언트측 함수를 만드는 역할을 합니다.

public string GetPostBackClientHyperlink( Control , string);


이 넘도 GetPostBackEventReference와 같은 일을 합니다. 다른점은 __doPostBack(어쩌구) 앞에 JavaScript:를 붙여준다는 것이죠. 이렇게요…  JavaScript:__doPostBack(어쩌구)   링크버튼 만들 때 사용하면 될 것 같군요.


public string GetPostBackClientEvent(Control, string );

이 것도 역시 같은 종류인데 저는 GetPostBackEventReference 메소드와의 차이를 아직 발견하지 못했습니다. 그래서 저는 이넘은 빼고 위의 두 메소드만 이용해 강좌를 할겁니다. 두 메소드의 차이를 알고싶은 분은 MSDN을 보시죠. 차이점을 발견하시면 저도 좀 알려주시구요.


 

백문이 불여 일견이란 말이 있으니 긴 지금 설명드린 녀석들이 실지로 어떻게 행동하는지 바로 코트를 보도록 하죠. 저는 편한 방법을 좋아하기 때문에 지금부터 보여드리는 예제는 vs.net을 사용하겠습니다. 참고로 전 vs.net 한글 베타를 사용하구요. 자 그럼 새로운 프로젝트를 하나 생성하고(저는 ClientScirptVsServerComponent로 이름 졌습니다.) 같이 함 해보시죠. 완성된 프로잭트 파일은 첨부해서 올렸습니다.


먼저 작성된 HTML부분을 쉽게 이해하기 위해 DOCUMENT의 PageLayout를 FlowLayout으로 변경하겠습니다. 그다음 웹폼위에다 Label컴포넌트와 Button컴포넌트를 하나씩 가져다 놓구요. Label은 Message라고 id를 바꿔줍니다. 버튼의 Text에는 “정식 Submit 버튼”이라는 문자열을 대입했습니다.

<asp:Button id=Button1 runat="server" text="정식 Submit 버튼 "></asp:Button>
<asp:label id=Message runat="server">Label</asp:label>


그리구 코드비하인드의(cs파일) Page_Load이벤트에 요렇게 코딩하시고요.

Message.Text = "<br>";
Message.Text += "Post Back만드는 스트립트 <br>";
Message.Text += "<br>";
Message.Text += "GetPostBackEventReference :" + Page.GetPostBackEventReference(Button1,"") + "<br>";
Message.Text += "GetPostBackClientEvent :" + Page.GetPostBackClientEvent(Button1,"") + "<br>";
Message.Text += "GetPostBackClientHyperlink :" + Page.GetPostBackClientHyperlink(Button1,"") + "<br>";

눈여겨 보실 부분은 각 메소드의 인자인데
Page.GetPostBackEventReference(Button1 /** (1) **/, "")

(1)에 PostBack 이벤트를 받을 컴포넌트의 인스턴스를 넣어줍니다.
그럼 실행해서 결과먼저 보는게 좋겠죠.


[__doPostBack() 스크립트로 Submit 하는 버튼]
Post Back만드는 스트립트
GetPostBackEventReference :__doPostBack('Button1','')
GetPostBackClientEvent :__doPostBack('Button1','')
GetPostBackClientHyperlink :javascript:__doPostBack('Button1','')

이런식으로 결과가 나오면 됩니다. 말씀드린 세종류의 메소드가 뭘하는지 한눈에 아시겠죠? 이놈들이 하는 일은 고작 우리가 클라이언트에서 사용할 수 있는 문자열을 리턴하는 것 뿐입니다. 우리는 이 문자열들을 사용해서 코딩하면 되구요.
이제 어떻게 사용하는지 방법을 알아 봅니다.

웹폼의 제일 위에다 Label버튼을 하나 더 추가하고 좀전에 만든 버튼의 이벤트를 만들기 위해 버튼을 더블클릭 합니다. 커서가 Button1_Click메소드 안에 있죠. 여기다 요렇게 ..


Label1.Text = Button1.UniqueID + “(“ + Button1.Text + ”)” + " 눌림";



여기서 중요한 것은 UniqueID인데 요넘은 클라이언트에서 컨트롤 인스탄스를 실별할 수 있는 유일한 키를 가지고있는 중요한 프로퍼티 입니다. ClientID라는 프로퍼티도 있는데 평상시에는 UniqueID와 동일한 값을 가지고 있기 때문에 이녀석을 쓰셔도 상관 없습니다. 그러나 특정 상황에서는 UniqueID만이 유일한 식별키가 될때가 있기 때문에 저는 무조건   UiqueID를 사용합니다. 말씀드린 특정상황은 아래에서 따로 설명하죠.

그 다음 Page_Load안에는 요렇게..


Label1.Text = "";


버튼 이벤트가 발생하기 전에 Label1.Text를 지워주기 위함입니다. 그래야 제대로 동작하나 확인이 되겠죠.
이제 클라이언트에서 Button1을 클릭하면 Button1_Click이벤트가 발생해Label1.Text에 눌린 버튼의 Text와 UniqueID가 디스플레이 됩니다. 지금까지 여러분이 알고있는 방법로는 크라이언트 측에서 Button1_Click 이벤트를 발생시킬수 있는 방법이 Button1을 클릭 하는 방법 밖에 없습니다.(눈치 빠르신 분은 벌써 방법을 알아 차리셨을지도 모르지요)


다른 버튼(링크버튼도 포함)에서 Button1_Click 이벤트를 발생시키는 방법이 있는데, 이것을 가능하게 하는 녀석들이  바로 위에 설명드린 세종류의 메소드들 입니다.계속 코딩하면서 설명드리겠습니다.

웹폼에서 Button1아래에다 버튼 한 개와 링크버튼(HyperLink컨트롤도 되고요) 하나를 추가합니다. 이때 주의하실 것은 버튼이 WebControls.Button 이 아니고 HtmlControls.HtmlInputButton이어야 합니다.(이유는 아래에 설명합니다.) 속성은 아래와같이 하시고요.

HtmlInputButton : ID ? MyButton01, runat="server"(추가) Value - 사용자 스크립으로 정식 Submit 버튼 모방

LinkButton : ID ? MyLinkButton01, Text - __doPostBack() 스크립트로 Submit 하는 링크버튼

여기서 새로운 이벤트를 하나 추가하도록 하겠습니다. Page클래스의 PreRender 이벤트 인데요. 앞으로 추가되는 코드들은 이 벤트의 내용으로 추가됩니다. Page_Load에 넣지 않고 PreRender안으로 넣는 이유는 asp.net의 이벤트 발생 순서 때문입니다. 여기서는 이벤트의 발생순서에 대해선 설명드리지 않겠습니다. 이벤트에 대해선 따로 강좌를 하는게 나을것 같군요.(그렇게 될지는 모르지만-*-)

일단은 InitializeComponent()안에 다음라인을 추가하시고요.

this.PreRender += new System.EventHandler(this.Page_PreRender);

이벤트를 구현 하는 바디를 추가합니다.(vs.net에서는 이것을 쉽게 할수있죠.)
그리고 PostBack을 하도록하는 코드도 추가하구요.

private void Page_PreRender(object sender, System.EventArgs e)
{
    MyLinkButton01.Attributes["href"] = Page.GetPostBackClientHyperlink(Button1,"");
    MyButton01.Attributes["onclick"] = Page.GetPostBackEventReference(Button1,"");
}


추가된 코드를 보시면 컨트롤 이스탄스의 Attributes로 처음 설명드린 메소드들의 반환값을 대입하는 것을 볼수있습니다. 자세히 보니 MyLinkButton01과 MyButton01에 사용하는 메소드가 틀리군요.


MyButton01부터 설명드리겠습니다. MyButton01.Attributes["onclick"] = Page.GetPostBackEventReference(Button1,"");의 결과로 클라이언트로 출력되는 결과는 onclick="__doPostBack('Button1','')" 라는 문자열이 추가되는 것입니다. 감이 오시나요?

그럼  MyLinkButton01.Attributes["href"] = Page.GetPostBackClientHyperlink(Button1,"");의 결과를 보죠. href="javascript:__doPostBack('MyLinkButton01','')”

음. 비슷하군요. __doPostBack('MyLinkButton01','')”앞쪽에 javascript:가 붙은 것이 틀리군요. 그러니까 MS에서 비슷한 종류의 메소드를 여러게 제공하는 이유는 게발자의 편의를 위함이지 사실상은 GetPostBackEventReference메소드 하나만 있어도 사용하는데는 지장이 없겠네요. 그렇지만 사용하라고 만들어놨으니 사용하는게 낫겠죠.


위의 코드에서 주목해 보실 곳이있는데 GetPostBackEventReference와 GetPostBackClientHyperlink로 전달되는 첫째 인자입니다.

Page.GetPostBackClientHyperlink(Button1 <- 여기주목,"");
Page.GetPostBackEventReference(Button1 <- 여기도 주목,"");

이 인자가 바로 __doPostBack()함수의 목표 인스탄스가 된다는 것입니다.


위에서 WebControls.Button이 아닌 HtmlControls.HtmlInputButton을 사용한 이유는 이 방식이 onclick 이벤트를 이용하는 방식이기 떼문입니다. WebControls.Button는 클라이어트측 출력으로 type=”submit”의 버튼을 만들거든요. 그레서 type=”button”을 출력하는 HtmlControls.HtmlInputButton 을 사용했습니다.

자 이제 실행시켜서 결과를 확인해 보시죠. MyLinkButton01이나 MyButton01이 모두 Button1의 Button1_Click이벤트를 발생시키는 것을 확인하실 수 있을겁니다.



클라이언트 스크립으로 서버컨트롤 호출하기 - 2

지난번 강좌에서는 GetPostBackEventReference 등의 메소드를 이용해 __doPostBack()을 발생시키는 방법을 알아봤습니다. 이번에는 서버에서 크라이언트 스크립트를 생성하는 방법에 대해 알아보겠습니다. 진도를 좀 빨리 나가려고 합니다. 너무 자세히 설명하는게 여러분의 능력을 너무 과소 평가하는 것 같은 느낌을 들어서요.(사실 저도 바쁘군요^^)

이번 강좌는 서버에서 클라이언트측 스크립트를 생성하는 방법이라 말씀드렸는데, 다시 말하면 기존의 asp나 php에서와는 다른 방법으로 좀더 asp.net다운 방법을 소개하려고 합니다.


원론적인 예기지만 우리가 손에익고 쉬운(?) asp를 두고 디따리 복잡한 객채 프로그래밍 방식인 asp.net을 선택한 이유는 객채 프로그래밍 방법이주는 이점이 그 만큼 매력적 이었기 때문이겠죠. 그러면 웹 프로그래밍에(asp.net) 있어서도 공통적으로 자주쓰이
는 모듈에 대해서는 코드의 수정없이 프로그램의 조립방식을 가야하는 것이 당연합니다. PC를 조립할 때 부속 몇게만 꽂으면 훌륭한 컴이 완성되듯이 말입니다. 그런데 웹 방식에서는 모든 출력의 종점이 브라우저이기 때문에 객채 방식으로 폼나게 만들어 실행한 결과가 브라우저 안에서는 짬뽕이 될수있습니다.


예를 들자면, 여러게의 미리 만들어두었던 User Control을(커스텀 서버 컨트롤도 되겠죠) 하나의 웹폼에 배치할 경우인데요.  A.ascx, B.ascx, C.ascx 의 유저컨트롤이있는데 이것을 웹폼 ABC.aspx파일에 끌어다 놓기 만으로 간단하게 페이지가 완성되었습니다. 컴파일을 시키니 기분좋게 에러도없이 컴파일 되는군요. 그런데 실행를 시키니 ‘뿅’하고 에러박스가 뜨면서 스크립스가 어쩌구 하면서 고장난 웹페이지가 출력되고 맙니다. 소스보기를 해서 열어보니 중복된 스크립트 함수가 에러의 원흉이군요. A.ascx와 B.ascx에서 동일
한 스크립트 함수를 출력했기 때문입니다.


웹에서 조립 방식의 프로그래밍을 하다보면 이런 비극은 종종 발생할 수 있습니다. 이런 비극을 해결하기 위해 MS에서는 몇가지 해결사 메소드를 준비하였구요. (그러나 사실상 아래 소개할 메소드들이 이 문제를 완전히 해결한건 아닙니다. 쓰는 방법에 따라…) 이 메소드들의 필요성을 설명하려고 한건데 예기가 길어졌습니다. 그럼 어떤 넘들인지 봐야죠.


public virtual void RegisterClientScriptBlock(
   string key, //스크립트를 식별하는 키
   string script //클라이언트로 등록될 스크립트
);


RegisterClientScriptBlock 이란 이름에서 느껴지듯이 클라이언트 스크립트를 등록하는 기능이 있습니다. Page객채에 등록되기 때문에 User Control이나 서버 컨트롤 등 페이지 어디서나 등록상황을 확인 할 수가 있습니다. 이미 등록되있는 스크립트를 같은 key로 다시 등록하려고 하면 등록이 되질않습니다.(에러는 없음)


script 문자열에는 “<script language=어쩌구>”와 “</script>”가 포함되어 있어야 합니다. RegisterClientScriptBlock 메소드가 저절로 만들어주질 않으니까요. 클라인언트로 출력될 때, script 문자열이 삽입되는 위치는 “<form action=어쩌구>” 문장 바로 다음입
니다.


public virtual void RegisterStartupScript(
   string key, //스크립트를 식별하는 키
   string script //클라이언트로 등록될 스크립트
);


위의 RegisterClientScriptBlock 메소드와 전달되는 인자는 동일합니다. 주의 하실 점은 RegisterClientScriptBlock의 key와 RegisterStartupScript의 key는 아무런 관계가 없습니다. 즉 자기자신을 통해 등록된 스크립트만 각자가 책임을 진다는 것이죠. 음.. 이것이 스크립트 중복문제를 일으킬 수도 있겠네요. RegisterClientScriptBlock메소드와 틀린점은 script 문자열이 삽입되는 자리가 form문의 마지막인 </form>바로 전이라는 점입니다.


RegisterStartupScript라는 이름에서 알 수 있듯이 클라이어트에서 페이지가 시작될 때 사용할 스크립트를 주로 등록합니다. 페이지 뒷부분으로 스크립트를 등록하는 이유는 모든 페이지 요소가 로딩되기 전에 시작함수가 이 요소를 호출하면 에러가 발생하기 때문입니다. 이 메소드의 이름을 보면 <body>부분에 onload=”어쩌구”를 추가해 줄 것 같지만 그렇지는 않습니다.(직접하세요)


public void RegisterOnSubmitStatement(
   string key, //스크립트를 식별하는 키
   string script //form의 OnSubmit=”이부분에 들아갈 스크립”
);


인자의 역할은 의의 메소드들과 비슷하고, script 가 삽입되는 곳이 다음과 같습니다. <form … onsubmit=”여기에 삽입”> 이 넘을 활용하면 예전과 같이 스크립트로 주민등록번호 검사와 같은 기능을 넣을 수있습니다.


public void RegisterArrayDeclaration(
   string arrayName, //배열을 식별하는 키이자 배열의 이름
   string arrayValue //배열의 값, 사용예 ? “a,b,c”
);


이 넘은 서버컨트롤을 클라이언트 스크립트에서 접근하려고 할 때 이용합니다. 뒤에서 소스를 보시는 것이 이해가 빠르리라 생각됩니다.


public bool IsClientScriptBlockRegistered(
   string key //스크립트를 식별하는 키
);


RegisterClientScriptBlock 메소드로 등록된 스크립트를 검사합니다. Key 가 이미 등록되었다면 true를 반환합니다.


public bool IsStartupScriptRegistered(
   string key //스크립트를 식별하는 키
);


RegisterStartupScript 메소드로 등록된 스크립트를 검사합니다. Key 가 이미 등록되었다면 true를 반환합니다.


public virtual void RegisterHiddenField(
   string hiddenFieldName, //히든필드를 식별하는 키이자 필드의 이름
   string hiddenFieldInitialValue
);


히든 필드를 출력할 때 사용합니다. 동일한 키의 히든필드가 이미 존제하면 무시되죠.


자 그럼 위의 메수드들을 구현한 실제 예를 보죠. 전 강좌와 중죽되는 내용이 많아서 중요한 부분만 실었습니다. 나머지 부분은 파일로 올려놓은 소스를 참고하시길…(음.. 성의가 없어졌군요^^)


private void Page_PreRender(object sender, System.EventArgs e)
{
    MyLinkButton01.Attributes["href"] = Page.GetPostBackClientHyperlink
(Button1,"MyLinkButton01");
    MyButton01.Attributes["onclick"] = Page.GetPostBackEventReference
(Button1,"MyButton01");
    MyButton02.Attributes["onclick"] = "ImitationSubmit();";

    Message.Text = "<br>";
    Message.Text += "Post Back만드는 스트립트 <br>";
    Message.Text += "<br>";
    Message.Text += "GetPostBackEventReference :" + Page.GetPostBackEventReference(Button1,"") + "<br>";
    Message.Text += "GetPostBackClientEvent :" + Page.GetPostBackClientEvent(Button1,"") + "<br>";
    Message.Text += "GetPostBackClientHyperlink :" + Page.GetPostBackClientHyperlink(Button1,"") + "<br>";
    //Page.RegisterClientScriptFile("abc","JavaScript","/none.js");

    string StartupScript = @"
<script language=JavaScript>
<!--
function OnPageLoad() {
alert(""RegisterStartupScript 메소드로 등록된 스크립트입니다."");
myArray[0].value = ""크라이언트 스크립으로 대입 되었습니다."";
}
//-->
</script>
    ";

    string ClientScriptBlock = @"
<script language=JavaScript>
<!--
function CheckInput() {{
var aBoxID = new Array(""{0}"",""{1}"",""{2}"");

for (var i=0;i<myArray.length;i++)
{{
    if (myArray[i].value == """")
    {{
        alert(aBoxID[i] + "" 상자에 입력하셔야죠."");
        return false;
    }}
}}
var sDisplay = ""잘 입력하셨습니다\n입력하신 내용은\n\n"";
sDisplay += ""{0} : "" + myArray[0].value + ""\n"";
sDisplay += ""{1} : "" + myArray[1].value + ""\n"";
sDisplay += ""{2} : "" + myArray[2].value + ""\n"";
alert(sDisplay);
return true;
}}

function TrySubmit()
{{
return CheckInput();
}}

function ImitationSubmit()
{{
if (CheckInput()) WebForm1.submit();
}}
//-->
</script>
    ";


    if (!Page.IsClientScriptBlockRegistered("StartupScript"))
        Page.RegisterStartupScript("StartupScript", StartupScript);

    if (!Page.IsClientScriptBlockRegistered("ClientScriptBlock"))
    {
        ClientScriptBlock = String.Format(ClientScriptBlock,TextBox1.UniqueID, TextBox2.UniqueID, TextBox3.UniqueID);
        Page.RegisterClientScriptBlock("ClientScriptBlock", ClientScriptBlock);
    }

    string RegArr = "document.getElementById(\"{0}\"),document.getElementById(\"{1}\"),document.getElementById(\"{2}\")";
    RegArr = String.Format(RegArr, TextBox1.UniqueID, TextBox2.UniqueID, TextBox3.UniqueID);
    Page.RegisterArrayDeclaration("myArray", RegArr);

    Page.RegisterOnSubmitStatement("submit", "return TrySubmit()");
}

다른 부분은 다 설명드린 내용이고 스크맆트를 정의하는 문자열에 대해서만 설명드리면 되겠네요. 아래는 스트립트 문자열 부분인데요. 잘 보시면 상당히 편리한 방법으로 스크립트 문자열을 정의 하고있습니다. 바로 @””리터럴 때문이데 알아두시면 편리하게 사용하실 수 있으실 겁니다. @””리터럴의 장점은 이스케이프 시퀀스의 처리를 하지 않기 때문에 자바 스크립트를 정의하는데 따봉입니다. 사용상 주의점은 “를 나따낼 경우 “”로 표시하시면 됩니다.아래의 코드를 만약 일반적인 방법으로 처리하려면 상당히 코드가 지저분 해지겠죠

    string ClientScriptBlock = @"
<script language=JavaScript>
<!--
function CheckInput() {{
var aBoxID = new Array(""{0}"",""{1}"",""{2}"");

for (var i=0;i<myArray.length;i++)
{{
    if (myArray[i].value == """")
    {{
        alert(aBoxID[i] + "" 상자에 입력하셔야죠."");
        return false;
    }}
}}
어쩌구 ….
저쩌구 …..
//-->
</script>
    ";


그런데 위의 코드 중에서 이상한 부분은 {나 }가 사용되는 자리에 {{와 }}가 사용된게 이상하실지도 모릅니다. 이 부분은 String.Format(어쩌구)함수 사용시 ‘{0}’이런식으로 형식문자를 받아들이기 때문에 {{와 }}를 사용한겁니다. 일단 위의 문라열이 String.Format(ClientScriptBlock,어쩌구) 부분을 지나가면 {{와 }}는 모두 {와 }로 바뀌게 됩니다.

마지막으로 스크립트 중복 해결에 대한 저의 생갈을 말씀드리고 허접한 강좌를 마칠까합니다.(벌써!!)RegisterClientScriptBlock과 RegisterStartupScript이 서로 별도로 중복 검사를 하므로 RegisterClientScriptBlock용의 스크립트와 RegisterStartupScript용의 스크립트로 두게의 파일을 두어 인클루드 해서 쓰면 중복문제를 피할 수 있다고 봅니다. 사용하는 모든 스크립트를 두게의 파일에 다 넣는 것이죠.(나의 허접한 의견은 여기서 끝납니다)

+ Recent posts