프록시(Proxy)란 ?
프록시=대신 받아주는 대리인
호출이 프록시를 먼저 거치고, 프록시가 필요하면 부가 작업을 수행한 뒤에 진짜 대상을 호출한다.
어디에서 사용되나 ?
- 네트워크 프록시 서버 : 사용자의 요청을 대신 보내주고 응답을 전달한다.
- 디자인 패턴의 프록시 : 원본 객체 앞에 대리 객체를 두어 접근 제어, 지연 로딩, 캐시, 로깅 등의 부가 기능을 붙인다.
- 스프링의 프록시(AOP) : 빈 앞에 프록시를 세워 부가 기능을 공통 처리한다.
프록시 서버 (Proxy Server)
프록시 서버는 클라이언트와 서버간에 주고 받는 네트워크 요청과 응답 사이에 위치하는 서버이다. 여러가지 이유로 사용되는데, 캐싱 또는 사용자 보호의 이유로 사용되는게 일반적이다.
1) 캐싱의 경우
고속 인터넷 연결이 가능한 건물 상에 프록시 서버를 두고, 사용자 요청이 오면 캐시에 같은 리소스가 있는지 먼저 확인한다.
- 캐시 HIT : 프록시가 바로 응답을 한다. 지연시간이 줄어들고, 외부 트래픽이 적어 원서버 부하가 적다.
- 캐시 MISS/만료 : 원서버에서 받아 업데이트 후 사용자에게 전달하고 캐시에 저장한다.
-> 반복 요청이 많은 이미지, 스크립트, 페이지에 특히 효과적이다.
2) 사용자 보호, 정책 적용
사용자는 서버와 직접 통신하지 않고 프록시에 요청한다. 프록시는 새 IP로 서버에 요청하고, 응답을 사용자에게 다시 전달한다.
디자인 패턴에서의 프록시
프록시는 원래 객체를 같은 타입의 대리 객체로 감싸서 접근을 제어하거나 부가 기능을 붙이는 패턴이다. 클라이언트는 인터페이스만 보고 쓰므로, 프록시를 써도 사용법은 변하지 않는다.
구성 요소
- Subject : 공통 인터페이스
- RealSubject : 실제 기능을 가진 원본 객체
- Proxy : 같은 인터페이스를 구현, 호출을 먼저 받아 처리 후 RealSubject에 위임
// ① 약속(인터페이스) — 클라이언트는 이 타입만 안다
interface Payment {
void pay(int amount);
}
// ② 원본(RealSubject)
class RealPayment implements Payment {
public void pay(int amount) {
System.out.println("은행에 실제 결제 요청: " + amount);
}
}
// ③ 프록시(Proxy) — 같은 인터페이스! 전/후로 부가 기능 추가
class PaymentProxy implements Payment {
private final Payment target;
public PaymentProxy(Payment target) { this.target = target; }
public void pay(int amount) {
System.out.println("[로그] 결제 전 검증");
target.pay(amount); // 원본에 위임
System.out.println("[로그] 영수증 저장");
}
}
// ④ 클라이언트 — “인터페이스만” 의존
class Checkout {
void doCheckout(Payment payment) {
payment.pay(10000); // 여기 코드는 절대 안 바뀐다!
}
}
여기서 보면,
- 클라이언트 입장 : '나는 Payment의 `pay()` 만 부르면 된다.
- 프록시 입장 : '같은 `pay()` 를 먼저 받아서 앞뒤로 부가 기능을 하고, 마지막에 원본에게 시킨다.
- 원본 입장 : '난 내 본업만 한다.'
다시 말해, 원본(RealSubject)와 같은 인터페이스를 가진 대리 객체(Proxy)가 먼저 호출을 받아 접근 제어, 지연 로딩, 캐시, 로깅 같은 부가 작업을 하고 원본에 위임한다.
언제 사용하면 좋은가 ?
- 지연 로딩 : 무거운 리소스는 필요할 때만 로드한다.
- 접근 제어 : 권한이나 상태 검증 후 위임한다.
- 캐시 : 같은 요청 결과를 재사용한다.
- 로그 : 호출 전 후로 로그를 호출한다.
스프링의 프록시
스프링은 부가 기능(로깅, 트랜잭션, 보안, 캐시 등)을 공통 처리하기 위해 빈 앞에 프록시를 세운다. 클라이언트의 호출이 프록시를 먼저 통과하면서 부가 기능이 실행되고, 그 다음 실제 메서드가 호출된다. 이게 바로 런타임 프록시 기반 AOP (Spring AOP)이다.
언제, 어떻게 만들어지는가 ?
애플리케이션 시작 시에 스프링은 모든 빈을 만들면서, '프록시가 필요할까?'를 검사한다. `@Aspect` ,`@Transactional`, `@Cacheable` , `@PreAuthorize`, `@Async` 과 같은 부가 기능 대상이면, 원본 객체 대신 프록시 객체를 빈으로 등록한다.
프록시 생성 방식 2가지
- JDK 동적 프록시 : 대상이 인터페이스를 구현하면 인터페이스 기반 프록시
- CGLIB : 인터페이스가 없으면 클래스 상속으로 프록시
대표적인 예시
- 로깅
- 트랜잭션
'Spring' 카테고리의 다른 글
| [Spring] PSA (Portable Service Abstraction) (0) | 2025.08.27 |
|---|---|
| [Spring] @Transactional 개념과 사용 (0) | 2025.08.19 |
| [Spring] AOP(Aspect Oriented Programming) (0) | 2025.08.11 |
| [Spring] 영속성 컨텍스트(Persistence Context) (3) | 2025.08.11 |
| [Spring] ORM과 JPA(Java Persistence API) (3) | 2025.08.11 |
