728x90
반응형
728x90
Proxy Pattern
- 다른 객체에 대한 접근을 제어하는 대리 객체를 만드는 패턴
- 실제 객체에 대한 접근을 간접적으로 처리
- 주로 접근 제어나 지연 로딩등의 목적을 가지고 사용
구성 요소
- Subject: 프록시가 제어할 실제 객체가 구현해야 하는 인터페이스
- Real Subject: 실제 비즈니스 로직을 구현한 클래스 -> 실제 작업 수행
- Proxy: 주제 인터페이스를 구현, Real Subject에 대한 참조를 가짐 -> 클라이언트의 요청을 Real Subject에 전달
동작 과정
- 클라이언트 요청
- 클라이언트는 실제 객체가 아닌 프록시 객체에 요청
- 실제 객체와 동일한 인터페이스를 구현하여 실제 객체를 호출하는 것처럼 보임
- 프록시 객체 처리
- 프록시 객체는 클라이언트로부터 받은 요청을 처리
- 접근 제어 용도로 사용 시 실제 객체에 접근 권한이 있는지 확인하는 등 처리 과정 추가 가능
- Real Subject 호출
- 프록시 객체에서 처리 후 실제 객체 호출
- 프록시 객체는 실제 Real Subject 객체를 참조로 가지고 있어 메서드 호출 가능 -> 실제 객체 사용 시 객체를 생성하는 지연 초기화 (lazy initialization)
- 실제 객체가 아닌 체인 형식으로 다른 프록시 호출 가능 -> 같은 인터페이스를 구현한 객체이기 때문
- Real Subject 작업 수행
- 클라이언트 요청에 대한 실제 작업을 처리
- 결과 반환
- Real Subject를 통해 처리한 결과를 프록시가 받아 클라이언트에게 반환
//Subject - 실제 객체가 구현할 인터페이스
public interface Subject {
void request();
}
//RealSubject - 실제 객체
public class RealSubject implements Subject {
@Override
public void request() {
log.info("실제 객체 호출");
}
}
//Proxy - 프록시 객체 (실제 객체와 같은 Subject 인터페이스를 구현)
public class Proxy implements Subject {
private RealSubject realSubject; //실제 객체의 참조를 가짐
public Proxy() {
this.realSubject = new RealSubject();
}
@Override
public void request() {
log.info("프록시: 접근 권한 체크");
realSubject.request();
}
}
//Client - 요청을 보낼 클라이언트
public class Client {
private final Subject subject;
public void execute() {
subject.request();
}
}
//사용
RealSubject realSubject = new RealSubject();
Client client = new Client(realSubject);
client.execute();
장점
- 실제 객체에 대한 접근을 통제할 수 있어 보안을 강화
- 객체를 필요할 때만 생성하거나 초기화하여 메모리와 성능을 최적화
단점
- 추가적인 레이어가 생기므로 코드가 복잡해짐 -> 프록시 객체를 생성해야 함
- 프록시를 통해 요청을 처리하기 때문에 약간의 성능 저하가 발생가능 -> 캐싱 등으로 사용 시 성능 향상 가능
Decorator Pattern
- 프록시 패턴과 유사하게 실제 객체를 감싼 데코레이터 객체를 통해 요청과 반환을 받음
- 프록시 패턴은 접근 제어를 주목적으로 사용했다면 데코레이터 패턴은 기능을 동적으로 추가하거나 수정하기 위한 목적으로 사용
구성 요소
- Component: 기능을 제공하는 인터페이스 또는 추상 클래스 (Subject)
- Concrete Component: 컴포넌트 인터페이스를 구현하는 구체적인 클래스 (Real Subject) - 기본 기능 제공
- Decorator: 컴포넌트 인터페이스를 구현하며 컴포넌트를 포함하는 클래스 (Proxy) - 기존 기능을 감싸고 추가적인 기능 제공
- Concrete Decorator: 데코레이터 클래스를 상속받아 특정 기능을 추가하는 클래스
동작 과정
- 기본 객체 생성
- 클라이언트는 기본 기능을 제공하는 객체를 생성
- 기능을 추가하기 전 핵심 로직 -> Real Subject 같은 객체
- 데코레이터 객체 생성
- 원하는 기능을 추가하기 위해 데코레이터 객체 생성 -> 프록시 객체 생성
- 데코레이터는 기존 리얼 객체를 인자로 받아 내부에서 참조 -> 프록시 내부에 실제 객체 참조를 가지는 것과 동일
- 기능 조합 및 추가
- 여러 데코레이터를 조합하여 다양한 기능을 추가할 수 있음
- 각 데코레이터는 자신의 기능을 추가하기 위해 내부의 리얼 객체에 대한 호출을 감싸는 구조를 가짐
- 메서드 호출
- 데코레이터 전처리: 실제 객체의 메서드 호출 전 추가 기능 실행
- 실제 객체 호출: 실제 객체의 메서드 호출 - 혹은 다음 데코레이터 호출
- 데코레이터 후처리: 메서드 호출 후 추가 기능 실행
- 결과 반환
- 클라이언트에게 결과 반환
//Component - 실제 객체가 구현할 인터페이스
public interface Component {
String request();
}
//Concrete Component - 실제 객체
public class ConcreteComponent implements Component {
@Override
public String request() {
return "example";
}
}
체인 형태의 호출을 이용한 방식
//Decorator - 시간을 측정하는 기능이 추가된 데코레이터
public class TimeDecorator implements Component {
private final Component component;
public TimeDecorator(Component component) {
this.component = component;
}
@Override
public String request() {
log.info("TimeDecorator 실행");
long startTime = System.currentTimeMillis();
String result = component.request();
long endTime = System.currentTimeMillis();
long resultTime = endTime - startTime;
log.info("TimeDecorator 종료 resultTime={}ms", resultTime);
return result;
}
}
//Decorator - Message 포매팅이 추가된 데코레이터
public class MessageDecorator implements Component{
private final Component component;
public MessageDecorator(Component component) {
this.component = component;
}
@Override
public String request() {
log.info("MessageDecorator 실행");
String request = component.request();
String decoResult = "*****" + request + "*****";
log.info("MessageDecorator 적용 전={}, 적용 후={}", request, decoResult);
return decoResult;
}
}
//Client
public class Client {
private final Component component;
public void execute() {
String result = component.request();
log.info("result={}",result);
}
}
//사용
RealComponent realComponent = new RealComponent();
MessageDecorator messageDecorator = new MessageDecorator(realComponent);
TimeDecorator timeDecorator = new TimeDecorator(messageDecorator);
Client client = new Client(timeDecorator);
client.execute();
상속을 이용한 방식
//Decorator - 기본 데코레이터
public abstract class Decorator implements Component {
protected final Component component;
public Decorator(Component component) {
this.component = component;
}
@Override
public String request() {
String result = component.request();
return result;
}
}
//Concrete Decorator - 시간을 측정하는 기능이 추가된 데코레이터
public class TimeDecorator extends Decorator {
public TimeDecorator(Component component){
super(component);
}
@Override
public String request() {
log.info("TimeDecorator 실행");
long startTime = System.currentTimeMillis();
String result = component.request();
long endTime = System.currentTimeMillis();
long resultTime = endTime - startTime;
log.info("TimeDecorator 종료 resultTime={}ms", resultTime);
return result;
}
}
//Concrete Decorator - Message 포매팅이 추가된 데코레이터
public class MessageDecorator extends Decorator{
public MessageDecorator(Component component){
super(component);
}
@Override
public String request() {
log.info("MessageDecorator 실행");
String request = component.request();
String decoResult = "*****" + request + "*****";
log.info("MessageDecorator 적용 전={}, 적용 후={}", request, decoResult);
return decoResult;
}
}
//Client
public class Client {
private final Component component;
public void execute() {
String result = component.request();
log.info("result={}",result);
}
}
//사용
RealComponent realComponent = new RealComponent();
MessageDecorator messageDecorator = new MessageDecorator(realComponent);
TimeDecorator timeDecorator = new TimeDecorator(realComponent);
Client timeClient = new Client(timeDecorator); //시간을 측정하는 데코레이터만 사용
Client messageClient = new Client(messageDecorator); //메세지 포매팅을 위한 데코레이터만 사용
timeClient.execute();
messageClient.execute();
장점
- 기존 코드를 변경하지 않고 기능을 추가할 수 있음
- 기존 객체에 새로운 기능을 추가하는 데 필요한 코드를 중복하지 않고 재사용할 수 있음
- 단순한 인터페이스로 기능을 추가 가능
단점
- 여러 개의 데코레이터를 사용하면 코드의 구조가 복잡해짐
- 데코레이터가 여러 개 중첩되면 호출 스택이 깊어져 성능이 저하될 수 있음
정리 - 사용 목적
- Proxy: 접근 제어, 지연 로딩 등의 목적으로 사용
- Decorator: 동적 기능 추가 목적으로 사용
스프링 핵심 원리 - 고급편 강의 | 김영한 - 인프런 강의 내용 참고
728x90
'백엔드 > Spring' 카테고리의 다른 글
[Spring] ProxyFactory (1) | 2024.11.06 |
---|---|
[Spring] 동적 프록시 (0) | 2024.11.05 |
[Spring] Strategy & Template Callback (0) | 2024.11.01 |
[Spring] Template Method (0) | 2024.10.31 |
[Spring] Thread Pool & ThreadLocal (0) | 2024.10.30 |