EgoCube.URLTools 컴포넌트와 URLEncode() 메서드 분석

등록일시: 2002-03-15 이전,  수정일시: 2018-04-07 12:14
조회수: 21,403
본문은 최초 작성 이후, 약 22년 이상 지난 문서입니다. 일부 내용은 최근의 현실과 맞지 않거나 동떨어져 있을 수 있으며 문서 내용에 오류가 존재할 수도 있습니다. 또한 본문을 작성하던 당시 필자의 의견과 현재의 의견에 많은 차이가 존재할 수도 있습니다. 이 점, 참고하시기 바랍니다.

EgoCube.URLTools 컴포넌트(이하 URLTools 컴포넌트)는 URL 인코딩 기능과 URL 디코딩 기능만을 갖고 있는 매우 간단한 COM 컴포넌트로, URL 디코딩 기능을 지원하지 않는 ASP의 Server.URLEncode() 메서드를 보완하기 위한 목적으로 제작되었다.

URLTools 컴포넌트는 COM 컴포넌트이므로 Microsoft Visual Basic 6.0이나 VBScript 같이 COM 컴포넌트를 사용할 수 있는 모든 종류의 프로그래밍 언어에서 사용 가능하며, 사실 코드 자체가 매우 간단하므로 굳이 컴포넌트 형태를 고집하지 않고 .inc 파일로 제작해서 ASP에서 #include 지시어를 사용하여 활용하거나 .vbs 파일로 제작하여 HTML 상에서 VBScript로 사용하는 것도 가능하다. 이 부분에 관해서는 이 글의 후반부에 다시 설명할 것이다. 다음 링크는 URLTools 컴포넌트의 Microsoft Visual Basic 6.0 SP5의 소스 코드와 설치 파일이다.

URLTools_Src.zip (11k)
URLTools_Setup.zip (1,398k)

직접 테스트해 본 결과로는 일반적인 환경하에서 만족스럽게 잘 실행된다. 즉, URL 인코딩 테스트의 경우 한글, 영어, 한문, 일어, 숫자, 그리고 기타 특수 문자들에 대한 URL 인코딩 테스트에서 정확한 인코딩 결과를 보여주었으며 한글 Windows 2000 Server의 IIS 5.0에서 실행되는 ASP의 Server.URLEncode() 메서드와 100% 동일한 결과를 리턴했다. 그리고, 물론 URL 디코딩 테스트에서도 인코딩된 문자열들을 100% 원래의 문자열로 환원하는 결과를 보여주었다.

단, URLTools 컴포넌트를 제작하면서 오직 한글 Windows 제품군만을 그 대상으로 했고, 더하여 URL 인코딩과 URL 디코딩의 대상도 오로지 한글 코드 페이지 949만으로 제한했기 때문에, 한글 Windows 제품군이 아닌 다른 언어의 Windows 제품군에서의 실행 결과는 보장하지 않는다. 또한, 코드 페이지 949에서 제공되는 문자 매핑인 한글, 영어, 한자, 일어, 숫자 및 기타 특수 문자 외의 다른 언어, 즉 예를 들어 러시아어나 프랑스어와 같은 언어를 URL 인코딩 또는 URL 디코딩했을 경우에도 실행 결과를 보장하지 않는다.

URLTools 컴포넌트에는 단지 두 가지 메서드, URLEncode() 메서드와 URLDecode() 메서드만 존재한다. 그리고, ASP에서의 일반적인 사용법은 다음 코드와 같은데 그 사용법이 매우 간단하므로 주석 이외의 별도의 설명은 생략하기로 한다. 한 가지 주의할 점은 URLEncode() 메서드나 URLDecode() 메서드에 인자로 변수를 넘겨줄 때는 반드시 CStr() 함수을 사용해서 문자열로 변환해서 넘겨줘야 한다는 것이다.

<%
        
  Dim objURLTool
  Dim strHangul
  Dim strResult
        
  strHangul = "인터넷"
        
        
  '** URLTools 컴포넌트의 인스턴스 생성
  Set objURLTool = Server.CreateObject("EgoCube.URLTools")
        
  '** URL 인코딩
  strResult = objURLTool.URLEncode(CStr(strHangul))
  Response.Write "URLEncode 결과 : " & strResult & "<BR>"
        
  '** URL 디코딩
  strHangul = objURLTool.URLDecode(CStr(strResult))
  Response.Write "URLDecode 결과 : " & strHangul & "<BR>"
        
  '** URLTools 컴포넌트의 인스턴스 제거
  Set objURLTool = Nothing
        
%>

다음 비교표는 URLTools 컴포넌트의 URL 인코딩 결과를 다른 여러가지 방법을 이용해서 얻은 URL 인코딩의 결과와 비교한 것이다. 참고로 이 비교표에서 각 언어별 Yahoo!의 URL 인코딩 결과는 테스트 대상 문자열을 각각의 Yahoo! 사이트에서 검색해서 출력되는 결과 페이지의 URL 을 분석하여 얻은 것이다.

그리고, URLTools 컴포넌트와 Server.URLEncode() 메서드의 URL 인코딩 결과는 모두 한글 Windows 2000 Server의 IIS에서, 즉 코드 페이지 949상에서 실행하여 얻은 결과이다. 따라서, Session.CodePage의 값을 다른 값으로 설정하고 테스트하는 경우 아래의 표와는 완전히 상이한 결과가 나온다.

한글 URL 인코딩 테스트 :: 테스트 대상 문자열 → 인터넷

Yahoo! Korea : %c0%ce%c5%cd%b3%dd
Yahoo! Japan : %c0%ce%c5%cd%b3%dd
Yahoo! (미국) : %c0%ce%c5%cd%b3%dd
Yahoo! China (GB2312) : %26%2351064%3b%26%2353552%3b%26%2345367%3b
Yahoo! China (Big5) : %26%2351064%3b%26%2353552%3b%26%2345367%3b
URLTools Component : %c0%ce%c5%cd%b3%dd
Server.URLEncode : %c0%ce%c5%cd%b3%dd

한자 URL 인코딩 테스트 :: 테스트 대상 문자열 → 中國語

Yahoo! Korea : %f1%e9%cf%d0%e5%de
Yahoo! Japan : %c3%e6%d4%a2%b8%ec
Yahoo! (미국) : %f1%e9%cf%d0%e5%de
Yahoo! China (GB2312) : %d6%d0%87%f8%d5Z
Yahoo! China (Big5) : %A4%A4%B0%EA%BBy
URLTools Component : %f1%e9%cf%d0%e5%de
Server.URLEncode : %f1%e9%cf%d0%e5%de

일어 URL 인코딩 테스트 :: 테스트 대상 문자열 → モバイル

Yahoo! Korea : %ab%e2%ab%d0%ab%a4%ab%eb
Yahoo! Japan : %a5%e2%a5%d0%a5%a4%a5%eb
Yahoo! (미국) : %ab%e2%ab%d0%ab%a4%ab%eb
Yahoo! China (GB2312) : %a5%e2%a5%d0%a5%a4%a5%eb
Yahoo! China (Big5) : %26%2312514%3b%26%2312496%3b%26%2312452%3b%26%2312523%3b
URLTools Component : %ab%e2%ab%d0%ab%a4%ab%eb
Server.URLEncode : %ab%e2%ab%d0%ab%a4%ab%eb

비교표에서 알 수 있는 것처럼 URLTools 컴포넌트의 URL 인코딩 결과는 Server.URLEncode() 메서드, Yahoo! Korea, Yahoo! (미국)의 URL 인코딩 결과와 100% 일치하는 만족스러운 결과를 보여준다. 이제 URLEncode() 메서드의 실제 코드 내용을 살펴보자. URLEncode() 메서드의 전체 코드는 250 줄도 채 안되므로 아래에 대부분의 코드를 옮긴다. 역시 매우 간단한 내용이므로 대부분의 설명을 주석으로 대체하기로 하고 중요한 몇몇 부분만을 설명하도록 하겠다.

다음은 URLEncode() 메서드의 전체 코드다. 오류 처리에 관한 코드의 양이 많아서 분량이 많은 것처럼 느껴지지만 실제로 URL 인코딩 그 자체에 필요한 부분은 분량이 얼마되지 않는다.

Public Function URLEncode(URLStr As String) As String

    Dim sURL                As String       '** 입력받은 URL 문자열
    Dim sBuffer             As String       '** URL 인코딩 처리 중 URL 을 담을 버퍼 문자열
    Dim sTemp               As String       '** 임시 문자열
    Dim cChar               As String       '** URL 문자열 중 현재 인텍스의 문자
    
    Dim Index               As Integer
    
    
    Dim lErrNum             As Long         '** 오류 번호
    Dim sErrSource          As String       '** 오류 소스
    Dim sErrDesc            As String       '** 소류 설명
    
    Dim sMsg                As String       '** 오류 메세지
    
    
On Error GoTo ErrorHanddle:


    sURL = Trim(URLStr)     '** URL 문자열을 얻는다.
    sBuffer = ""            '** 임시 버퍼용 문자열 변수 초기화.

    
    '******************************************************
    '* URL 인코딩 작업
    '******************************************************
    
    For Index = 1 To Len(sURL)
    
        '** 현재 인덱스의 문자를 얻는다.
        cChar = Mid(sURL, Index, 1)

        If cChar = "0" Or _
           (cChar >= "1" And cChar <= "9") Or _
           (cChar >= "a" And cChar <= "z") Or _
           (cChar >= "A" And cChar <= "Z") Or _
           cChar = "-" Or _
           cChar = "_" Or _
           cChar = "." Or _
           cChar = "*" Then

            '** URL 에 허용되는 문자들 :: 버퍼 문자열에 추가한다.
            sBuffer = sBuffer & cChar
            
        ElseIf cChar = " " Then
        
            '** 공백 문자 :: + 로 대체하여 버퍼 문자열에 추가한다.
            sBuffer = sBuffer & "+"
            
        Else
        
            '** URL 에 허용되지 않는 문자들 :: % 로 인코딩해서 버퍼 문자열에 추가한다.
            sTemp = CStr(Hex(Asc(cChar)))
            
            If Len(sTemp) = 4 Then

                sBuffer = sBuffer & "%" & Left(sTemp, 2) & "%" & Mid(sTemp, 3, 2)

            ElseIf Len(sTemp) = 2 Then

                sBuffer = sBuffer & "%" & sTemp

            End If
                
        End If

    Next


    '** 결과를 리턴한다.
    URLEncode = sBuffer
    
    Exit Function
    

ErrorHanddle:

    '** 오류가 발생하면 공백 문자를 리턴한다.
    URLEncode = ""
    
    '** 오류 정보를 얻는다.
    lErrNum = Err.Number
    sErrSource = Err.Source
    sErrDesc = Err.Description
    
    '** 이벤트 로그에 오류를 기록한다.
    sMsg = vbCrLf & vbCrLf & _
           "Error Object : EgoCube.URLTools," & vbCrLf & _
           "Error Method : Public Function URLEncode(URLStr As String) As String," & vbCrLf & _
           "Error Number : " & lErrNum & "," & vbCrLf & _
           "Error Source : " & sErrSource & "," & vbCrLf & _
           "Error Description : " & sErrDesc
    App.LogEvent sMsg, vbLogEventTypeError
    
    '** 오류를 발생시킨다.
    Err.Raise lErrNum, sErrSource, sErrDesc

    
    Exit Function
    
    
End Function

이 코드에서 중요한 처리를 담당하는 부분은 모두 For ... Next 문 내부에 들어 있다. 그중에서도 제일 중요한 부분은 붉은색으로 강조된 부분으로, 그 외의 나머지 코드들은 변수를 정의하거나 오류를 처리하는 등 상대적으로 덜 중요한 부분들이다. URLEncode() 메서드는 인자로 받은 문자열을 sURL이라는 변수에 받아서 가장 첫 문자에서부터 마지막 문자까지 For ... Next 문을 돌면서 한 문자씩 URL 인코딩 처리를 하는데 이 때 모두 세 가지 경우가 있을 수 있다.

첫 번째 경우는 해당 문자가 URL 인코딩 처리를 할 필요가 없는 문자인 경우이다. 즉, a~z, A~Z, 0~10, -, _, ., 그리고 * 같은 문자들은 URL 인코딩 처리를 할 필요가 없다. 따라서, 해당 문자를 그대로 임시 버퍼용 변수 sBuffer에 추가한다. 두 번째 경우는 해당 문자가 빈칸, 즉 공백 문자(Space)인 경우다. 공백 문자는 URL 인코딩 과정 중 약간 특수하게 취급되는데 '+'로 바뀐 후 역시 변수 sBuffer에 추가된다.

마지막 세 번째 경우는 해당 문자가 앞의 두 가지 경우에 해당하지 않는 모든 문자인 경우다. 한글, 한자, 일어, 특수문자 등이 바로 이 경우에 속하며 이 세 번째 경우를 처리하기 위한 코드가 위의 코드에서 붉은색으로 강조된 부분이다. 이 부분의 코드를 명확하게 이해하기 위해서는 먼저 PC상에서의 한글 및 다국어 처리 방법을 이해하고 있어야만 한다.

본래 PC가 처음 등장했을 당시에는 영어권 언어 이외의 다른 언어, 예를 들어 한글, 일어 또는 한자 등과 같은 아시아 지역의 언어나 중동, 서아시아 지역 등의 언어에 대한 처리는 전혀 고려되지 않았다. 그것도 그럴 것이 어짜피 PC라는 기계 자체가 처음 만들어진 곳이 미국이었고 따라서 PC의 문자 입출력 체계도 자연스럽게 영어권 언어만 고려하여 만들어진 것일 터이므로 IBM이나 마이크로소프트를 탓할 만한 일은 아닌것 같다. 또 당시에는 그 누구도 PC가 지금과 같이 전세계적으로 널리 활용되리라는 것을 예상하지 못했을테니 말이다.

아무튼 이렇게 비영어권 언어들에 대한 배려가 전무한 상태로 ANSI(American National Standards Institute)에서 표준으로 제정한 코드 집합이 바로 그 유명한 ASCII 코드다. 사실 ANSI의 본래 뜻이 미국 표준 협회라는 점을 생각해 본다면 비영어권 언어들에 대한 무관심은 어찌보면 그 당시로선 당연한 것이라고 생각할 수도 있을 것이다. 이 ASCII 코드 테이블의 전체 구성 내역은 http://www.asciitable.com/ 같은 사이트에서 쉽게 찾아볼 수 있다. ASCII 코드에는 영문 대/소문자, 숫자, CR, LF 등의 각종 제어문자, 움라우트와 같은 특수한 발음기호, 선문자 등의 코드에 각각 0부터 255사이의 값이 지정되어 있는데, 일반적으로 0부터 127까지를 ASCII 코드, 128부터 255까지를 확장 ASCII 코드(Extended ASCII Code)라고 부른다.

그런데, 문제는 위에서도 얘기한 바와 같이 이 ASCII 코드에는 비영어권 언어들에 고려가 전혀 없는 상태이므로 ASCII 코드만 가지고는 한글, 한자, 일어 등과 같은 비영어권 언어의 처리가 불가능 하다는 것이다. 그래서, 이 문제를 해결하기 위해서 제시된 방법이 128부터 255까지의 확장 ASCII 코드(Extended ASCII Code)를 두 개씩 조합해서 사용하는 것이다.

즉, PC를 사용할 때 영문과 숫자, 제어코드 및 몇몇 기호는 필수적으로 사용할 수 밖에 없으므로 이런 문자들이 할당된 ASCII 코드 테이블의 전반부 128개의 코드, 0부터 127번까지는 순수한 ASCII 코드의 값을 그대로 사용하고 비교적 사용 빈도가 낮은 ASCII 코드 테이블의 후반부 128개의 코드, 128부터 255까지 즉 확장 ASCII 코드를 두 개씩 조합해서 각각의 코드 조합에 한 개의 문자를 할당하여 사용하는 것이다. 이런 방법을 사용하면 단순 계산으로만 생각해봐도 후반부 확장 ASCII 코드의 128개의 코드마다 각각 128가지 조합이 가능하므로 하나의 코드 페이지마다 128 * 128 = 16,384개의 문자를 표현할 수 있게 되는 것이다. 이런 방식의 코드 페이지를 DBCS(Double-Byte Character Set) 코드 페이지 또는 MBCS(Multi-Byte Character Set) 코드 페이지라고 부른다.

다음 URL은 위에서 설명한 방식으로 한글의 문자값이 할당된 한글 코드 페이지 949의 전체 테이블 내역을 살펴볼 수 있는 마이크로소프트에서 제공하는 Windows Codepage 949 페이지이다.

이 테이블을 자세히 살펴보면 이미 얘기한 것처럼 0부터 127까지는 일반적인 ASCII 코드와 동일하다는 사실을 알 수 있다. 그러나, 확장 ASCII 코드 영역의 경우에는 상자 안쪽에 해당 코드에 해당하는 문자값이 나타나 있는 것이 아니라 클릭할 수 있도록 링크가 걸려있고, 이 링크를 클릭해서 이동한 페이지에서 실제로 해당 코드에 할당된 문자값들을 볼 수 있도록 되어 있는데, 비단 한글 뿐만이 아니라 일어, 한자, 로마자, 특수 기호 등이 준비되어 있다는 것을 확인할 수 있다.

그렇다면 실제로 이 테이블을 이용하여 임의의 한글 문자 하나를 읽어보자. 위의 URL 인코딩 테스트 비교표에서 '인터넷'이라는 한글 URL 인코딩 테스트 대상 문자열의 인코딩 결과는 '%c0%ce%c5%cd%b3%dd'이었다. 이 결과에서 % 기호를 제거하면 'c0cec5cdb3dd'가 되는데 이는 각각 c0ce → '인', c5cd → '터', b3dd → '넷'의 의미를 지닌다. 그 중, '인'자를 살펴보면 c0로 시작하므로 코드 페이지 949 표에서 c0를 찾아서 클릭한다. 그리고, 그 다음 16진수는 ce이므로, c0를 클릭해서 이동한 페이지에서 ce에 해당하는 문자값을 표에서 찾아 읽어보면 '인'이라는 문자인 것을 확인할 수 있다. 참고로, '인'자 바로 밑에 씌여져 있는 값 'C778'은 '인'자에 해당하는 UNICODE 값을 나타낸다.

이제 다시 URLEncode() 메서드의 코드로 관심을 돌려보자. Microsoft Visual Basic 6.0에서 이런 한글 또는 한자 등과 같은 확장 ASCII 코드값을 읽으려면 어떻게 해야할까? 그 답은 의외로 간단한데 일반적인 ASCII 코드의 값을 얻을 때 사용하는 함수인 Asc() 함수를 그대로 사용하면 된다. 따라서, sTemp = CStr(Hex(Asc(cChar)))와 같이 먼저 Asc() 함수를 사용하여 현재 문자의 ASCII 코드값을 얻고 Hex() 함수를 사용해서 이 값을 16 진수로 바꾼 후, CStr() 함수를 사용하여 문자열 형식으로 변환한다. 이렇게 얻은 최종 변환된 16진수 값의 문자열 길이가 2면 ASCII 코드상의 일반 특수 기호인 셈이고 길이가 4라면 이는 확장 ASCII 코드상의 문자이므로 두 경우 모두 적절히 '%' 기호를 추가하여 역시 임시 버퍼용 변수 sBuffer에 추가하면 된다.

이 방법을 사용해서 실제로 '인'자를 URL 인코딩해 나가는 경우를 생각해보자. 위에서 이미 얘기한 것처럼 먼저 Asc() 함수를 사용해서 Integer 형의 ASCII 코드값을 얻는다. '인' 자의 경우, -16,178이라는 값이 리턴되는데 이 예상하지 못한 음수값은 리턴된 값이 (Signed) Integer 형의 범위를 초과했기 때문에 발생하는 것이다. 그러나, 메모리 내부의 실제 비트값들은 올바르게 설정되어져 있으므로 보기에 조금 이상한 것은 무시하기로 한다. 다시 이 값을 Hex() 함수를 사용해서 16진수로 변환하면 그 결과값으로 'C0CE'가 리턴된다. 그렇다. 이제 서서히 우리가 기대하는 값이 나오기 시작한다. 나머지 과정은 간단하다. CStr() 함수로 문자열로 바꾸고, 적당한 문자열 관련 함수들을 사용해서 '%' 기호를 적당한 위치에 넣어주면 그 결과는 '%C0%CE'가 되고 이로써 모든 과정이 끝난다.

이런 과정을 URL 인코딩 대상 문자열의 모든 문자에 대해서 처리하고 그 처리 결과를 저장하고 있던 임시 버퍼용 변수 sBuffer를 리턴해주면 모든 과정이 끝나게 된다. 이것이 URLEncode() 메서드의 전부다.