프록시 패턴 심층 가이드: 접근 제어 구축의 예술

발행: (2026년 5월 21일 AM 10:05 GMT+9)
7 분 소요
원문: Dev.to

Source: Dev.to

프록시 패턴 개요

핵심 역할

역할설명
Subject(추상 주제)RealSubjectProxy의 공통 인터페이스를 정의
RealSubject(실제 주제)실제 비즈니스 로직을 수행하는 객체
Proxy(프록시)RealSubject에 대한 참조를 보유하고, 필요할 때 실제 객체를 생성하거나 접근
Client(클라이언트)Proxy를 통해 실제 객체와 상호 작용하며, 실제 객체 존재를 인식하지 못함

전형적인 호출 체인은 다음과 같습니다:

클라이언트 → 로컬 프록시 → 네트워크 → 원격 서비스

프록시 패턴의 전형적인 적용

지연 로드(가상 프록시)

비용이 많이 드는 리소스(예: 원격 이미지)에 접근해야 할 때, 실제로 필요할 때만 실제 객체를 생성합니다.

class VirtualProxyImage:
    def __init__(self, url):
        self.url = url
        self._real_image = None

    def display(self):
        # 최초 호출 시에만 실제 이미지를 로드
        if not self._real_image:
            self._real_image = RealImage(self.url)
        self._real_image.display()

접근 제어

프록시는 호출 전에 인증이나 권한 검사를 수행할 수 있으며, 이는 회사 출입 통제 시스템과 유사합니다.

캐시 및 지연 로드 예시

아래 예시는 프록시를 사용해 이미지의 지연 로드와 캐시를 구현하여 디스크 읽기를 중복하지 않도록 하는 방법을 보여줍니다.

from abc import ABC, abstractmethod
import time

# 추상 주제
class Image(ABC):
    @abstractmethod
    def display(self):
        pass

# 실제 주제
class RealImage(Image):
    def __init__(self, filename):
        self.filename = filename
        self._load_from_disk()

    def _load_from_disk(self):
        print(f"이미지를 로드 중: {self.filename}")
        time.sleep(1)  # 로드 시간 시뮬레이션
        print("로드 완료!")

    def display(self):
        print(f"이미지 표시: {self.filename}")

# 가상 프록시
class ProxyImage(Image):
    def __init__(self, filename):
        self.filename = filename
        self._real_image = None

    def display(self):
        if self._real_image is None:
            self._real_image = RealImage(self.filename)
        self._real_image.display()

# 사용 예시
print("=== 첫 번째 표시 ===")
img = ProxyImage("photo.jpg")
img.display()

print("\n=== 두 번째 표시 ===")
img.display()

실행 결과

=== 첫 번째 표시 ===
이미지를 로드 중: photo.jpg
로드 완료!
이미지 표시: photo.jpg

=== 두 번째 표시 ===
이미지 표시: photo.jpg

프록시 패턴 특성 비교

특성프록시 패턴데코레이터 패턴
목적접근 제어, 지연 로드, 권한 검증동적 기능 추가, 행동 강화
관계실제 객체를 대체실제 객체를 감싸는 형태
생성 시점사전 또는 런타임에 생성런타임에 감싸서 생성
클라이언트 인식무감지(투명 프록시)추가 데코레이션 레이어를 인식

실제 사례

연결 풀(리소스 프록시)

연결 풀은 본질적으로 데이터베이스/네트워크 연결에 대한 프록시입니다. 미리 일정 수의 연결을 생성해 두고, 클라이언트는 프록시를 통해 연결을 얻고 사용 후 반환합니다. 매번 새로 만들 필요가 없습니다.

API 제한 프록시

고성능 시스템에서 프록시는 요청 속도 제한을 구현해 백엔드 서비스가 과부하되지 않도록 보호합니다.

import time

class RateLimitedProxy:
    def __init__(self, real_service, max_requests, time_window):
        self.real_service = real_service
        self.max_requests = max_requests
        self.time_window = time_window
        self.requests = []  # 요청 타임스탬프 기록

    def call(self, *args, **kwargs):
        now = time.time()
        # 만료된 요청 기록 정리
        self.requests = [t for t in self.requests if now - t < self.time_window]
        if len(self.requests) >= self.max_requests:
            raise Exception("요청이 너무 빈번합니다. 잠시 후 다시 시도하세요")

        self.requests.append(now)
        return self.real_service.execute(*args, **kwargs)

온라인 시험 시스템에서의 프록시

  • 화면 전환 횟수 감지
  • 복사·붙여넣기 제한
  • 비정상적인 행동(예: 이상 네트워크 요청) 기록

프록시를 활용하면 비즈니스 로직을 수정하지 않고도 위와 같은 모니터링·제한 기능을 구현할 수 있습니다.

프록시 패턴의 가치

  • 디커플링: 접근 로직과 비즈니스 로직을 분리
  • 강화: 로그, 캐시, 제한 등 기능을 무감지하게 추가
  • 제어: 접근 제어·권한 검증 구현
  • 최적화: 지연 로드와 캐시를 통해 성능 향상

프록시 패턴을 마스터하면 시스템 설계 시 강력한 무기를 얻게 됩니다.

다음 예고

전략 패턴(Strategy Pattern) — 알고리즘을 교환 가능하게 만드는 예술.

맺음말

프록시 패턴을 적절히 사용하면 시스템의 성능과 보안을 크게 향상시킬 수 있지만, 과도한 설계는 피해야 합니다. 실제로 접근 제어나 횡단 관심사를 추가해야 할 때만 프록시 도입을 고려하세요. 디자인 패턴 학습 여정에서 풍성한 수확을 거두시길 바랍니다!

0 조회
Back to Blog

관련 글

더 보기 »