Mobile & App Stack/Java Software Architecture & Patterns

프록시 패턴(Proxy Pattern) 완벽 정리: 제어와 성능 최적화의 핵심

임베디드 친구 2024. 12. 26. 08:49
반응형

소프트웨어 설계에서 어떤 객체를 생성하는 데 비용이 많이 들거나, 객체에 접근하기 전 권한을 확인해야 한다면 어떻게 해야 할까요? 실제 객체를 직접 노출하는 대신 대리인을 내세우는 프록시 패턴(Proxy Pattern)이 그 해답입니다.

오늘은 성능 최적화와 보안의 핵심인 프록시 패턴에 대해 깊이 있게 알아보겠습니다.

Generated by Gemini AI.


1. 프록시 패턴이란?

프록시(Proxy)는 '대리인'이라는 뜻입니다. 프록시 패턴은 실제 객체(RealSubject)에 접근하기 전, 중간에 대리자 객체(Proxy)를 두어 접근을 제어하거나 부가적인 로직을 수행하게 하는 구조 패턴입니다.

왜 사용하는가?

  • 지연 초기화(Lazy Initialization): 객체 생성 비용이 클 때, 실제로 사용되는 시점까지 생성을 미뤄 시스템 성능을 높입니다.
  • 보안 및 접근 제어: 클라이언트가 객체에 접근하기 전 권한을 검증합니다.
  • 로깅 및 모니터링: 실제 객체의 메서드 호출 전후로 로그를 남기거나 실행 시간을 측정할 수 있습니다. (Spring AOP의 핵심 원리)

2. 프록시 패턴의 주요 종류

유형 핵심 역할 활용 예시
가상 프록시 (Virtual) 무거운 객체의 생성을 지연시킴 고해상도 이미지 로딩, DB 지연 로딩
보호 프록시 (Protection) 객체 접근 권한을 제어함 사용자 등급별 메뉴 접근 제한
원격 프록시 (Remote) 멀리 떨어진 서버의 객체를 로컬처럼 사용 RPC, RMI, 대리 응답
캐싱 프록시 (Caching) 데이터 요청 결과를 저장해 재사용 웹 서버의 프록시 서버, 결과값 캐싱

3. 프록시 패턴의 구조

프록시 패턴은 클라이언트가 실제 객체와 프록시를 동일한 방식으로 다룰 수 있도록 공통 인터페이스를 구현하는 것이 핵심입니다.

  • Subject: 프록시와 실제 객체가 구현할 공통 인터페이스입니다.
  • RealSubject: 실제 핵심 비즈니스 로직을 수행하는 객체입니다.
  • Proxy: 실제 객체를 필드로 가지며, 요청을 전달하기 전후에 제어 로직을 수행합니다.

4. Java 구현 예제: 이미지 지연 로딩 (가상 프록시)

웹 페이지에 수많은 고화질 이미지가 있을 때, 모든 이미지를 한꺼번에 로딩하면 속도가 매우 느려집니다. 사용자가 스크롤하여 이미지를 보려고 할 때만 로딩하는 '가상 프록시'를 구현해 보겠습니다.

Step 1. 공통 인터페이스 정의

Java
 
interface Image {
    void display();
}

Step 2. 실제 객체 (RealSubject) 구현

Java
 
class RealImage implements Image {
    private String fileName;

    public RealImage(String fileName) {
        this.fileName = fileName;
        loadFromDisk(); // 생성 시 리소스 소모가 큰 작업 수행
    }

    private void loadFromDisk() {
        System.out.println("💾 디스크에서 이미지 로딩 중...: " + fileName);
    }

    @Override
    public void display() {
        System.out.println("🖼️ 이미지 표시: " + fileName);
    }
}

Step 3. 프록시 객체 (Proxy) 구현

Java
 
class ProxyImage implements Image {
    private RealImage realImage;
    private String fileName;

    public ProxyImage(String fileName) {
        this.fileName = fileName;
    }

    @Override
    public void display() {
        // 지연 초기화: 실제로 화면에 보여줄 때 객체를 생성함
        if (realImage == null) {
            realImage = new RealImage(fileName);
        }
        realImage.display();
    }
}

Step 4. 클라이언트 코드 테스트

Java
 
public class ProxyDemo {
    public static void main(String[] args) {
        Image image = new ProxyImage("high_resolution_photo.jpg");

        // 1. 처음 호출 시: 실제 객체가 생성되고 로딩됨
        System.out.println("[첫 번째 호출]");
        image.display();

        // 2. 두 번째 호출: 이미 생성된 객체를 사용하여 성능 이득
        System.out.println("\n[두 번째 호출]");
        image.display();
    }
}

5. 프록시 패턴 vs 데코레이터 패턴

두 패턴은 구조가 매우 비슷하여 혼동하기 쉽습니다. 하지만 의도(Intent)에서 큰 차이가 있습니다.

  • 프록시 패턴: 객체에 대한 접근 제어생성 지연이 주 목적입니다. (대리인 역할)
  • 데코레이터 패턴: 객체에 새로운 기능을 동적으로 추가하는 것이 목적입니다. (장식 역할)

결론

프록시 패턴은 단순한 대리인을 넘어 시스템의 성능을 최적화하고 보안을 강화하는 강력한 설계 기법입니다. 우리가 매일 사용하는 Spring 프레임워크의 트랜잭션(@Transactional)이나 AOP도 이 프록시 패턴을 기반으로 동작합니다.

객체 생성 비용이 고민되거나 접근 로직을 분리하고 싶다면 프록시 패턴 도입을 적극 추천합니다!


도움이 되셨다면 공감과 구독 부탁드립니다!

반응형