관리 메뉴

IT 쟁이

ASP.NET 가이드 2. 숫자/문자 입력 텍스트 박스 만들기 본문

JavaScript

ASP.NET 가이드 2. 숫자/문자 입력 텍스트 박스 만들기

클라인STR 2008. 1. 21. 20:20

ASP.NET 가이드 2. 숫자/문자 입력 텍스트 박스 만들기

저자: 한동훈(traxacun)

[지난 기사 보기] ASP.NET 가이드 1. 자바 스크립트 사용하기


0. 소개

지난 시간에는 ASP.NET에서 원하는 컨트롤에 포커스를 지정하기 위한 SetFocus() 함수를 작성해보았다. 이번에는 여기에 숫자만 입력받는 텍스트 박스와 문자만 입력받는 텍스트 박스를 제작해볼 것이다. 마지막으로 HtmlInputText 컨트롤과 TextBox 컨트롤 모두에 원하는 동작을 정의하기 위한 코드 작성법을 살펴볼 것이며, 이를 위해 ASP.NET 컨트롤 트리에 대해 살펴볼 것이다.


1. 숫자 입력 텍스트 박스

첫번째는 간단한 숫자 입력만 받는 자바스크립트를 작성하는 것이다.




[그림1] AddNumeralsOnlyScript() 구현


함수 이름이나 스크립트 이름은 모두 문자열 상수로 정의하여 사용하고 있다. 사용자가 입력한 문자 코드를 알아내는 부분이 다소 길게 작성되어 있어서 의아하게 여기는 분도 있을 것이다. 첫번째 evt를 구하는 부분인데, 이벤트를 의미하는 문구가 IE에서는 event이며, Netscape와 같은 다른 브라우저는 evt를 사용한다.

문자 코드를 알아내는 부분을 이해하기 위해서는 먼저 Netscape와 같은 브라우저와 IE 브라우저의 차이점을 알아야한다. W3C DOM Model에서는 keypress, keyup, keydown 이벤트에 대해 정의하고 있지만 어떤 값을 주고 받는지에 대한 정의가 없다. 이 때문에, 각 브라우저 제작사는 자기들만의 방식으로 값을 저장하고 있다. 예를 들어, Netscape 호환 브라우저들은 keypress 이벤트에 대해서는 charCode 속성을 사용하여 값을 알아낼 수 있으며, 이 값은 ASCII 값이다. keyup과 keydown 이벤트에 대해서는 keyCode 속성을 사용하여 값을 알아낼 수 있지만 charCode는 keyup/keydown 이벤트에서 키 값을 알아낼 수 없다. Netscape 4 - 이하 NS4 -에서는 which 속성을 사용해서 키 값을 가져온다. 반면에, IE에서는 이들 이벤트에 대해서 keyCode 속성으로 값을 알아낼 수 있으며, 이 값은 유니코드로 되어 있다.

위와 같은 다소 복잡한 코드를 사용하게 되는 것은 Netscape의 일관되지 못한 구현 때문이라 할 수 있다. 사용자가 숫자가 아닌 다른 문자를 입력했을 때 보여주기 위한 기본 문구를 numeralsOnlyWarning 변수에 작성했다.

스크립트에서 보면 NumeralsOnlyWarning 속성을 사용하고 있다. NumeralsOnlyWarning 속성을 사용하는 이유는 다른 경고 메시지를 보여주기 위해 소스 코드를 변경하지 않아도 되게 하기 위한 것이다. 또한, 자바 스크립트에서 문자열을 사용하기 위해 "나 '를 사용할 수 있다. 위 자바 스크립트는 문자열을 사용하기 위해 '를 사용하고 있는데, 사용자가 경고 문구에 '를 사용하면 스크립트 에러가 발생한다. 스크립트 오류를 방지하기 위해 '를 \'로 변환해야 한다. 다음은 이러한 처리를 해주는 NumeralsOnlyWarning 속성 구현이다.




[그림2] NumeralsOnlyWarning 속성


C#에서는 문자열에서 \를 나타내기 위해 \\를 사용해야 한다. 이와 같은 스크립트를 작성했으면 RegisterClientScript() 함수에 AddNumeralsOnlyScript()를 추가한다. 이제, 컨트롤에 숫자만 입력받을 수 있게 하기 위한 방법을 살펴보자.




[그림3] SetNumeralsOnly 구현


가장 있을법한 구현은 직접 TextBox 컨트롤을 인자로 넘겨 받아서 위와 같은 코드를 작성하는 것이다. 실제로 위 코드를 사용하는 방법은 다음과 같다.

SetNumeralsOnly( TextBox1 );
SetNumeralsOnly( TextBox2 );

그러나, 복잡한 웹 디자인을 넘겨 받아서 작성하는 경우에 웹 컨트롤로 변환하는 것은 시간도 많이 걸리고, 디자인이 깨지는 경우가 많기 때문에 대부분 HTML 서버 컨트롤로 변환하여 작업하는 경우가 대부분이므로 HtmlInputText 컨트롤에 대해서도 위와 같이 숫자 전용 필드로 지정할 수 있게 해야 한다.




[그림4] SetHtmlNumeralsOnly 구현


그림3과 그림4의 SetNumeralsOnly와 SetHtmlNumeralsOnly 구현은 두 가지 문제점이 있다. 한가지는 경우에 따라 ASPX 페이지에서 웹 폼 컨트롤로 변환하거나 HTML 서버 컨트롤로 변환하는 경우에 코드에 유연성이 없기 때문에 코드도 함께 변경해야 한다는 것이다. 다른 하나는 위 함수들은 넘겨받는 인자에 대한 널(NULL) 체크를 하지 않기 때문에 Load, PreRender 이벤트가 아닌 Init 이벤트에서 호출될 경우 NullReferenceException 예외를 발생시킬 수 있다는 점이다.




[그림5] 개선된 버전


그림5에서 개선한 코드는 모두 해당 컨트롤을 직접 전달받아서 처리를 하는 코드이며, 컨트롤의 이름을 넘겨받아서 작성하는 코드는 아니다.

SetNumeralsOnly( "TextBox1" );

이와 같은 방식으로 코드를 작성하고, 사용할 수 있는 SetNumeralsOnly() 함수를 작성하는 방법은 없을까? 이를 위해서는 먼저 ASP.NET이 페이지를 어떤 계층 구조로 구분하고 있는지 알아야 한다.


2. ASP.NET 컨트롤 계층 구조




[그림6] 컨트롤 계층구조


ASP.NET에서 모든 컨트롤은 Control 클래스를 상속받는다. ASPX 페이지의 기본이 되는 Page 클래스로 Control 클래스를 상속받는다. Control 클래스는 컨트롤이 갖고 있는 자식 컨트롤들을 Controls 속성으로 접근할 수 있다.

Page 클래스로 마찬가지로 Controls 속성을 갖고 있으며, 이 속성을 사용해서 ASP.NET 페이지가 어떻게 처리되는지 살펴볼 것이다. 먼저, ControlTree01.aspx 페이지를 작성하고 Page_Load 메서드에 다음과 같은 코드를 작성한다.




[그림7] ControlTree01.aspx.cs



[그림8] ControlTree01 실행결과


그림8에서 알 수 있는 것처럼 웹 폼을 추가한 상태에서도 3개의 컨트롤이 있는 것을 확인할 수 있다. 여기에 TextBox 컨트롤과 Button 컨트롤을 추가하고 페이지를 다시 실행해보자.




[그림9] TextBox와 Button을 추가하고 실행한 화면


그림9에서 알 수 있는 것처럼 화면에 컨트롤을 추가해도 결과에는 변함이 없다. ASPX 페이지에 추가하는 컨트롤은 실제로 HtmlForm 컨트롤의 자식 컨트롤이기 때문에 그렇다. 따라서, TextBox와 Button 컨트롤의 결과를 알고 싶다면 HtmlForm.Controls 속성에 대해서 루프를 돌아야한다. 다음은 자식 컨트롤을 알아보기 위해 Page_Load를 변경한 것이다.




[그림10] 자식 컨트롤을 보여주는 구현



[그림11] 실행화면


[그림11]에서 볼 수 있는 것처럼 ASPX 페이지에 추가한 컨트롤이 파란색으로 표시되어 나타나는 것을 알 수 있다. 컨트롤 이름으로 컨트롤을 찾아주는 FindControl()을 이용하면 TextBox 컨트롤이든, HtmlInputText 컨트롤이든 구분하지 않고 사용할 수 있는 SetNumeralsOnly() 함수를 만들 수 있을 것이다.

다음은 [그림3, 4, 5]에서 소개한 SetNumeralsOnly()와 SetHtmlNumeralsOnly()를 하나로 합친 최종 버전의 SetNumeralsOnly를 구현한 것이다. - [그림 3,4,5]에 소개한 코드는 모두 잊어주기 바란다. ^^;




[그림12] SetNumeralsOnly()의 실제 구현


실제로 이 코드는 모두 한 줄로 작성된 것을 편집을 위해서 여러 줄로 나눈 것이다. FindControl()을 사용해서 컨트롤을 찾아본다. 첫번째 if는 TextBox 컨트롤인 경우에 스크립트를 추가하고, 두번째 if는 HtmlInputText 컨트롤인 경우에 스크립트를 추가한다.

지금까지 숫자 전용 텍스트 박스를 만들기 위한 코드를 살펴보았다. 알아야 할게 너무 많아서 숫자 전용 텍스트 박스 만들기 보다 다른 내용에 대해 살펴본 게 더 많았다. 다음은 문자 전용 텍스트 박스를 만들기 위한 코드이다. 이 코드는 숫자 전용 필드 만들기와 거의 대부분이 동일하다.

그러나, 모두 동일하다고 하면 독자들도 재미없을 줄로 안다. 그림12에 소개한 SetNumeralsOnly()는 문제점을 갖고 있으니 곰곰히 생각해보기 바란다. 다음에 소개할 SetLettersOnly()에서는 이러한 문제점을 해결한 것이다. 물론, 독자 여러분에게 제공되는 소스 코드도 그림12와 같은 코드가 아니다!


2. 문자 입력 텍스트 박스

여기서도 먼저 문자 입력을 받기 위해 AddLettersOnlyScript()를 작성한다.




[그림13] AddLettersOnlyScript() 구현



[그림14] LettersOnlyWarning 속성]



[그림15] SetLettersOnly() 구현



[그림16] SetLettersOnly( string ) 구현


[그림15]의 구현과 [그림12]의 구현의 차이를 눈치 빠른 독자들은 알아냈을 것이다. ASP.NET 1.1의 버그인지 모르지만 경우에 따라 HtmlForm 컨트롤이 화면에 나타나지 않고 LiteralControl로 표현되는 경우가 있다. 그런 경우에 [그림12]와 같은 구현 방식은 동작하지 않는다. 때문에 전체 컨트롤에 대한 루프를 돌면서 FindControl을 이용해서 컨트롤을 찾아내야 한다.

두번째로 GetType()을 이용해서 타입을 비교하는 대신 is 연산자를 사용하고 있다. GetType()을 사용하는 것은 VB.NET이나 다른 닷넷 언어로 옮기기 쉬운 장점이 있지만 문자열 비교를 하기 때문에 위와 같이 전체 클래스명을 입력할 때 대소문자만 틀려도 오류를 찾아내기가 어렵다. ToUpper() 같은 함수까지 호출하려면 코드가 상당히 길어지고 지저분해진다. is 연산자는 특정 형식(Type)과 호환되는지의 여부만 판별하여 true/false만을 반환하기 때문에 코드도 읽기 쉬워진다.

[그림12] SetNumeralsOnly() 구현의 또 다른 문제점은 해당하는 컨트롤을 찾아서 작업을 했으면 끝내야 하는데, 그렇지 않고 무작정 전체 컨트롤에 대한 루프를 돈다는 것이다. 여기서는 원하는 작업이 끝나면 break를 사용해서 루프를 바로 끝내고 있다.

보다 나은 코드: EnrollStartupScript()와 EnrollClientScript()
[그림1]과 [그림13]은 각각 AddNumeralsOnlyScript()와 AddLettersOnlyScript()를 구현하고 있다. 이 코드에는 아무 문제도 없을까?




[그림17] 코드 오염의 증거


[그림1]과 [그림13]에서는 이와 비슷한 코드가 중복해서 사용되고 있다. 앞으로 추가하는 스크립트가 많아지면 많아질수록 이러한 코드는 증가할 것이다. 코드 중복에 대해서는 많은 사람들이 공감을 표하면서도 위와 같은 한줄짜리 코드에 대해서도 함수로 독립시킬 필요가 있을까? 라고 반문할 것이다. "우린 시간이 없어! 대충하자고!" 라는 이야기를 많이 듣고, 솔직히 때로 대충하기도 한다.

굳이 [리팩토링], [Code Complete], [Anti-Patterns]와 같은 책들의 권위(?)를 빌리지 않더라도 대답은 "한줄짜리 코드라도 중복된다면 함수로 독립시켜야 한다."이다.

더욱이 ASP.NET 2.0에서는 Page.RegisterStartupScript()는 Page.ClientScript.RegisterStartupScript()로 변경되었으며, Page.RegisterClientScriptBlock()은 Page.ClientScript.RegisterClientScriptBlock()으로 변경되었다. 나중에 ASP.NET 2.0용 코드를 작성할 경우 중복된 코드를 내버려 둔다면 모든 코드를 뜯어고쳐야한다.

.NET에 이미 RegisterStartupScript와 RegisterClientScriptBlock 함수가 있기 때문에 EnrollStartupScript와 EnrollClientScript로 함수 이름을 정의했다.




[그림18] EntrollStartupScript/EnrollClientScript 구현


Enroll 함수를 사용하여 Add 함수를 변경하는 것은 독자의 몫으로 남겨둔다. ^^;


4. BasePage 사용하기

SetNumeralsOnly.aspx 페이지를 생성하고 다음과 같이 코드를 변경하였다.
Mona.Web.UI.BasePage를 상속하였고, 컨트롤 인스턴스와 문자열로 숫자 입력 필드를 지정하는 경우가 모두 동작하는지 테스트하기 위해 TextBox와 HtmlInputText 컨트롤를 각각 2개씩 추가하였다.




[그림19] SetNumeralsOnly.aspx.cs



[그림20] SetNumeralsOnly.aspx 실행화면


[그림20]에서 볼 수 있는 것처럼 문자를 입력하려고 하면 경고 메시지가 나타나는 것을 볼 수 있다.
SetNumeralsOnly()의 자바 스크립트 설명에서 생략한 부분이 있는데 문자중에 "."은 입력할 수 있다. 대부분의 업무용 프로그램에서 요율등을 입력할 때 소수점까지 입력해야 하기 때문에 이것을 제한에서 제외했다. 순수하게 숫자만 입력받고 싶다면 AddNumeralsOnlyScript()의 구현부분을 수정하기 바란다.

다음시간에는 주민번호, 사업자 등록번호 등에 적용할 수 있는 ApplyFormat() 구현과 카드 번호 입력시 포커스를 자동으로 옮기는 자동포커스, 마우스 드래깅과 같은 긁어가기를 차단하는 방법에 대해 소개할 것이다.

[참고자료]
Event Handling in the DOM

출처 - 한빛네트워크
Comments