✅ 1. 프록시(Proxy) 란?
- 다른 객체에 대한 접근을 제어하는 객체로 디자인 패턴의 프록시 패턴과 일치하는 개념이다.
- 프록시의 주요 목적은 접근 제어, 로깅/감시, 지연 로딩, 보안 처리, 트랜잭션 처리 등이 있다.
✅ 2. 프록시패턴(Proxy Pattern) 이란?
interface Subject {
void request();
}
class RealSubject implements Subject {
public void request() {
System.out.println("실제 작업 수행");
}
}
class Proxy implements Subject {
private RealSubject realSubject;
public void request() {
if (realSubject == null) {
realSubject = new RealSubject(); // 지연 초기화
}
System.out.println("프록시: 접근 제어 또는 로깅");
realSubject.request(); // 실제 객체에 요청 전달
}
}
- 프록시 개념을 이용해 객체를 통해 다른 객체에 접근하도록 만드는 설계 패턴을 의미한다.
- GoF(Gang of Four)의 디자인 패턴 중 하나이며, 구조 패턴에 속하게 된다.
- 보통 프록시 패턴에서는 접근을 제어하거나 부가적인 작업(로깅, 인증, 캐싱 등)을 하게 된다.
- 정리하면, 프록시 패턴은 구현 객체를 직접 호출하지 않고 같은 인터페이스를 구현판 프록시 객체를 통해 필요한 부가 로직(로깅, 인증, 캐싱, 트랜잭션 등)을 먼저 수행한 후 진짜 구현 객체의 메서드를 호출하는 방식이다.
- Spring 에선 AOP 를 활용 하면 비즈니스 로직의 부가 기능을 넣을 수 있는데, 이때 AOP는 프록시 객체를 만들어 부가 기능을 수행 한 뒤 실제 로직을 실행을 시켜준다. 이때 프록시 객체를 개발자가 직접 만들지 않아도 Spring 은 AOP 설정을 보고 프록시 객체를 런타임에 동적으로 생성해서 빈으로 등록을 해준다. 즉, 개발자는 비즈니스 로직만 작성을 하면 된다. 이 처럼 프록시객체를 런타임에 만들어 주는 방식을 동적 프록시(Dynamic Proxy) 라고 한다.
- 반대로 개발자가 직접 프록시 객체를 정의하는 것은 정적 프록시(Static Proxy) 라고 부른다. ( 프록시 패턴 활용 )
✅ 3. 동적 프록시(Dynamic Proxy) 란?
| 구분 | 정적 프록시 | 동적 프록시 |
| 생성 시점 | 컴파일 타임 | 런타임 |
| 구현 방식 | 개발자가 직접 구현 | 프레임워크가 자동 생성 |
| 기반 | 인터페이스 or 클래스 | 인터페이스(JDK), 클래스(CGLIB) |
| 유연성 | 낮음 | 높음 |
| 사용 예 | 프록시 패턴 직접 구현 | Spring AOP, Feign, JPA 등 |
- 런타임에 인터페이스를 구현한 프록시 객체를 생성할 수 있는 기능을 의미한다.
- 쉽게말해, 인터페이스만 있으면 구현체 없이도 그 동작을 가로채거나 조작하는 객체를 만들 수 있는 기능입니다.
- 런타임 시점에 프록시 객체를 동적으로 생성하는 기법(기술)을 의미한다.
- 즉, 내부적으로 프록시 패턴을 동적으로 구현하는 기술이기 때문에 패턴이 아닌 동적으로 구현하는 기술적인 수단임.
- Java 에서는 java.lang.reflect.Proxy 와 InvocationHandler 를 이용해서 동적 프록시를 구현하게 됨.
(1) JDK 동적 프록시 - 인터페이스 기반
- 인터페이스가 반드시 존재해야 하며, 인터페이스를 구현한 익명 클래스(프록시 객체)를 런타임에 생성을 하는 방식이다.
- 인터페이스 타입으로 반환이 되며, 실제 구현체 없이도 인터페이스에 선언된 메서드를 가로채서 부가 로직 수행이 가능함.
- 대표적인 예시로는 @FeignClient , Spring Data JPA 의 Repository, @Transactional 등이 있다.
- 즉, 인터페이스를 만들면 해당 인터페이스를 구현하는 프록시 객체를 내부적으로 만든다는 의미임. 내부 구현
- JDK(Java Development Kit) 에서 기본 제공하는 기능이며,java.lang.reflect.Proxy 클래스를 이용해서 런타임에 프록시 객체를 생성한다. 즉, 자바 JDK 자체에 있는 기능을 사용해서 인터페이스 기반으로 런타임(동적)에 프록시를 만드는 방식이라 JDK 동적 프록시라 부름 ( 참고: 반드시 인터페이스가 있어야 함 )
(2) CGLIB 프록시 - 클래스 기반
- 기존 구현 클래스의 복사본(서브클래스)을 만들어 프록시 객체를 생성하는 방식이다.
- 기존 클래스의 서브클래스(상속)를 생성해서, 메서드를 오버라이딩하여 가로채는 방식이다.
- 원본 클래스의 하위 클래스이며, 원본 클래스와 동일한 기능을 하면서 추가 기능을 삽입한다.
- 이 방식은 SpringBoot 의 @Service, @Component 같이 빈으로 등록되 구현 클래스에 직접 AOP가 적용될 때 사용함.
- 쉽게말해, 해당 빈에 AOP(부가 기능)를 적용할 때 사용되는 기술을 의미한다.
- CGLIB 은 Code Generaion Library 의 약자로 외부 라이브러리를 사용하는 프록시를 의미한다. 외부 바이트코드 조작 라이브러리를 사용해서, 바이트코드를 직접 생성해서, 원본 클래스의 서브클래스(자식 클래스)를 만들어 냄으로써 클래스 기반 프록시를 생성하기 때문에 CGLIB 프록시라고 부르는 것이다. 스프링에서는 가능하면 JDK 프록시를 우선 사용하고, 필요 할 때만 CGLIB 으로 fallback 을 하게 된다. ( 걍 진짜 직접 짜주는 느낌이라 생각하면 됨. )
✅ 4. 대표적인 동적 프록시(Dynamic Proxy) 예시
(1) Spring AOP
@Service
public class OrderService {
@Transactional
public void placeOrder() {
// 핵심 로직
}
}
- 트랜잭션, 로깅, 보안, 성능 측정 등 부가 기능을 비즈니스 로직과 분리해서 처리하기 위한 용도
(2) Spring Data JPA
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
}
- JpaRepository 같은 인터페이스만 정의하면 자동으로 구현체가 생성이 되며, 내부적으로 프록시 객체를 만든다.
(3) FeignClient - Spring Cloud OpenFeign
@FeignClient(name = "user-service", url = "http://localhost:8080")
public interface UserClient {
@GetMapping("/users/{id}")
User getUserById(@PathVariable Long id);
}
- HTTP API 호출을 Java 인터페이스처럼 사용을 한다. 내부적으로 프록시 객체를 만든다.
(4) Spring Security
- FilterChainProxy, MethodSecurityInterceptor 등에서 인증된 사용자의 권한 확인이나 접근 제헌 등을 처리할 때 프록시 기반 보안 필터 체인이 동작을 한다.
'공부 > ☕Java' 카테고리의 다른 글
| [Java] JDBC 동작 원리와 커넥션 풀 이해하기 (2) | 2025.08.06 |
|---|---|
| [Java] 멀티 스레드 프로그래밍 (2) | 2025.06.04 |
| [Java] 자바 스트림(Stream) 개념 + 예제 총정리 📚 (2) | 2025.06.03 |
| [Java] 컬렉션 프레임워크 정리: List, Set, Map 인터페이스와 대표 구현체 총정리 (0) | 2025.05.30 |
| [Java] java.nio 사용법 | Path, Files, FileChannel 예제 모음 (3) | 2025.05.30 |