그 밖의 유용한 VBScript 5.X 버전의 기능들
지금까지 일련의 글들을 통해서 VBScript 5.X 버전에서 제공되는 여러 가지 새로운 기능들 중에서도 가장 중요한 위치를 점하고 있는 두 가지 기능, 즉 정규 표현식(Regular Expression)과 클래스(Class)에 관해서 살펴봤다. 이 두 가지 기능이야말로 VBScript가 5.X 버전대에서 보여주는 변화의 핵심이라고 말할 수 있을 것이다.
그러나 지극히 당연한 얘기겠지만 정규 표현식과 클래스가 VBScript 5.X 버전의 변화의 전부는 아니며 그 밖에도 유용한 기능들이 폭넓은 수준으로 지원되고 있는데, 바로 With 문이나 Execute 문(또는 ExecuteGlobal 문), Eval() 함수, 그리고 Timer() 함수 등이 그 대표적인 사례이다. 따라서, 본문에서는 바로 이런 기능들에 대해서 살펴보도록 하겠다.
Execute 문과 ExecuteGlobal 문, 그리고 Eval() 함수
작업을 하다 보면 종종 다음과 같은 상황을 접하게 된다. 가령, 어떤 작업을 처리하는데 대략 세 네 가지 정도의 상황이 발생할 수 있다고 가정해보도록 하자. 당연히 그 각각의 상황마다 프로그램의 처리는 달라진다. 이를테면, 수학적인 문제를 처리한다고 가정해 볼 때, 상황 A의 경우에는 a + b라는 공식을 사용해서 값을 처리하고, 상황 B의 경우에는 a + b - c라는 공식을 사용해서 값을 처리해야 하며, 상황 C의 경우에는 a * b * 1.2라는 공식을 사용해서 값을 처리해야 한다고 가정해보자.
일반적인 방법으로 이 문제를 해결하려면 아마도 이런 작업을 처리하는 사용자 함수를 하나 만들어야 할 것이다. 그리고, a, b, c의 값과 상태값을 이 함수에 인자로 넘겨주면, 내부 코드에서는 Case 문이 사용될 확률이 높다. 그런 다음, 인자로 넘겨진 상태값에 따라 적절한 공식을 사용하여 값을 구하고 그 결과를 리턴해준다. 이것이 보통 상식적인 처리 방법이다. 아마도 실무에서 이와 가장 유사한 처리 사례는 예금이나 채권 등의 이자 계산에서 찾아볼 수 있지 않을까 한다.
그런데, 이런 처리 방식은 본질적인 문제점을 갖고 있다. 무슨 얘긴가 하면 코드가 완성된 후에, 발생할 수 있는 상황이 늘어나거나 상황에 따른 공식이 변경되면 코드 자체를 수정해야 한다는 점이다. 또한 극단적인 예로, 발생할 수 있는 상황이 세 네 가지가 아닌 수 십여 가지 혹은 수 백여 가지라면 동일한 갯수 만큼 Case 문이 사용되야 한다. 이런 프로그래밍 방식은 구조적인 면에서 가급적 피하는 것이 바람직하다.
이 상황을 타개하기 위한 방법에는 어떤 방법이 있을까? VBScript 5.X 이전의 버전에서는 그냥 그대로 Case 문을 경우의 수 만큼 작성하는 방법 외에는 별다른 방법이 없었다. 아무리 고민을 해봐도 더 이상 해결책이 없는 것이다. 그런데, 놀라운 점은 JavaScript에는 이런 상황에 유용하게 사용할 수 있는 기능이 이미 오래전부터 존재하고 있었다는 사실인데, 바로 eval() 함수가 그것이다.
아마도 JavaScript에 능숙한 분들은 이미 이 함수에 관해서 잘 알고 계실 것이다. 이 eval() 함수는 코드 자체를 문자열 변수로 다룰 수 있게 해준다. 백 번 설명하는 것보다 코드를 한 번 보는 것이 더 이해가 쉬울 것이다. 결론부터 얘기하자면 다음의 두 코드는 100% 동일하다.
<script language="JavaScript" type="text/javascript">
<!--
var mydate = new Date();
... 생략 ...
//-->
</script>
이것이 일반적인 코드다. 그렇다면 이번에는 eval() 함수를 사용하는 경우를 살펴보도록 하자.
<script language="JavaScript" type="text/javascript">
<!--
eval("var mydate = new Date();");
... 생략 ...
//-->
</script>
이미 말했듯이 이 두 코드의 실행 결과는 100% 동일하다. 여기서 중요한 사실은 eval() 함수에 인자로 넘어가는 코드가 문자열 형태를 갖고 있다는 점이다. 따라서, 코드에서 프로그래머 임의로 편집하는 것이 가능해진다. 한 마디로 eval() 함수에 인자로 넘겨질 코드 자체를 편집할 수 있게 되는 셈이다. 마치, SQL 구문을 문자열 형태로 편집하는 것처럼.
그렇다면 이번에는 eval() 함수가 실무에서 사용되는 조금 더 구체적인 사례를 간단하게 한 가지 들어보도록 하겠다. 여러분이 작성하고 있는 임의의 HTML 문서에 myForm이라는 이름을 가진 폼(Form)이 하나 존재하고 있고, 그 내부에는 이름이 myTextbox_n인 n개의 텍스트박스(Textbox)가 존재한다고 가정해보자. 여기서 n은 텍스트박스의 순서를 의미하는 숫자값으로서 1부터 시작한다. 이때 JavaScript를 이용해서 그 n개의 텍스트박스에 순서대로 1부터 n까지의 초기값을 입력해야 할 경우, JavaScript를 처음 다뤄보시는 분들이라면 아마도 다음과 같은 코드가 머리 속에 떠오르기 쉬울 것이다.
<script language="JavaScript" type="text/javascript">
<!--
document.myForm.myTextbox_1.value = "1";
document.myForm.myTextbox_2.value = "2";
document.myForm.myTextbox_3.value = "3";
... 중략 ...
document.myForm.myTextbox_n.value = "n";
//-->
</script>
그러나 실무에서는 이런 코드는 좀처럼 사용되지 않는다. 무엇보다 이 코드는 n이라는 불확실한 값에 의지해서 프로그래밍되었기 때문에 상황이 바뀔 때마다 매번 코드도 수정해야 한다. 또한 위에서 얘기했던 VBScript의 사례와 동일하게 코드의 줄 자체가 n개 만큼 요구되므로 n의 값이 매우 큰 경우 코드를 작성하는 일 자체가 힘겨워진다. 그렇다면 이번에는 다음 코드를 살펴보도록 하자.
<script language="JavaScript" type="text/javascript">
<!--
var tempObj;
for (var i = 1; i <= n; n++)
{
tempObj = eval("document.myForm.myTextbox_" + i);
tempObj.value = i;
}
//-->
</script>
이 코드는 동일한 문제를 eval() 함수를 사용하여 해결하고 있다. 휠씬 더 깔끔하고 간단한게 문제를 처리하고 있다는 것을 알 수 있다. 이처럼 eval() 함수를 사용하면 코드 그 자체를 문자열처럼 다룰 수 있으므로 문제를 해결하는데 큰 도움이 된다.
이제 다시 VBScript에 관한 얘기로 돌아가 보자. 만약 방금 살펴본 JavaScript의 eval() 함수와 동일한 기능이 VBScript에서도 지원된다면 당면한 문제를 해결하는데 큰 도움이 될 것이다. 필자가 이런 얘기를 하는 이유는? 그렇다. 바로 VBScript 5.X 버전부터는 JavaScript의 eval() 함수와 동일한 기능이 지원되기 때문이다. 다만, 한 개가 아닌 세 개의 구문이 지원되는데 Execute 문과 ExecuteGlobal 문, 그리고 Eval() 함수가 바로 그것이다.
이렇게 여러 개의 구문이 지원되는 데는 약간의 이유가 있는데, 그 중 첫 번째 이유는 VBScript의 = 연산자가 값 할당과 비교의 두 가지 의미를 지니고 있기 때문에 이 두 경우를 명확하게 구분하기 위함이고, 다른 한 가지 이유는 코드 그 자체의 인스턴스에 관한 문제 때문이다. 그냥 쉽게 생각해서 JavaScript의 eval() 함수를 조금 더 세분화했다고 보면 맞을 것이다.
첫 번째로 살펴볼 구문은 Eval() 함수다. VBScript의 Eval() 함수와 JavaScript의 eval() 함수는 그리 다른 점이 없다. 다만, VBScript의 Eval() 함수는 이를테면 수식 계산 전용이라고 말할 수 있다. 다음의 두 코드는 역시 100% 동일한 실행 결과를 보장한다.
<%
'** 일반적인 방법을 사용하여 1 + 2 의 결과값을 얻는다.
Response.Write "<font size=-1>" & (1 + 2) & "</font><br>"
'** Eval() 함수를 사용하여 1 + 2 의 결과값을 얻는다.
Response.Write "<font size=-1>" & Eval("1 + 2") & "</font><br>"
%>
이 코드에서 두 가지 서로 다른 방법으로 얻은 수식 계산의 결과는 당연하게도 두 경우 모두 동일하게 3이 리턴된다. 그런데, 다음과 같은 경우는 얘기가 조금 복잡해진다.
<%
Dim i
i = 10
Response.Write "<font size=-1>" & Eval("i = 11") & "</font><br>"
%>
이 경우 문제의 근본적인 핵심은 = 연산자다. 익히 알고 있는 바와 같이 VBScript의 = 연산자는 두 가지의 기능을 갖고 있다. 그 첫 번째 기능은 좌변에 위치한 변수에 우변에 위치한 값을 대입하는 기능이고, 두 번째 기능은 좌변의 값과 우변의 값을 비교해서 값이 동일한지 여부를 파악하고 True 또는 False를 리턴해주는 비교의 기능이다. 문제가 복잡해지는 이유는, 위와 같은 경우 과연 이 두 가지 기능 중 어떤 기능이 사용될지 모른다는 점 때문이다.
결론부터 얘기하면 Eval() 함수에 인자로 넘겨진 수식에 = 연산자가 존재하는 경우, = 연산자는 항상 두 번째 기능, 즉 비교 연산자로 해석된다. 반대로 동일한 수식이 Execute 문이나 ExecuteGlobal 문에 넘겨지면 항상 첫 번째 기능, 즉 대입 연산자로 해석된다. 따라서, 위의 코드에서 = 연산자는 비교 연산자로 해석되고 결과로는 False를 리턴한다.
또 한 가지 주의할 점은 Eval() 함수에 인자로 넘겨지는 코드의 실행 결과에는 반드시 리턴값이 존재해야 한다는 점이다. Execute 문이나 ExecuteGlobal 문이 그 분류상 '문'으로 다루어지는 반면, Eval() 함수가 말 그대로 '함수'로 다뤄지는 데는 바로 이런 이유가 숨어있는 것이다. 따라서, 다음과 같은 코드는 구문 오류를 발생시킨다.
<%
'** 구문 오류가 발생한다.
Response.Write "<font size=-1>" & Eval("Dim strMyString") & "</font><br>"
%>
이런 점들로 미뤄 볼 때 VBScript 의 Eval() 함수는 그야말로 수식 계산에 최적화되었다고 말할 수 있을 것이다. 따라서, Eval() 함수를 응용하면 다음과 같은 일들이 가능해진다. 사용자들로부터 수 십개 또는 수 백개의 다양한 수식, 이를테면 금융 상품에 따른 이자 계산 공식 같은 것을 입력받아 데이타베이스에 저장해 놓고, 필요할 때마다 데이타베이스로부터 수식을 읽어들여 사용하는 것이다. 만약, 새로운 금융 상품이 만들어지거나 이자율 등이 변경되는 경우에는 코드를 수정하지 않고서도 데이타베이스에 저장된 수식을 추가하거나 수정하는 것만으로도 새로운 수식의 적용이 즉각 가능해진다.
반면, 특정한 수식 처리가 요구되는 것이 아니라 복잡하고 많은 분량의 코드 집합을 처리해야 하는 경우라면, Eval() 함수보다는 Execute 문이나 ExecuteGlobal 문을 사용하는 것이 더 바람직하다. 그 명칭에서 짐작할 수 있는 것처럼 Execute 문과 ExecuteGlobal 문은 한 가지 차이점만을 제외한다면 거의 동일한 기능을 제공해준다. 그 차이점에 관해서는 잠시 뒤에 다시 자세하게 설명하기로 하고 일단은 Execute 문을 기준으로 글을 진행해 나가도록 하겠다. 역시 마찮가지로 다음의 두 코드는 100% 동일한 실행 결과를 보장한다.
<%
Dim i
'** 일반적인 방법을 사용하여 변수 i 에 값 10 을 대입한다.
i = 10
Response.Write "<font size=-1>" & i & "</font><br>"
'** Execute 문을 사용하여 변수 i 에 값 10 을 대입한다.
Execute "i = 10"
Response.Write "<font size=-1>" & i & "</font><br>"
%>
이미 앞에서 얘기했던 것처럼 Execute 문이나 ExecuteGlobal 문은 인자로 넘겨진 코드 문자열에 = 연산자가 존재하는 경우 항상 대입 연산자로 해석한다. 따라서, 이 코드에 사용된 Execute 문은 변수 i에 값 10을 대입하게 된다. 코드를 좀 더 확실하게 파악하려면 변수 i에 대입되는 값을 직접 변경해보면서 그 결과를 확인해보는 것이 좋을 것이다. 그러나, Execute 문이나 ExecuteGlobal 문의 진정한 능력은 이 정도 수준에서 그치지 않는다. 그 사용법에 따라서 실시간으로 아예 새로운 프로시저를 선언하거나 덮어쓰는 것이 가능해진다.
개인적으로 Execute 문과 ExecuteGlobal 문의 이런 기능은 그야말로 대단한 것이라고 생각한다. Sub 프로시저나 Function 프로시저를 코드에서 동적으로 추가하거나 덮어 쓰는 것이다! 말은 간단하지만 실로 놀라운 기능일 뿐더러 이런 기능의 응용 범위는 매우 다양하다. 사이트에 로그인한 자격 증명에 따라 이름이 동일한 프로시저가 아예 다른 프로시저로 동작한다던가, 또는 요즘 유행하는 스킨 기능을 게시판에 추가할 때 단지 이미지나 레이아웃 뿐만이 아닌 기능 자체의 스킨화도 구현이 가능해진다. 다음 코드는 myNewSub라는 이름의 새로운 Sub 프로시저를 동적으로 추가하는 코드다.
<%
Dim strSubCode
'** Sub 프로시저의 코드를 변수에 담는다.
strSubCode = "Public Sub myNewSub()" & vbCRLF & _
"Response.Write ""Execute 문에 의해 추가된 프로시저.""" & vbCRLF & _
"End Sub"
'** Execute 문을 사용하여 새로운 Sub 프로시저를 선언한다.
Execute strSubCode
'** 실제로 myNewSub() 프로시저를 호출한다.
Call myNewSub()
%>
이 코드를 실행시켜보면 정확하게 'Execute 문에 의해 추가된 프로시저.'라는 문자열이 줄력되는 것을 확인할 수 있을 것이다. 그런데, 만약 이 코드와 같이 Execute 문을 사용해서 선언한 프로시저와 동일한 이름의 프로시저가 이미 존재하고 있었다면 어떻게 될까? 다음 코드를 보도록 하자.
<%
Dim strSubCode
'** Sub 프로시저의 코드를 변수에 담는다.
strSubCode = "Public Sub myNewSub()" & vbCRLF & _
"Response.Write ""Execute 문에 의해 추가된 프로시저.""" & vbCRLF & _
"End Sub"
'** 일반적인 방식으로 Sub 프로시저를 선언한다.
Public Sub myNewSub()
Response.Write "일반적인 방식으로 추가된 프로시저."
End Sub
'** Execute 문을 사용하여 새로운 Sub 프로시저를 선언한다.
Execute strSubCode
'** 실제로 myNewSub() 프로시저를 호출한다.
Call myNewSub()
%>
코드를 자세히 살펴보면 알 수 있겠지만 이미 일반적인 방법으로 선언된 myNewSub() 프로시저를 Execute 문을 사용해서 다시 한 번 선언하고 있다. 그럼에도 불구하고 코드를 직접 실행시켜 보면 전혀 오류를 발생시키지 않고 'Execute 문에 의해 추가된 프로시저.'라는 문자열이 정상적으로 출력된다. 즉, 일반적인 방법으로 선언한 myNewSub() 프로시저가 Execute 문을 사용하여 선언된 myNewSub() 프로시저에 의해 덮어 씌여지는 것이다.
이때, 한 가지 착각하기 쉬운 점이 있는데 그것은 Execute 문의 호출 위치에 관한 것이다. 이 코드에서는 먼저 일반적인 방법으로 프로시저를 선언한 다음 Execute 문을 사용해서여 동일한 프로시저를 덮어썼다. 그런데, 만약 Execute 문을 사용하여 프로시저를 선언하고, 그 다음에 일반적인 방법으로 동일한 프로시저를 선언하면 어떻게 될까? 한 마디로 이번에는 순서를 거꾸로 하는 것이다. 여러분의 생각은 어떨지 모르겠다. 구문 오류가 발생할 것이라고 생각하는가? 아니면 나중에 호출된 일반적인 방법으로 선언한 프로시저가 활성화된다고 생각하는가?
정답은 놀랍게도 여전히 'Execute 문을 사용하여 선언한 프로시저의 인스턴스가 활성화된다.'이다. 이 결과가 믿어지지 않는다면 실제로 직접 테스트해보기 바란다. 이런 결과가 발생되는 이유는 다음과 같다. 많은 분들이 오해하고 있는 부분이기도 한데 VBScript가 아무리 스크립트 언어고 실시간으로 컴파일된다고는 하지만, 정말로 한 줄 컴파일하고 실행하고 한 줄 컴파일하고 실행하고하는 식으로 동작하지는 않는다는 점이다. 결국, VBScript 역시도 나름대로의 컴파일 단계를 갖고 있고 필자 역시도 이에 관해서 상세하게는 알지 못하지만, 큰 관점에서 보아 구문을 읽어들이고 선언된 변수와 프로시저의 코드를 메모리에 올리는 등의 작업을 하는 단계와 실제로 VBScript가 실행되는 단계, 즉 일반적으로 런타임(Runtime)이라고 부르는 단계가 서로 구분되어 존재한다.
따라서, Execute 문의 실제 코드상의 위치와는 상관 없이 일반적인 방법으로 선언된 프로시저의 코드가 메모리에 올라가는 시점은 위의 두 단계 중에서 첫 번째 단계가 된다. 그리고, Execute 문은 그 다음 단계, 즉 런타임시에 실행되어 메모리에 있는 프로시저의 코드를 덮어 쓰게 된다. 그러므로 일견 보기에는 코드에서 윗줄에 위치한 Execute 문이 먼저 실행되는 것 같지만 사실은 항상 나중에 실행되는 것이다.
마지막으로 Execute 문과 ExecuteGlobal 문간의 차이점에 대해서 알아보고 다음 주제로 넘어가기로 하겠다. 일반적으로 Class 문을 사용해서 작성한 클래스 내부에 위치한 프로시저들을 제외한 모든 VBScript의 프로시저들은 전역 인스턴스를 가진다. 그러나, Execute 문의 경우에는 사정이 조금 다르다. 다음 코드를 살펴보자.
<%
Dim strSubCode
'** Sub 프로시저의 코드를 변수에 담는다.
strSubCode = "Public Sub myNewSub()" & vbCRLF & _
"Response.Write ""Execute 문에 의해 추가된 프로시저.""" & vbCRLF & _
"End Sub"
'** Execute 문을 호출하는 코드를 포함한 새로운 Sub 프로시저.
'** 따라서 Execute 문을 호출하기 위해서는 먼저 이 myWrapperSub() 를 호출해야 한다.
Public Sub myWrapperSub()
Execute strSubCode
End Sub
Call myWrapperSub()
Call myNewSub()
%>
이 코드 역시 지금까지의 코드와 그다지 다른 점이 눈에 띄지 않는다. 다만 두드러지는 한 가지 중요한 차이점은 Execute 문의 호출이 또 다른 프로시저 내부에서 이뤄진다는 사실이다. 그러나, 의외로 이 코드는 실제로 실행을 시켜보면 오류가 발생한다. 그리고, 오류 메세지의 내용을 살펴본다면 myNewSub() 프로시저를 찾을 수 없다는 내용의 메세지임을 알 수 있을 것이다.
결국 이런 일이 발생하는 원인은 Execute 문의 실행 결과가 지역에 국한된 인스턴스이기 때문이다. 따라서, 이 코드와 같은 경우 Execute 문이 호출된 지역을 벗어나는 순간 myNewSub() 프로시저의 인스턴스도 사라지게 된다. 이런 결과를 원하지 않는다면 Execute 문 대신, ExecuteGlobal 문을 사용하면 된다. 이미 짐작하고 있는 것처럼 ExecuteGlobal 문의 실행 결과는 항상 전역의 인스턴스를 갖기 때문이다.
With 문
이미 With 문에 대해서는 한 차례 설명한 바 있으므로 지금 다시 얘기하지는 않도록 하겠다. With 문에 관한 내용은 다음의 링크를 참고하기 바란다.
간단한 내용이므로 이해에 그다지 어려움이 없을 것으로 생각한다.
GetLocale() 함수와 SetLocale() 함수
이 두 함수는 그 이름에서도 알 수 있는 것처럼 시스템의 로케일과 관련된 유용한 기능들을 제공해준다. 즉, GetLocale() 함수는 현재 시스템의 로케일 ID 값을 10진수로 리턴해주며, SetLocale() 함수는 전역 로케일을 설정하고 기존의 로케일 ID 값을 역시 10진수로 리턴해준다. 여기서 얘기하는 로케일 ID란 미리 정의된 임의의 32비트 값 또는 그에 해당하는 짧은 문자열을 의미하며, 그 전체 목록은 다음과 같다.
로케일 설명 | 짧은 문자열 | 16진수값 | 10진수값 |
---|---|---|---|
Afrikaans | af | 0x0436 | 1078 |
Albanian | sq | 0x041C | 1052 |
Arabic - United Arab Emirates | ar-ae | 0x3801 | 14337 |
Arabic - Bahrain | ar-bh | 0x3C01 | 15361 |
Arabic - Algeria | ar-dz | 0x1401 | 5121 |
Arabic - Egypt | ar-eg | 0x0C01 | 3073 |
Arabic - Iraq | ar-iq | 0x0801 | 2049 |
Arabic - Jordan | ar-jo | 0x2C01 | 11265 |
Arabic - Kuwait | ar-kw | 0x3401 | 13313 |
Arabic - Lebanon | ar-lb | 0x3001 | 12289 |
Arabic - Libya | ar-ly | 0x1001 | 4097 |
Arabic - Morocco | ar-ma | 0x1801 | 6145 |
Arabic - Oman | ar-om | 0x2001 | 8193 |
Arabic - Qatar | ar-qa | 0x4001 | 16385 |
Arabic - Saudi Arabia | ar-sa | 0x0401 | 1025 |
Arabic - Syria | ar-sy | 0x2801 | 10241 |
Arabic - Tunisia | ar-tn | 0x1C01 | 7169 |
Arabic - Yemen | ar-ye | 0x2401 | 9217 |
Armenian | hy | 0x042B | 1067 |
Azeri - Latin | az-az | 0x042C | 1068 |
Azeri - Cyrillic | az-az | 0x082C | 2092 |
Basque | eu | 0x042D | 1069 |
Belarusian | be | 0x0423 | 1059 |
Bulgarian | bg | 0x0402 | 1026 |
Catalan | ca | 0x0403 | 1027 |
Chinese - China | zh-cn | 0x0804 | 2052 |
Chinese - Hong Kong S.A.R. | zh-hk | 0x0C04 | 3076 |
Chinese - Macau S.A.R | zh-mo | 0x1404 | 5124 |
Chinese - Singapore | zh-sg | 0x1004 | 4100 |
Chinese - Taiwan | zh-tw | 0x0404 | 1028 |
Croatian | hr | 0x041A | 1050 |
Czech | cs | 0x0405 | 1029 |
Danish | da | 0x0406 | 1030 |
Dutch - The Netherlands | nl-nl | 0x0413 | 1043 |
Dutch - Belgium | nl-be | 0x0813 | 2067 |
English - Australia | en-au | 0x0C09 | 3081 |
English - Belize | en-bz | 0x2809 | 10249 |
English - Canada | en-ca | 0x1009 | 4105 |
English - Carribbean | en-cb | 0x2409 | 9225 |
English - Ireland | en-ie | 0x1809 | 6153 |
English - Jamaica | en-jm | 0x2009 | 8201 |
English - New Zealand | en-nz | 0x1409 | 5129 |
English - Phillippines | en-ph | 0x3409 | 13321 |
English - South Africa | en-za | 0x1C09 | 7177 |
English - Trinidad | en-tt | 0x2C09 | 11273 |
English - United Kingdom | en-gb | 0x0809 | 2057 |
English - United States | en-us | 0x0409 | 1033 |
Estonian | et | 0x0425 | 1061 |
Farsi | fa | 0x0429 | 1065 |
Finnish | fi | 0x040B | 1035 |
Faroese | fo | 0x0438 | 1080 |
French - France | fr-fr | 0x040C | 1036 |
French - Belgium | fr-be | 0x080C | 2060 |
French - Canada | fr-ca | 0x0C0C | 3084 |
French - Luxembourg | fr-lu | 0x140C | 5132 |
French - Switzerland | fr-ch | 0x100C | 4108 |
FYRO Macedonian | mk | 0x042F | 1071 |
Gaelic - Ireland | gd-ie | 0x083C | 2108 |
Gaelic - Scotland | gd | 0x043C | 1084 |
German - Germany | de-de | 0x0407 | 1031 |
German - Austria | de-at | 0x0C07 | 3079 |
German - Liechtenstein | de-li | 0x1407 | 5127 |
German - Luxembourg | de-lu | 0x1007 | 4103 |
German - Switzerland | de-ch | 0x0807 | 2055 |
Greek | el | 0x0408 | 1032 |
Hebrew | he | 0x040D | 1037 |
Hindi | hi | 0x0439 | 1081 |
Hungarian | hu | 0x040E | 1038 |
Icelandic | is | 0x040F | 1039 |
Indonesian | id | 0x0421 | 1057 |
Italian - Italy | it-it | 0x0410 | 1040 |
Italian - Switzerland | it-ch | 0x0810 | 2064 |
Japanese | ja | 0x0411 | 1041 |
Korean | ko | 0x0412 | 1042 |
Latvian | lv | 0x0426 | 1062 |
Lithuanian | lt | 0x0427 | 1063 |
Malay - Malaysia | ms-my | 0x043E | 1086 |
Malay - Brunei | ms-bn | 0x083E | 2110 |
Maltese | mt | 0x043A | 1082 |
Marathi | mr | 0x044E | 1102 |
Norwegian - Bokm | no-no | 0x0414 | 1044 |
Norwegian - Nynorsk | no-no | 0x0814 | 2068 |
Polish | pl | 0x0415 | 1045 |
Portuguese - Portugal | pt-pt | 0x0816 | 2070 |
Portuguese - Brazil | pt-br | 0x0416 | 1046 |
Raeto-Romance | rm | 0x0417 | 1047 |
Romanian - Romania | ro | 0x0418 | 1048 |
Romanian - Moldova | ro-mo | 0x0818 | 2072 |
Russian | ru | 0x0419 | 1049 |
Russian - Moldova | ru-mo | 0x0819 | 2073 |
Sanskrit | sa | 0x044F | 1103 |
Serbian - Cyrillic | sr-sp | 0x0C1A | 3098 |
Serbian - Latin | sr-sp | 0x081A | 2074 |
Setsuana | tn | 0x0432 | 1074 |
Slovenian | sl | 0x0424 | 1060 |
Slovak | sk | 0x041B | 1051 |
Sorbian | sb | 0x042E | 1070 |
Spanish - Spain | es-es | 0x0C0A | 1034 |
Spanish - Argentina | es-ar | 0x2C0A | 11274 |
Spanish - Bolivia | es-bo | 0x400A | 16394 |
Spanish - Chile | es-cl | 0x340A | 13322 |
Spanish - Colombia | es-co | 0x240A | 9226 |
Spanish - Costa Rica | es-cr | 0x140A | 5130 |
Spanish - Dominican Republic | es-do | 0x1C0A | 7178 |
Spanish - Ecuador | es-ec | 0x300A | 12298 |
Spanish - Guatemala | es-gt | 0x100A | 4106 |
Spanish - Honduras | es-hn | 0x480A | 18442 |
Spanish - Mexico | es-mx | 0x080A | 2058 |
Spanish - Nicaragua | es-ni | 0x4C0A | 19466 |
Spanish - Panama | es-pa | 0x180A | 6154 |
Spanish - Peru | es-pe | 0x280A | 10250 |
Spanish - Puerto Rico | es-pr | 0x500A | 20490 |
Spanish - Paraguay | es-py | 0x3C0A | 15370 |
Spanish - El Salvador | es-sv | 0x440A | 17418 |
Spanish - Uruguay | es-uy | 0x380A | 14346 |
Spanish - Venezuela | es-ve | 0x200A | 8202 |
Sutu | sx | 0x0430 | 1072 |
Swahili | sw | 0x0441 | 1089 |
Swedish - Sweden | sv-se | 0x041D | 1053 |
Swedish - Finland | sv-fi | 0x081D | 2077 |
Tamil | ta | 0x0449 | 1097 |
Tatar | tt | 0X0444 | 1092 |
Thai | th | 0x041E | 1054 |
Turkish | tr | 0x041F | 1055 |
Tsonga | ts | 0x0431 | 1073 |
Ukrainian | uk | 0x0422 | 1058 |
Urdu | ur | 0x0420 | 1056 |
Uzbek - Cyrillic | uz-uz | 0x0843 | 2115 |
Uzbek - Latin | uz-uz | 0x0443 | 1091 |
Vietnamese | vi | 0x042A | 1066 |
Xhosa | xh | 0x0434 | 1076 |
Yiddish | yi | 0x043D | 1085 |
Zulu | zu | 0x0435 | 1077 |
예를 들어 현재 시스템의 로케일 설정을 알고 싶다면 다음과 같이 코딩하면 된다.
Response.Write "<font size=-1>" & GetLocale() & "</font><br>"
필자도 그렇고, 대부분 한글 Windows를 사용하고 있을 것이므로 특별히 설정을 바꾸거나 하지 않았다면 한국(Korean)을 뜻하는 1042라는 값이 리턴될 것이다.
반대로 현재 시스템의 로케일을 영국(English - United Kingdom)으로 바꿔서 설정하고 싶다면 다음과 같이 코딩하면 된다.
orgLocID = SetLocale("en-gb")
또는...
orgLocID = SetLocale(2057)
이 코드에서 SetLocale() 함수의 리턴값을 저장하는 orgLocID 변수에는 기존의 로케일 ID 값이 10 진수로 저장된다. 따라서 잠시 로케일을 변경했다가 다시 원래의 로케일로 돌아가야 하는 상황이라면 이 리턴값을 사용하면 된다.
그러나, 사실 GetLocale() 함수와 SetLocale() 함수를 사용하지 않더라도 로케일 ID 값을 얻거나 재설정할 수 있는 방법이 전혀 없는 것은 아니다. ASP에서 제공해주는 내부 객체들 중 Session 객체를 살펴보면 LCID라는 프로퍼티가 있는데, 이 프로퍼티 역시 로케일 ID 값을 얻거나 재설정하는데 사용된다. 다음 코드는 IIS 5.0 설명서에서 발췌한 Session.LCID 예제 코드다.
<%
Session.LCID = 2057
Dim curNumb
curNumb = FormatCurrency(125)
Response.Write (curNumb)
%>
이 코드는 '£125.00'라는 문자열을 리턴해준다. 즉, 로케일 ID 값이 2057인 영국의 로케일 설정이 적용되는 것이다. 이렇게 꼭 GetLocale() 함수와 SetLocale() 함수를 사용하지 않더라도 로케일 관련 설정을 할 수 있다. 다만, GetLocale() 함수 및 SetLocale() 함수와 Session.LCID 프로퍼티는 두 가지 부분에서 큰 차이점을 가지고 있다.
첫 번째 차이점은 Session.LCID 프로퍼티는 ASP 프로그램에서만 사용할 수 있는 반면, GetLocale() 함수와 SetLocale() 함수는 VBScript를 호스팅하는 프로그램에서는 어디서든지 사용 가능하다는 점이다. 가령, 클라이언트측 VBScript 작성시에는 GetLocale() 함수와 SetLocale() 함수만 사용할 수 있다.
두 번째 차이점은 설정된 로케일의 적용 범위다. 즉, Session.LCID 프로퍼티를 사용해서 설정된 로케일은 다름 아닌 Session 객체에 포함된 프로퍼티답게 ASP의 Session 범위 전체에 걸쳐서 설정이 유지된다. 그 반면, GetLocale() 함수와 SetLocale() 함수로 설정된 로케일은 그 페이지가 끝나면 다시 시스템 기본 로케일 설정으로 돌아오는 것이다.
Timer() 함수
이전 버전의 VBScript를 사용하면서 의례 경험하게 되는 몇 가지 곤란한 상황들 중 한 가지 경우는, 바로 특정 루틴이 실행될 때 경과한 시간을 체크할 수 있는 방법이 전혀 없었다는 점이다. 기껏해야 Second() 함수를 사용해서 경과한 시간을 초 단위로 구하는 것이 고작이었다. 그러나, VBScript 5.X 버전에서부터는 Timer() 함수가 지원됨으로 해서 이런 상황이 다소나마 개선되었다.
이 Timer() 함수는 매일 오전 12시 00분 00초를 기준으로 현재까지 지나간 시간을 1/100초까지 리턴해준다. 재미있는 것은 단순히 Timer() 함수를 호출해서 리턴값을 구하는 경우에는 방금 얘기한 것처럼 1/100초 수준의 정밀도로 결과값을 리턴해주지만, Timer() 함수의 리턴값끼리 더하거나 빼는 등의 계산을 할 경우에는 정밀도가 소숫점 6~7자리까지로 늘어난다는 점이다. 다음은 임의의 For ... Next 문을 실행하는 데 경과한 시간을 구하는 코드이다.
<%
Dim sTime, eTime
Dim i, j
sTime = Timer() '** 작업 시작 시간
For i = 0 To 100000
j = j + i * 2
Next
eTime = Timer() '** 작업 종료 시간
Response.Write "<font size=-1>1. 변수 j 의 최종 결과값은 " & j & " 입니다.</font><br>"
Response.Write "<font size=-1>2. For ... Next 문이 루프를 수행하는데 걸린 총 시간은 " & _
(eTime - sTime) & " 초 입니다.</font><br>"
%>
펜티엄 4인 필자의 노트북에서는 이 코드의 루프를 수행하는데 0.109375초가 걸렸다. 물론, 이러한 값들이 어떤 절대적인 의미를 지닌다고 말하기는 힘들 것이다. 더군다나 Timer() 함수의 리턴값은 매일 초기화되는 값이므로 그 사용에 주의하도록 한다.
GetRef() 함수
엄밀히 말해서 이 GetRef() 함수는 클라이언트측 VBScript에서만 사용 가능한 함수다. 그럼에도 불구하고 이렇게 소개를 하는 것은 그 예상 밖의 강력함 때문이다. GetRef() 함수는 HTML 상의 각종 이벤트들, 예를 들면 OnLoad 이벤트나 OnClick 이벤트 등이 발생하는 경우 실행될 VBScript 프로시저를 지정할 때 그 바인딩을 위해서 사용된다. 다음은 Windows Script V5.6 온라인 설명서에서 발췌한 예제 코드이다.
<SCRIPT Language="VBScript">
Function GetRefTest()
Dim Splash
Splash = "GetRefTest 버전 1.0" & vbCrLf
Splash = Splash & Chr(169) & " YourCompany 1999 "
MsgBox Splash
End Function
Set Window.Onload = GetRef("GetRefTest")
</SCRIPT>
이 코드는 GetRefTest()라는 이름의 프로시저를 선언하고 이를 OnLoad 이벤트에 바인딩시킨다. Set 문의 사용에 유의하기 바란다. 결과적으로, OnLoad 이벤트가 발생하면 GetRefTest() 프로시저가 실행된다. GetRef() 함수는 DHTML 객체 모델에 존재하는 모든 이벤트를 대상으로 사용 가능할 뿐더러, 하나의 이벤트에 대해서 여러 번 다시 바인딩하는 것을 허용한다. 따라서 이런 특성을 JScript의 교차 언어 지원 특성과 같이 사용할 경우 <body onload="..."> 와 같은 패턴에 의한 이벤트 헨들러 바인딩과는 달리 매우 강력한 클라이언트측 프로그래밍이 가능해진다. 즉, 페이지를 새로고침 하지 않고서도 VBScript 코딩에 의해서 동일한 이벤트의 이벤트 헨들러를 한 페이지 내에서 다른 프로시저로 변경할 수 있는 것이다.
정정: GetRef() 함수를 사용해서 동적으로 이벤트 핸들러를 설정하는 방식을 설명하고 있는 이 문단에는 다소 부적절한 내용들이 포함되어 있다. 익히 알고 있는 것처럼, 이벤트 헨들러를 동적으로 설정하는 작업은 반드시 GetRef() 함수를 사용하지 않더라도, DOM에서 제공되는 attachEvent()/detachEvent() 함수나 addEventListener()/removeEventListener() 함수를 사용하면 훨씬 더 수월하게 처리할 수 있다. 이는 본문을 작성한 2000년대 초반 당시, 필자의 지식이 부족했기에 벌어진 일이다. GetRef() 함수는 함수 자체에 대한 참조를 반환해준다는 정도로만 이해하면 무리가 없을 것이다.
콜론(:) 기호
사실 콜론(:) 기호는 VBScript 5.X 버전에서 처음 소개된 기능은 아니다. 다만, 의외로 그 기능을 모르시는 분들이 많이 계셔서 여기 소개한다. 여러분은 이미 밑줄(_) 기호에 대해서 잘 알고 계실 것이다. 밑줄 기호는 한 줄에 쓰기에는 너무 긴 내용을 여러 줄로 나누어 쓸 수 있도록 해준다. ASP 프로그래밍에서는 다음 코드와 같이 주로 SQL 문을 구성할 때 사용하는 경우가 많다.
<%
...
Dim strSQL
strSQL = "SELECT Col01, Col02, Col03 " & _
"FROM Table01 " & _
"WHERE Col01 > 1000"
...
%>
반면 콜론 기호는 이와 반대로 여러 줄에 나뉜 코드를 한 줄에 쓸 수 있도록 해준다. 이런저런 설명을 하는 것 보다는 바로 코드를 살펴보는 것이 이해가 더 빠를 것이다. 다음의 코드에서 사용된 두 가지 형태의 코딩 패턴은 서로 100% 동일한 것이다.
<%
Dim i, j, k, z
'** 일반적인 형태의 코드 라인
i = 1
j = 3
k = 5
z = i + j + k
Response.Write "<font size=-1>" & z & "</font><br>"
'** 모든 코드를 한 줄에 쓴 형태의 코드 라인
i = 1: j = 3: k = 5: z = i + j + k
Response.Write "<font size=-1>" & z & "</font><br>"
%>
이런 기능은 관점에 따라서는 그다지 중요한 기능이 아닐 수도 있다. 그러나 필자처럼 코드 정리를 열심히 하는 습관을 갖고 있는 분들은 꽤나 유용하게 사용할 수 있을 것이라고 생각한다.
- ASP(Active Server Page)는 프로그래밍 언어가 아니다. 2002-03-15 00:00
- Microsoft Windows Script 5.6과 VBScript 5.6 2002-03-15 00:00
- VBScript 5.6과 정규 표현식(Regular Expression) 2002-04-14 20:15
- RegExp.Test() 메서드와 RegExp.Execute() 메서드의 활용 2002-04-29 11:24
- RegExp.Replace() 메서드의 활용 2002-05-26 22:49
- VBScript 5.6과 클래스(Class) 2002-06-19 12:31
- 접근 제한문, Initialize 이벤트와 Terminate 이벤트, 그리고 프로퍼티 프로시저 2002-08-19 16:08
- 인자를 가진 프로퍼티와 디폴트 프로퍼티(Default Property) 2002-09-11 09:12
- 그 밖의 유용한 VBScript 5.X 버전의 기능들 2002-11-03 16:19
- 기존의 ASP 프로그램을 이용한 간단한 스케줄링 잡(Scheduling Job) 설정 2003-02-21 10:31
- FileCube 버전 0.0.3 (설치 방법 및 기타 관련 정보) 2003-05-26 09:31
- FileCube 버전 0.0.4 (커스텀 레코드셋으로 구현한 정렬 기능) 2003-09-15 12:28
- 인터넷 익스플로러 스크립팅 개체와 인터페이스 2004-01-07 09:06
- 인터넷 익스플로러가 HTML을 처리하는 기본적인 방법에 대한 이해 2004-03-29 14:54
- 재미있는 자바스크립트 01, TABLE 태그와 HTCs의 활용 2005-04-25 14:00
- 재미있는 자바스크립트 02, HTCs의 작성 2005-07-13 09:18
- 재미있는 자바스크립트 03, HTCs의 작성과 ASP.NET 사용자 지정 컨트롤 연동 2006-05-25 16:49