// 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트랩으로 호출을 가로채 로깅·검증을 얹을 수 있음- 클래식 구현보다 짧지만, 가로채는 지점이 언어 규칙에 묶여 있어 세밀한 제어는 클래스 쪽이 유리
정리
- 프록시는 기능을 바꾸지 않고 접근 경로만 바꾸는 패턴
- 같은 인터페이스를 유지하는 것이 전제 — 이게 깨지면 호출부가 대리인을 의식하게 되고 이점이 사라짐