사실이 아니라 공부한 내용과 생각을 정리한 글입니다. 언제든 가르침을 주신다면 감사하겠습니다.
해당 글은 김영한 님의 인프런 강의 스프링 핵심 원리 - 고급 편 을 학습하며 정리한 내용입니다.
지난 글에서 전략 패턴과 한계점에 대해 정리했습니다.
(https://ksh-dev.tistory.com/62)
이번 글에서는 전략 패턴의 한계를 극복하기 위한 또 다른 패턴인 프락시 패턴 & 데코레이터 패턴에 대해 알아보겠습니다.
1. 해결하고자 하는 문제
기존 핵심 비즈니스 로직을 전혀 수정하지 않고 부가 기능을 추가하고 싶습니다.
2. 문제를 푸는 방식
그림만 보면 Context 가 Strategy 인터페이스 타입을 참조하고 있고 DI를 통해 의존관계를 조립 후 사용한다는 면에서 전략 패턴과 매우 유사한 형태임을 알 수 있습니다.
그런데, 여기서 가장 큰 차이는 Proxy는 Server 클래스를 참조하고 있다는 점 입니다.
즉, 런타임 의존관계는 아래와 같습니다.
런타임 의존관계를 보면 전략 패턴과의 차이를 확실히 알 수 있습니다.
프락시 패턴의 경우 런타임에 client 가 바라보는 구현채는 proxy이고 proxy는 내부적으로 server 객체를 참조하는 형태를 갖습니다.
여기서 핵심은 client는 proxy를 참조하는지 server를 참조하는지 전혀 몰라야 한다는 점입니다.
Java에서는 이를 상속을 통해 구현할 수 있습니다.
인터페이스를 사용하던가 클래스 자체를 상속한 proxy를 만들 수 있습니다.
proxy를 사용하면 client와 server 사이의 부가적인 작업을 proxy를 통해 수행할 수 있습니다.
일반적으로 프락시를 통해 접근 제어(권한에 따른 접근 차단 & 캐싱 & 지연 로딩)를 달성한다면 이는 프록시 패턴으로 불리고, 부가 기능 추가를 달성한다면 데코레이터 패턴으로 불립니다.
3. 예제 코드를 통한 이해
최초에 순수 비즈니스 로직이 아래와 같이 있었다고 가정합니다.
@Slf4j
public class PureLogic {
public void logic(){
log.info("순수 비즈니스 로직");
}
}
여기에 프록시를 적용해 실행시간을 로깅하는 부가 기능을 기존 순수 비즈니스 로직의 변경 없이 추가해 보겠습니다.
우선 PureLogic을 상속받아 아래와 같이 구현합니다.
@Slf4j
public class PureLogicProxy extends PureLogic{
private final PureLogic target;
public PureLogicProxy(PureLogic target){
super();
this.target = target;
}
public void logic(){
long startTime = System.currentTimeMillis();
//비즈니스 로직 실행
target.logic(); //위임
//비즈니스 로직 종료
long endTime = System.currentTimeMillis();
long resultTime = endTime - startTime;
log.info("resultTime={}", resultTime);
}
}
이제 클라이언트 코드는 아래와 같습니다.
public class Client {
private final PureLogic pureLogic;
public Client(PureLogic pureLogic){
this.pureLogic = pureLogic;
}
public void doLogic(){
pureLogic.logic();
}
}
클라이언트 클래스는 PureLogic 클래스만 알고 있고 Proxy는 전혀 모릅니다.
그리고 PureLogic 구현채를 생성자를 통해 외부에서 주입받고 있습니다.
클라이언트 테스트 코드는 다음과 같습니다.
public class ClientTest {
@Test
public void clientTest(){
Client client = new Client(new PureLogicProxy(new PureLogic()));
client.doLogic();
}
}
Client 클래스를 통해 객체 생성 시 DI를 통해 PureLogic의 Proxy 객체를 주입하고 있습니다.
이런 과정을 통해 기존 PureLogic 클래스의 코드를 전혀 변경하지 않고 부가 기능을 확장할 수 있었습니다.
4. 한계점
프락시를 사용하기 위해 기존 모든 클래스로 부터 상속이나 인터페이스를 통해 프록시를 만들고 적용 가능한 구조로 변경해야 합니다.
기존 클래스가 천 개라면 프록시 클래스를 천개 만들어야 하는 상황입니다.
메서드 실행시간을 로깅하기 위한 코드는 모두 동일한데 천 개의 프락시를 만들고 천개의 로깅 코드를 작성해야 합니다.
프록시 클래스를 하나만 만들고 모든 곳에 적용할 수 있는 방법이 없을까요?
다음 글에서는 동적 프락시에 대해 학습합니다.
'Design Pattern' 카테고리의 다른 글
전략 패턴 (0) | 2022.04.18 |
---|---|
템플릿 메소드 패턴 (0) | 2022.04.18 |
댓글