매크로나 수집 프로그램을 돌리다 보면 지금 이 PC가 어떤 공인 IP로 나가고 있는지 확인해야 할 때가 있습니다. 공유기 뒤에 있으면 ipconfig로는 내부 IP(192.168.x.x)만 보이니까, 외부 서버에 물어봐야 진짜 공인 IP를 알 수 있습니다. 네이버 지도 웹이 내부적으로 호출하는 IP 확인 주소를 이용하면 응답이 짧아서 VBA에서 바로 쓰기 편합니다.
Function GetIP() As String
Dim URL As String, Cookie As String
Dim T As String
URL = "https://map.naver.com/v5/api/ip"
With CreateObject("WinHttp.WinHttpRequest.5.1")
.Open "GET", URL
.SetRequestHeader "Host", "map.naver.com"
.SetRequestHeader "User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0"
.SetRequestHeader "Accept", "application/json, text/plain, */*"
.SetRequestHeader "Accept-Language", "ko-KR,ko;q=0.8,en-US;q=0.5,en;q=0.3"
.SetRequestHeader "Referer", "https://map.naver.com/"
.SetRequestHeader "Connection", "keep-alive"
If Len(Cookie) Then .SetRequestHeader "Cookie", Cookie
.SetRequestHeader "TE", "Trailers"
.Send
.WaitForResponse: DoEvents
T = .ResponseText
'T = StrConv(.ResponseBody, vbUnicode)
End With
GetIP = Split(T, ",")(0)
End Function
코드 설명
WinHttp.WinHttpRequest.5.1은 Windows에 기본으로 들어 있는 HTTP 컴포넌트라 참조 설정 없이 CreateObject로 바로 씁니다. 헤더는 브라우저에서 지도 페이지가 보내는 요청과 비슷하게 맞춰 둔 것입니다. Cookie 변수는 선언만 하고 비워 두었는데, 쿠키가 필요한 다른 요청으로 이 틀을 재사용할 때 값을 채워 넣으면 그 줄이 동작하도록 남겨 둔 자리입니다.
응답 텍스트를 쉼표로 나눠 첫 번째 조각을 IP로 돌려줍니다. 응답 앞부분에 IP가 오는 형식을 전제로 한 처리라서, 혹시 결과에 따옴표나 괄호가 붙어 나오면 Replace로 걷어 내면 됩니다. 주석 처리된 StrConv(.ResponseBody, vbUnicode) 줄은 한글이 섞인 응답이 깨질 때 바꿔 쓰는 대안인데, IP는 숫자와 점뿐이라 여기서는 필요 없습니다.
Sub Test()
Debug.Print GetIP()
End Sub
직접 실행 창에 현재 공인 IP가 찍히면 성공입니다.
주의할 점
이 주소는 네이버가 공개한 API가 아니라 지도 웹이 내부적으로 쓰는 주소입니다. 언제든 경로나 응답 형식이 바뀔 수 있으니, 업무용 파일에 넣을 때는 결과가 IP 모양(숫자.숫자.숫자.숫자)인지 확인하는 검사를 하나 붙여 두는 게 좋습니다. 그리고 짧은 간격으로 계속 부르는 용도는 피하세요. 프로그램 시작할 때 한 번, 또는 VPN이나 프록시를 바꾼 직후 한 번 확인하는 정도가 적당합니다.
VB.NET에서도 같은 방식으로 CreateObject("WinHttp.WinHttpRequest.5.1")을 그대로 쓸 수 있고, 새로 짠다면 HttpClient.GetStringAsync로 같은 주소를 부른 뒤 결과를 쉼표로 나누면 됩니다.
결과가 IP 모양인지 확인하기
위에서 말한 검사는 정규식 한 번이면 됩니다. 숫자 1~3자리가 점으로 네 번 이어지는지만 보는 간단한 형태라 300 같은 값까지 걸러 내지는 못하지만, 응답 형식이 바뀌어 HTML이나 오류 메시지가 돌아오는 경우는 확실히 잡아 줍니다.
Function IsIPv4(ByVal s As String) As Boolean
With CreateObject("VBScript.RegExp")
.Pattern = "^\d{1,3}(\.\d{1,3}){3}$"
IsIPv4 = .Test(Trim(s))
End With
End Function
If IsIPv4(GetIP()) Then 처럼 감싸서 쓰고, 실패하면 빈 값으로 처리하거나 다른 확인 주소로 넘어가게 하면 됩니다. IP만 평문으로 돌려주는 공개 서비스(예: api.ipify.org)도 있으니 예비 주소로 하나 정해 두면 한쪽이 막혀도 버틸 수 있습니다.
네트워크가 끊겼을 때
인터넷이 끊겨 있거나 회사 방화벽이 막고 있으면 .Send 에서 런타임 오류가 나고 매크로가 멈춥니다. 함수 안에 On Error 처리를 넣어 빈 문자열을 돌려주게 해 두는 게 좋고, 응답이 늦을 때 무작정 기다리지 않도록 .Open 앞에 .SetTimeouts 5000, 5000, 5000, 5000 처럼 시간 제한(밀리초)을 걸어 둘 수 있습니다. 네 값은 순서대로 이름 확인, 연결, 전송, 수신 제한입니다.
.Open 의 세 번째 인자를 생략하면 동기 방식이라 .Send 가 응답을 다 받을 때까지 기다립니다. 그래서 이 코드에서는 .WaitForResponse 가 없어도 결과가 같습니다. CreateObject 로 만드는 지연 바인딩이라 32비트·64비트 오피스 어느 쪽에서도 따로 고칠 부분은 없습니다.