이 글의 내용은 김영한님의 스프링 핵심 원리 - 고급편 내용을 정리하였다.
AOP들어가기 전 프록시에 대한 정리
프록시의 주요 기능
- 접근 제어
- 권한에 따른 접근 차단
- 캐싱
- 지연 로딩
- 부가 기능 추가
- 원래 서버가 제공하는 기능에 더해서 부가 기능을 수행
- 요청 값이나, 응답 값을 중간에 변형한다.
- 실행시간을 측정해서 추가 로그를 남긴다.
- 원래 서버가 제공하는 기능에 더해서 부가 기능을 수행
클라이언트와 서버라고 하면 보통 서버 컴퓨터를 생각하게 된다. 하지만 클라이언트와 서버의 개념은 상당히 넓게 사용된다.
클라이언트는 의뢰인, 서버는 서비스나 상품을 제공하는 사람이나 물건을 뜻한다.
이 개념을 객체에 도입하면, 요청하는 객체는 클라이언트가 되고, 요청을 처리하는 객체는 서버가 된다.
클라이언트가 요청한 결과를 서버에 직접 요청한는 것이 아니라 어떤 대리자(Proxy) 를 통해 간접적으로 서버에 요청할 수 있다.
- 프록시 객체가 중간에 있으면 '접근 제어'와 '부가 기능 추가'를 수행할 수 있다.
여기까지가 프록시에 대한 짧은 개념이다.
GOF 디자인 패턴에서는 프록시를 사용한 프록시 패턴과 데코레이터 패턴이 존재하는데 잠깐 관련 패턴을 살펴보자.
디자인 패턴 책을 보며 혼자 공부할 때는 데코레이터 패턴이 프록시 개념에서 나온지 모르고 외웠던 기억이 나는데, 강의를 보다보니 프록시 / 데코레이터 패턴은 뿌리가 갖고(프록시) 클래스 다이어그램도 상당히 비슷한 것을 볼 수 있다. 데코레이터 패턴에서 Decorator 추상클래스를 일반 class로 만들어 기능별로 추가한다면 프록시 패턴과 똑같다.
프록시 패턴 - 접근 제어가 목적
프록시 패턴을 이용하여 간단한 캐시를 적용
- 캐시도 접근 자체를 제어하는 기능 중 하나
Subject.java
public interface Subject {
String doAction();
}
Proxy.java
@Slf4j
public class Proxy implements Subject{
private Subject subject;
private String data;
public Proxy(Subject subject) {
this.subject = subject;
}
public String doAction() {
log.info("Proxy 호출");
if (data == null) {
data = subject.doAction();
}
return data;
}
}
RealSubject.java
@Slf4j
public class RealSubject implements Subject {
@Override
public String doAction() {
log.info("실제 객체 호출");
return "data";
}
}
Client.java
public class Client {
private Subject subject;
public Client(Subject subject) {
this.subject = subject;
}
public void doAction() {
subject.doAction();
}
}
@Test
void noProxyTest() {
Client client = new Client(new Proxy(new RealSubject()));
client.doAction();
client.doAction();
client.doAction();
}
- RealSubject 코드와 클라이언트 코드를 변경하지 않고, 프록시를 도입해서 접근 제어가 가능함
- 클라이언트 코드의 변경 없이 자유롭게 프록시를 넣고 뺄 수 있음
- 클라이언트 입장에서는 프록시 객체가 주입되었는지, 실제 객체가 주입되었는지 알지 못함
- 클라이언트가 실제 객체가 아닌 인터페이스를 호출하면서 객체간의 결합도를 낮춤
- 실제 객체는 캡슐화? 되어있음
데코레이터 패턴 - 새로운 기능 추가가 목적
데코레이터 패턴은 프록시 패턴과 유사하지만 패턴을이 만들어진 의도(intent)는 부가 기능 추가이다.
- 프록시로 부가 기능을 추가하는 것이 데코레이터 패턴이다.
Component.java
public interface Component {
String operation();
}
ConcreteComponent.java
@Slf4j
public class ConcreteComponent implements Component {
@Override
public String operation() {
log.info("ConcreteComponent 호출");
return "data";
}
}
Decorator.java
public abstract class Decorator implements Component{
protected Component component;
public Decorator(Component component) {
this.component = component;
}
}
LogDecorator.java (ConcreteDecorator)
@Slf4j
public class LogDecorator extends Decorator {
public LogDecorator(Component component) {
super(component);
}
@Override
public String operation() {
log.info("LogDecorator 호출");
String result = component.operation();
log.info("로그를 남긴다.");
log.info("LogDecorator 호출 끝");
return result;
}
}
TimeDecorator.java (ConcreteDecorator)
@Slf4j
public class TimeDecorator extends Decorator {
public TimeDecorator(Component component) {
super(component);
}
@Override
public String operation() {
log.info("TimeDecorator 호출");
long startTime = System.currentTimeMillis();
String result = component.operation();
long endTime = System.currentTimeMillis();
long resultTime = endTime - startTime;
log.info("TimeDecorator 호출 끝 resultTime = {}ms", resultTime);
return result;
}
}
DecoratorPatternClient.java
@Slf4j
public class DecoratorPatternClient {
private Component component;
public DecoratorPatternClient(Component component) {
this.component = component;
}
public void execute() {
String result = component.operation();
log.info("result={}", result);
}
}
@Test
void decorator() {
DecoratorPatternClient client = new DecoratorPatternClient(new TimeDecorator(new LogDecorator(new ConcreteComponent())));
client.execute();
}
- 객체에 추가 책임(기능)을 동적으로 추가
'프로그래밍 노트 > SPRING' 카테고리의 다른 글
[Spring] 프록시팩토리_1 (feat. Advice) (0) | 2022.09.28 |
---|---|
[Spring] 동적 프록시 기술(feat. 리플렉션) (0) | 2022.09.27 |
[Spring] 스프링의 트랜잭션 기술 (0) | 2022.04.16 |
Master/Slave DB 라우팅/이용하기(feat. AbstractRoutingDataSource) (0) | 2022.04.03 |
[SpringBatch] JobParameter와 Scope (0) | 2021.12.01 |