1. 내부 IP & 외부 IP 

  • socket + getsockname() → 내부 IP 확인
  • urllib 또는 http.client + api.ipify.org → 외부 IP 확인
### =====  내부 IP 확인 =====
import socket          # 네트워크 소켓 통신을 위한 표준 라이브러리
import urllib.request  # HTTP 요청을 보낼 수 있는 표준 라이브러리
import json            # JSON 형식 데이터를 파싱하기 위한 라이브러리
import ssl             # SSL 인증서 처리 라이브러리

in_addr = socket.socket(socket.AF_INET, socket.SOCK_STREAM)   # TCP 소켓 객체 생성 (IPv4, TCP 방식)
in_addr.connect(("www.google.com", 443))                      # 구글 서버(443: HTTPS 포트)에 연결 → 내 PC의 네트워크 인터페이스가 사용됨
print("내부 IP : ", in_addr.getsockname()[0])                 # 소켓이 사용한 내 PC의 IP 주소를 가져와 출력


### ===== 외부 IP - 표준 라이브러리(1) urllib 활용 =====
url = "https://api.ipify.org?format=json"                     # 내 외부 IP를 반환해주는 공개 API URL

# SSL 인증서 검증을 비활성화 (테스트용; 보안적으로는 권장되지 않음)
ssl._create_default_https_context = ssl._create_unverified_context

with urllib.request.urlopen(url) as response:                 # 지정한 URL로 HTTPS 요청 전송
    data = response.read().decode("utf-8")                    # 응답(body)을 읽고 UTF-8 문자열로 디코딩
    ip = json.loads(data)["ip"]                               # JSON 문자열을 파싱 후, "ip" 키 값만 추출

print("외부 IP 주소: ", ip)                                   # 외부 IP 출력


### ===== 외부 IP - 표준 라이브러리(2) http.client 활용 =====
import http.client                                            # HTTP 요청을 저수준으로 다룰 수 있는 표준 라이브러리
import json                                                   # JSON 파싱 라이브러리 (다시 import해도 무방)
import ssl                                                    # SSL 인증서 관련 라이브러리 (다시 import해도 무방)

# SSL 인증서 검증을 비활성화한 context 생성 (로컬 테스트용)
context = ssl._create_unverified_context()

conn = http.client.HTTPSConnection("api.ipify.org", context = context)  # api.ipify.org에 HTTPS 연결 생성
conn.request("GET", "/?format=json")                                    # GET 요청 보내기

res = conn.getresponse()                                                # 서버로부터 응답 받기
data = res.read().decode("utf-8")                                       # 응답 내용을 읽어서 UTF-8 문자열로 변환
ip = json.loads(data)["ip"]                                             # JSON 파싱 후, "ip" 값 추출

print("외부 IP 주소: ", ip)                                             # 외부 IP 출력

 

2. 함수로 정리

  • get_internal_ip(), get_external_ip_urllib(), get_external_ip_httpclient() 형태로 호출할 수 있게 정리
 
import socket             # 네트워크 소켓(통신) 기능 제공
import urllib.request     # 고수준 HTTP(S) 요청을 보낼 수 있는 표준 라이브러리
import http.client        # 저수준 HTTP(S) 요청을 다루는 표준 라이브러리
import json               # JSON 문자열 ↔ 파이썬 객체 변환
import ssl                # SSL/TLS 인증서 검증 및 컨텍스트 설정


def get_internal_ip(target_host="www.google.com", port=443, timeout=5):
    """
    내부(사설) IP를 추출합니다.
    원리: 외부 서버에 소켓으로 연결하면, OS가 사용할 네트워크 인터페이스를 선택하는데,
          그때 소켓의 로컬 주소에 내부 IP가 담깁니다.
    """
    # 외부 서버(기본: 구글 443)로 TCP 연결을 생성 (타임아웃 설정)
    with socket.create_connection((target_host, port), timeout=timeout) as s:
        # 연결된 소켓의 로컬 주소(내 컴퓨터 측 주소)를 얻고, 그중 IP만 추출
        return s.getsockname()[0]


def get_external_ip_urllib(verify_ssl=True, timeout=5):
    """
    외부(공인) IP를 urllib로 조회합니다.
    기본은 SSL 인증서 검증을 수행(verify_ssl=True). 검증 문제 시 False로 끌 수 있습니다(테스트용).
    """
    # 외부 IP를 JSON으로 반환하는 공개 API URL

    # SSL 컨텍스트 생성: 검증 모드/비검증 모드 선택
    context = ssl.create_default_context() if verify_ssl else ssl._create_unverified_context()

    # 지정 URL로 HTTPS 요청을 보냄 (컨텍스트/타임아웃 지정)
    with urllib.request.urlopen(url, context=context, timeout=timeout) as resp:
        # 응답 본문 바이트를 읽고 UTF-8로 디코딩
        body = resp.read().decode("utf-8")
        # JSON 문자열을 파싱하고 "ip" 필드만 추출
        return json.loads(body)["ip"]


def get_external_ip_httpclient(verify_ssl=True, timeout=5):
    """
    외부(공인) IP를 http.client로 조회합니다.
    기본은 SSL 인증서 검증을 수행(verify_ssl=True). 검증 문제 시 False로 끌 수 있습니다(테스트용).
    """
    # SSL 컨텍스트 생성: 검증 모드/비검증 모드 선택
    context = ssl.create_default_context() if verify_ssl else ssl._create_unverified_context()

    # HTTPS 연결 객체를 생성 (호스트: api.ipify.org, SSL 컨텍스트/타임아웃 적용)
    conn = http.client.HTTPSConnection("api.ipify.org", context=context, timeout=timeout)
    try:
        # GET 요청 전송 (경로에 ?format=json 쿼리로 JSON을 요청)
        conn.request("GET", "/?format=json")
        # 서버 응답 객체 수신
        res = conn.getresponse()
        # 응답 바디를 읽고 UTF-8로 디코딩
        body = res.read().decode("utf-8")
        # JSON 파싱 후 "ip" 필드만 반환
        return json.loads(body)["ip"]
    finally:
        # 네트워크 리소스 정리를 위해 연결 닫기
        conn.close()


# ------ 사용 예시 (직접 실행 시) ------
if __name__ == "__main__":
    # 내부 IP 출력 (연결 가능한 외부 호스트를 통해 로컬 인터페이스 확인)
    print("내부 IP :", get_internal_ip())

    # 외부 IP 출력 - urllib (인증서 검증 ON: 운영 권장)
    try:
        print("외부 IP (urllib, verify=ON):", get_external_ip_urllib(verify_ssl=True))
    except Exception as e:
        print("urllib 검증 ON 실패 → 테스트용 비검증으로 재시도:", e)
        print("외부 IP (urllib, verify=OFF):", get_external_ip_urllib(verify_ssl=False))

    # 외부 IP 출력 - http.client (인증서 검증 ON: 운영 권장)
    try:
        print("외부 IP (http.client, verify=ON):", get_external_ip_httpclient(verify_ssl=True))
    except Exception as e:
        print("http.client 검증 ON 실패 → 테스트용 비검증으로 재시도:", e)
        print("외부 IP (http.client, verify=OFF):", get_external_ip_httpclient(verify_ssl=False))

(SSL 인증서 문제가 있는 환경을 고려해, verify_ssl 옵션으로 검증을 끄거나 켤 수 있게 했습니다. 운영/배포 환경에선 verify_ssl=True를 권장합니다.)

  • 운영/배포: verify_ssl=True 유지(기본값).
  • 로컬 테스트에서 인증서 오류 발생: 일시적으로 verify_ssl=False로 호출 → 동작 확인 후, 인증서 환경을 정상화하세요.
    • (macOS) Python 설치 경로의 Install Certificates.command 실행
    • (Windows/Linux) 시스템 루트 인증서 최신 상태로 유지

3. 클래스로 묶기

  • IPFetcher 클래스 안에 get_internal_ip(), get_external_ip_urllib(), get_external_ip_httpclient() 메서드 3개를 넣었습니다.
  • verify_ssl, timeout, target_host 등은 생성자에서 기본값 설정 후 메서드에서 사용합니다.  

 

 
import socket             # 네트워크 소켓(통신) 기능 제공
import urllib.request     # 고수준 HTTP(S) 요청을 보낼 수 있는 표준 라이브러리
import http.client        # 저수준 HTTP(S) 요청을 다루는 표준 라이브러리
import json               # JSON 문자열 ↔ 파이썬 객체 변환
import ssl                # SSL/TLS 인증서 검증 및 컨텍스트 설정


class IPFetcher:
    """
    내부 IP(사설 IP)와 외부 IP(공인 IP)를 조회하는 기능을 제공하는 클래스.
    """

    def __init__(self, target_host="www.google.com", port=443, timeout=5, verify_ssl=True):
        """
        클래스 초기화.
        :param target_host: 내부 IP 확인을 위해 연결할 외부 호스트 (기본: 구글)
        :param port: 연결 포트 (기본: 443)
        :param timeout: 네트워크 연결 타임아웃 (초)
        :param verify_ssl: 외부 IP 조회 시 SSL 인증서 검증 여부 (운영환경: True 권장)
        """
        self.target_host = target_host
        self.port = port
        self.timeout = timeout
        self.verify_ssl = verify_ssl

    def get_internal_ip(self):
        """
        내부(사설) IP 조회.
        원리: 외부 호스트에 소켓 연결을 열면, OS가 선택한 네트워크 인터페이스의 로컬 주소(IP)를 알 수 있음.
        """
        with socket.create_connection((self.target_host, self.port), timeout=self.timeout) as s:
            return s.getsockname()[0]

    def get_external_ip_urllib(self):
        """
        외부(공인) IP 조회 - urllib 사용.
        :return: 외부 IP 문자열
        """
        context = ssl.create_default_context() if self.verify_ssl else ssl._create_unverified_context()

        with urllib.request.urlopen(url, context=context, timeout=self.timeout) as resp:
            body = resp.read().decode("utf-8")
            return json.loads(body)["ip"]

    def get_external_ip_httpclient(self):
        """
        외부(공인) IP 조회 - http.client 사용.
        :return: 외부 IP 문자열
        """
        context = ssl.create_default_context() if self.verify_ssl else ssl._create_unverified_context()
        conn = http.client.HTTPSConnection("api.ipify.org", context=context, timeout=self.timeout)
        try:
            conn.request("GET", "/?format=json")
            res = conn.getresponse()
            body = res.read().decode("utf-8")
            return json.loads(body)["ip"]
        finally:
            conn.close()


# ---- 사용 예시 ----
if __name__ == "__main__":
    fetcher = IPFetcher(timeout=5, verify_ssl=True)

    # 내부 IP 확인
    print("내부 IP :", fetcher.get_internal_ip())

    # 외부 IP 확인 (urllib)
    try:
        print("외부 IP (urllib):", fetcher.get_external_ip_urllib())
    except Exception as e:
        print("urllib 오류:", e)
        print("재시도 (verify_ssl=False):", IPFetcher(verify_ssl=False).get_external_ip_urllib())

    # 외부 IP 확인 (http.client)
    try:
        print("외부 IP (http.client):", fetcher.get_external_ip_httpclient())
    except Exception as e:
        print("http.client 오류:", e)
        print("재시도 (verify_ssl=False):", IPFetcher(verify_ssl=False).get_external_ip_httpclient())

 

이제, 아래 예시처럼 IPFetcher 객체 하나로 내부/외부 IP 조회 기능을 모두 사용할 수 있습니다.

 
fetcher = IPFetcher(timeout=3, verify_ssl=True)
print(fetcher.get_internal_ip())
print(fetcher.get_external_ip_urllib())
print(fetcher.get_external_ip_httpclient())
 

 

 

반응형

+ Recent posts