웹 요청 URL에 한글 검색어나 &, =, 공백 같은 특수문자를 넣으려면 퍼센트 인코딩을 해야 합니다. VBA에는 이걸 해 주는 내장 함수가 없어서 바이트 단위로 직접 변환 함수를 짜는 경우가 많은데, UTF-8 변환까지 신경 쓰려면 생각보다 코드가 길어집니다. 더 짧은 방법은 Windows에 들어 있는 HTML 문서 객체(htmlfile, MSHTML)를 하나 만들어, 그 안의 자바스크립트 엔진에게 encodeURIComponent 를 대신 시키는 것입니다.
코드
Function encode(str)
Set htmlfile = CreateObject("htmlfile")
htmlfile.parentWindow.execScript "function encode(s) {return encodeURIComponent(s)}", "jscript"
encode = htmlfile.parentWindow.encode(str)
End Function
Function decode(str)
Set htmlfile = CreateObject("htmlfile")
htmlfile.parentWindow.execScript "function decode(s) {return decodeURIComponent(s)}", "jscript"
decode = htmlfile.parentWindow.decode(str)
End Function
execScript 로 문서의 창(window)에 encode 라는 자바스크립트 함수를 정의해 두면, 그 함수는 parentWindow 의 메서드처럼 VBA에서 바로 부를 수 있습니다. 자바스크립트의 encodeURIComponent 는 문자열을 UTF-8 기준으로 인코딩하기 때문에 encode("허벌 라이프") 는 %ED%97%88%EB%B2%8C%20%EB%9D%BC%EC%9D%B4%ED%94%84 처럼 웹 서버가 기대하는 형식으로 나옵니다. decode 는 그 반대입니다.
encodeURIComponent를 고른 이유
자바스크립트에는 비슷한 encodeURI 도 있는데, 이쪽은 URL 전체를 넣는 용도라 &, =, ?, / 를 인코딩하지 않습니다. 검색어 같은 파라미터 값에 & 가 들어 있으면 그 뒤가 다른 파라미터로 잘려 버리죠. 그래서 값 하나하나를 인코딩할 때는 encodeURIComponent 가 맞고, 완성된 URL 전체를 다시 감싸면 :// 까지 인코딩돼서 안 됩니다.
쓸 때 걸리는 부분
Option Explicit 를 쓰는 모듈에서는 htmlfile 변수를 선언하지 않아 컴파일 오류가 납니다. Dim htmlfile As Object 를 넣어 주세요. 그리고 함수를 부를 때마다 문서 객체를 새로 만들고 스크립트를 등록하므로, 수천 건을 반복 인코딩하면 눈에 띄게 느립니다. 그럴 땐 객체를 한 번만 만들어 재사용하는 형태로 바꿔 쓰면 됩니다.
Private mHtml As Object
Function encodeFast(str)
If mHtml Is Nothing Then
Set mHtml = CreateObject("htmlfile")
mHtml.parentWindow.execScript "function encode(s) {return encodeURIComponent(s)}", "jscript"
End If
encodeFast = mHtml.parentWindow.encode(str)
End Function
decodeURIComponent 는 %E 처럼 형식이 깨진 값을 받으면 자바스크립트 오류를 던지고, VBA에서는 런타임 오류로 나타납니다. 외부에서 받은 값을 디코딩할 때는 On Error 로 감싸 두는 게 좋습니다. 또 폼 전송 형식에서 공백을 뜻하는 + 는 이 함수가 공백으로 바꿔 주지 않으니, 필요하면 디코딩 전에 Replace(str, "+", " ") 를 먼저 하세요.
엑셀 2013 이상만 쓴다면 인코딩은 WorksheetFunction.EncodeURL 로도 됩니다. 다만 디코딩 함수는 없어서, 둘 다 필요하거나 Access·VB6처럼 워크시트 함수가 없는 환경이라면 이 방법이 편합니다. 64비트 Office에서 ScriptControl 을 쓸 수 없을 때 대안으로도 많이 쓰입니다.
검색 주소를 만들 때는 이렇게 씁니다
인코딩은 주소 전체가 아니라 값 부분에만 겁니다. 검색어에 공백과 & 가 섞여 있어도 한 덩어리 값으로 전달됩니다.
Sub Test()
Dim url As String
url = "https://search.naver.com/search.naver?query=" & encode("엑셀 VBA & 매크로")
Debug.Print url
End Sub
직접 실행 창에는 query= 뒤로 %EC%97%91... 처럼 퍼센트 기호가 이어진 값이 찍히고, 공백은 %20, & 는 %26 으로 바뀌어 있습니다. 파라미터가 여러 개라면 값마다 따로 encode 를 거쳐서 & 로 이어 붙이면 됩니다.
UTF-8 이 아닌 사이트에서는?
encodeURIComponent 는 항상 UTF-8 로 인코딩합니다. 오래된 국내 사이트 중에는 검색어를 EUC-KR(CP949) 바이트로 인코딩해서 받는 곳이 있는데, 그런 곳에 이 결과를 보내면 검색어가 깨져 엉뚱한 결과가 나옵니다. 주소창에 한글로 검색했을 때 바뀐 값이 %C7%D1 처럼 한 글자당 두 묶음이면 EUC-KR, %ED%95%9C 처럼 세 묶음이면 UTF-8 입니다. EUC-KR 사이트라면 StrConv(str, vbFromUnicode) 로 바이트를 얻어 한 바이트씩 % 와 16진수로 바꾸는 함수를 따로 써야 합니다.
같은 이유로, EUC-KR 로 인코딩된 값을 decode 에 넣으면 올바른 UTF-8 바이트열이 아니라서 자바스크립트가 오류를 던집니다. 위에서 말한 On Error 처리가 이 경우에도 필요합니다. 자바스크립트의 옛 함수인 escape 는 한글을 %uD55C 같은 형식으로 바꾸는데, 이 형식은 일반적인 웹 서버가 기대하는 퍼센트 인코딩이 아니니 쓰지 않는 게 좋습니다.