JAVA

[JAVA] 프록시 패턴(Proxy Pattern)과 데코레이터 패턴(Decorator Pattern)

sagecode 2025. 6. 10. 14:52

객체지향 설계에서 자주 등장하는 디자인 패턴 중 하나인 프록시(Proxy) 패턴과 데코레이터(Decorator) 패턴은 구조는 유사하지만 목적은 전혀 다른 패턴이다.

 

프록시 패턴이란?

프록시(Proxy) 패턴은 실제 객체에 접근하기 전, 중간 객체를 통해 접근을 제어하는 패턴입니다. 이 중간 객체는 실제 객체와 동일한 인터페이스를 구현하며, 다음과 같은 용도로 활용된다.

  • 접근 제어 : 권한이 있는 사용자만 접근
  • 지연 로딩 : 실제 객체를 사용할 시점에 생성
  • 캐싱 : 동일 요청에 대해 미리 데이터를 준비해 놓고 빠른 응답
  • 로깅 : 호출 및 실행 내역 기록
  • 보안 : 요청 유효성 검사 등

구현

public interface Image {
    void display();
}
public class RealImage implements Image {
    private String fileName;

    public RealImage(String fileName) {
        this.fileName = fileName;
        loadFromDisk();
    }

    private void loadFromDisk() {
        System.out.println("Loading image: " + fileName);
    }

    @Override
    public void display() {
        System.out.println("Displaying image: " + fileName);
    }
}
public 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();
    }
}
public class Main {
    public static void main(String[] args) {
        Image img = new ProxyImage("sample.jpg");

        System.out.println("1. 프록시 객체 생성만 했을 때:");
        // 아직 이미지 로딩 안 됨

        System.out.println("\n2. display() 호출 시:");
        img.display(); // 실제 이미지 로딩됨

        System.out.println("\n3. 두 번째 display() 호출 시:");
        img.display(); // 로딩 없이 바로 표시
    }
}

실행결과

1. 프록시 객체 생성만 했을 때:

2. display() 호출 시:
Loading image: sample.jpg
Displaying image: sample.jpg

3. 두 번째 display() 호출 시:
Displaying image: sample.jpg

 

  • RealImage 객체는 처음 display()가 호출될 때만 생성됨 → 지연 로딩(Lazy Loading)
  • 클라이언트는 프록시인지 실제 객체인지 구분하지 않음 → 인터페이스 통일
  • 프록시가 객체 생성을 제어함

프록시 패턴이 필요한 문제상황들

그럼 이 프록시 패턴을 사용하면 해결가능한 문제상황들은 무엇이 있을까?

 

1. 객체 생성 비용이 너무 클 때 (지연 로딩)

-> 이미지나 동영상 같이 대용량의 파일인 경우 필요할 때만 객체를 생성하기 위해 프록시 객체에서 먼저 요청을 받는다.

 

2. 접근 권한을 제어해야 할 때

-> 관리자만 접근 가능한 데이터의 경우 프록시 객체에서 사용자 인증을 거친 뒤 객체를 생성한다.

 

3. 객체에 접근한 기록이 필요할 때

-> 관리자 로그, api 호출 내역 등을 기록하고 싶을 때, 프록시 객체를 통해서 들어온 요청들을 기록한다.

 

데코레이터 패턴이란?

데코레이터(Decorator) 패턴은 기존 객체의 기능을 유지하면서 새로운 기능을 추가할 수 있는 디자인 패턴이다. 프록시처럼 같은 인터페이스를 구현하지만, 접근 제어 목적이 아니라 기능 확장을 위해 사용된다.

  • 기존 코드를 변경하지 않고 기능을 추가하고 싶을 때
  • 상속보다 합성(composition)을 활용하고 싶을 때

구현

public interface Image {
    void display();
}
public class BasicImage implements Image {
    private String fileName;

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

    @Override
    public void display() {
        System.out.println("Displaying image: " + fileName);
    }
}
public abstract class Decorator implements Image {
    protected Image image;

    public Decorator(Image image) {
        this.image = image;
    }

    @Override
    public void display() {
        image.display(); // 기본 동작을 위임
    }
}
public class CaptionDecorator extends Decorator {
    private String caption;

    public CaptionDecorator(Image image, String caption) {
        super(image);
        this.caption = caption;
    }

    @Override
    public void display() {
        super.display(); // 원래 이미지 표시
        System.out.println("→ Caption: " + caption); // 추가 기능
    }
}
public class Main {
    public static void main(String[] args) {
        Image baseImage = new BasicImage("photo.jpg");
        Image decorated = new CaptionDecorator(baseImage, "caption~");

        System.out.println("[1] 기본 이미지:");
        baseImage.display();

        System.out.println("\n[2] 캡션 추가된 이미지:");
        decorated.display();
    }
}

 

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

Proxy Pattern
Decorator Pattern

프록시 패턴과 데코레이터 패턴의 차이점은 아래 표와 같다.

항목 프록시 패턴 데코레이터 패턴
목적 접근 제어, 지연 로딩, 로깅 등 기능 확장
객체 생성 시점 요청 조건에 따라 생성 항상 생성됨
대표 예시 Spring AOP, Hibernate Lazy Loading Java I/O Stream, UI 컴포넌트
구조 대리 객체가 실제 객체 대신 동작 기능을 덧붙이는 래퍼 객체