반응형
소개
인터넷을 사용하다 보면 VPN과 프록시 서버라는 용어를 자주 접하게 됩니다. 이 둘은 비슷해 보이지만 작동 방식과 용도에 중요한 차이가 있습니다. 이번 글에서는 VPN과 프록시 서버의 차이점을 이해하고, Python의 aiohttp를 사용한 구현 방법까지 자세히 알아보겠습니다.
VPN(Virtual Private Network)의 이해
VPN은 공용 네트워크를 통해 사설 네트워크처럼 안전한 연결을 만드는 기술입니다.
VPN의 주요 특징
- 전체 네트워크 트래픽을 암호화
- 운영체제 수준에서 작동
- IP 주소 완전 변경
- 모든 애플리케이션에 적용
프록시 서버의 이해
프록시 서버는 클라이언트와 목적지 서버 사이에서 중계 역할을 하는 서버입니다.
프록시 서버의 종류
- Forward Proxy (포워드 프록시)
- 클라이언트 -> 프록시 -> 목적지 서버 내부 네트워크에서 외부로 나가는 트래픽을 중계
- Reverse Proxy (리버스 프록시)
- 클라이언트 <- 프록시 <- 목적지 서버 외부에서 들어오는 트래픽을 내부 서버로 분산 로드 밸런싱, 캐싱 등의 용도로 사용
- SOCKS Proxy
- TCP/UDP 레벨에서 작동하는 프록시 더 낮은 레벨에서 동작하여 다양한 프로토콜 지원
VPN과 프록시의 주요 차이점
VPN | Proxy | |
보안 수준 | 전체 트래픽 암호화 | 특정 애플리케이션/프로토콜만 처리 |
적용 범위 | 시스템 전체 | 특정 애플리케이션 또는 프로토콜 |
속도 | 암호화로 인한 속도 저하 | 상대적으로 빠름 |
설정 복잡도 | 시스템 수준 설정 필요 | 애플리케이션별 간단 설정 |
aiohttp를 사용한 프록시 구현
기본 프록시 클라이언트
import aiohttp
import asyncio
from typing import Optional, Dict
class ProxyClient:
def __init__(self, proxy_url: Optional[str] = None):
"""
비동기 프록시 클라이언트
Args:
proxy_url: "http://proxy.example.com:8080" 형식의 프록시 주소
"""
self.proxy_url = proxy_url
self.session = None
async def __aenter__(self):
"""비동기 컨텍스트 관리자 진입"""
self.session = aiohttp.ClientSession()
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
"""세션 정리"""
if self.session:
await self.session.close()
async def get(self, url: str, headers: Optional[Dict] = None) -> str:
"""
프록시를 통한 GET 요청
Example:
async with ProxyClient("http://proxy.example.com:8080") as client:
response = await client.get("http://example.com")
"""
if not self.session:
raise RuntimeError("세션이 초기화되지 않았습니다")
async with self.session.get(
url,
proxy=self.proxy_url,
headers=headers
) as response:
return await response.text()
프록시 서버 구현
from aiohttp import web
import aiohttp
from typing import Optional
class ProxyServer:
def __init__(self, host: str = "0.0.0.0", port: int = 8080):
"""
간단한 프록시 서버 구현
"""
self.host = host
self.port = port
self.app = web.Application()
self.app.router.add_route("*", "/{path:.*}", self.handle_request)
async def handle_request(self, request: web.Request) -> web.Response:
"""
모든 요청을 처리하는 핸들러
"""
# 원본 요청 URL 재구성
path = request.match_info["path"]
target_url = f"http://{path}"
# 클라이언트 헤더 복사
headers = dict(request.headers)
headers.pop("Host", None)
try:
async with aiohttp.ClientSession() as session:
# 원본 요청 메서드와 동일한 메서드로 요청
async with session.request(
request.method,
target_url,
headers=headers,
data=await request.read()
) as response:
# 응답 반환
return web.Response(
status=response.status,
body=await response.read(),
headers=response.headers
)
except Exception as e:
return web.Response(status=500, text=str(e))
def run(self):
"""서버 실행"""
web.run_app(self.app, host=self.host, port=self.port)
# 사용 예시
if __name__ == "__main__":
proxy = ProxyServer()
proxy.run()
SOCKS5 프록시 클라이언트
import aiohttp
from aiohttp_socks import ProxyConnector
from typing import Optional
class SOCKS5Client:
def __init__(
self,
proxy_host: str,
proxy_port: int,
username: Optional[str] = None,
password: Optional[str] = None
):
"""
SOCKS5 프록시 클라이언트
Args:
proxy_host: 프록시 서버 주소
proxy_port: 프록시 서버 포트
username: 인증 사용자명 (선택)
password: 인증 비밀번호 (선택)
"""
self.proxy_host = proxy_host
self.proxy_port = proxy_port
self.username = username
self.password = password
self.session = None
async def __aenter__(self):
"""프록시 연결 설정"""
connector = ProxyConnector.from_url(
f"socks5://{self.proxy_host}:{self.proxy_port}",
username=self.username,
password=self.password
)
self.session = aiohttp.ClientSession(connector=connector)
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
"""세션 정리"""
if self.session:
await self.session.close()
async def request(self, method: str, url: str, **kwargs):
"""
프록시를 통한 요청 전송
Example:
async with SOCKS5Client("proxy.example.com", 1080) as client:
response = await client.request("GET", "http://example.com")
"""
if not self.session:
raise RuntimeError("세션이 초기화되지 않았습니다")
async with self.session.request(method, url, **kwargs) as response:
return await response.text()
보안 및 모니터링
프록시 로깅 미들웨어
import logging
from datetime import datetime
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
@web.middleware
async def logging_middleware(request: web.Request, handler):
"""
프록시 요청/응답 로깅
"""
start_time = datetime.now()
try:
response = await handler(request)
duration = datetime.now() - start_time
logger.info(
f"Request: {request.method} {request.path} "
f"Status: {response.status} "
f"Duration: {duration.total_seconds():.3f}s"
)
return response
except Exception as e:
logger.error(f"Error processing request: {str(e)}")
raise
활용 예시
지역 제한 우회
async def access_geo_restricted_content(url: str, proxy_url: str):
"""
지역 제한 콘텐츠 접근 예시
"""
async with ProxyClient(proxy_url) as client:
try:
content = await client.get(
url,
headers={"User-Agent": "Mozilla/5.0 ..."}
)
return content
except Exception as e:
print(f"접근 실패: {str(e)}")
return None
웹 스크래핑
async def scrape_with_rotating_proxies(
urls: List[str],
proxy_list: List[str]
):
"""
여러 프록시를 번갈아 사용하는 스크래핑
"""
results = []
for url, proxy in zip(urls, cycle(proxy_list)):
async with ProxyClient(proxy) as client:
try:
content = await client.get(url)
results.append(content)
except Exception as e:
print(f"스크래핑 실패 ({proxy}): {str(e)}")
return results
마치며
VPN과 프록시는 각각의 장단점이 있으며, 사용 목적에 따라 적절히 선택해야 합니다. aiohttp를 사용하면 Python에서 효율적인 프록시 클라이언트와 서버를 구현할 수 있으며, 비동기 처리를 통해 높은 성능을 얻을 수 있습니다.
'파이썬 > 크롤링' 카테고리의 다른 글
requests vs aiohttp - HTTP 요청의 모든 것 (0) | 2025.01.11 |
---|---|
Requests : HTTP 요청 최적화 (0) | 2024.02.06 |
크롤링이 가능한 사이트인지 확인하는 방법 (0) | 2024.02.04 |
크롤링을 할 때 셀레니움이 필요한지 확인하는 방법 (0) | 2024.02.03 |
Selenium 4.1.0에서 headless 설정 변경 (0) | 2024.02.01 |