~/csslab.dev/notes $ ls

// lab

프록시 패턴 — 원본 앞에 대리인을 세우는 이유

  • GoF 구조 패턴 정리 — 원본 객체를 직접 쓰지 않고 대리 객체를 통해 사용하는 방식
  • 호출부는 원본과 대리인을 구분하지 않음 — 둘이 같은 인터페이스를 구현하기 때문
  • 정리 목적: "왜 한 겹 더 두는가"를 장점 단위로 분해해 두기

프록시를 두는 이유

원본과 호출부 사이에 자리를 만들면 그 자리에서 할 수 있는 일이 생김.

  • 접근 제어 — 권한을 확인하고 통과 여부를 결정
  • 지연 초기화 — 실제로 필요한 시점까지 원본 생성을 미룸
  • 캐싱 — 같은 요청의 결과를 재사용
  • 로깅 — 요청·응답을 기록
  • 원격 호출 감춤 — 원격 서비스를 로컬 객체처럼 다뤄 네트워크 비용을 줄임

클래식 구현

interface Subject {
  request(): void;
}

class RealSubject implements Subject {
  request() { /* 실제 작업 */ }
}

class ProxySubject implements Subject {
  constructor(private real: RealSubject) {}

  request() {
    if (!this.checkAccess()) return;   // 접근 제어
    this.real.request();
    this.logAccess();                  // 로깅
  }

  private checkAccess() { return true; }
  private logAccess() { /* 기록 */ }
}
  • 호출부는 Subject 타입만 알면 됨 → 원본을 대리인으로 교체해도 코드 변경 없음

언어 내장 Proxy

JS에는 프록시가 언어 차원에 있어 클래스를 따로 만들지 않아도 됨.

const handler = {
  get(target: object, prop: string, receiver: unknown) {
    // 속성 접근·메서드 호출을 가로채 부가 동작을 얹음
    return Reflect.get(target, prop, receiver);
  },
};

const guarded = new Proxy(original, handler);
  • get 트랩으로 호출을 가로채 로깅·검증을 얹을 수 있음
  • 클래식 구현보다 짧지만, 가로채는 지점이 언어 규칙에 묶여 있어 세밀한 제어는 클래스 쪽이 유리

정리

  • 프록시는 기능을 바꾸지 않고 접근 경로만 바꾸는 패턴
  • 같은 인터페이스를 유지하는 것이 전제 — 이게 깨지면 호출부가 대리인을 의식하게 되고 이점이 사라짐