검색결과 리스트
글
스프링 핵심 원리 - 고급편 9
인프런 강의 68일차.
- 스프링 핵심 원리 - 고급편 (김영한 강사님)
- 반드시 한번은 정복해야할 쉽지 않은 내용들
- 크게 3가지 고급 개념을 학습
1. 스프링 핵심 디자인 패턴
> 템플릿 메소드 패턴
> 전략 패턴
> 템플릿 콜백 패턴
> 프록시 패턴
> 데코레이터 패턴
2. 동시성 문제와 쓰레드 로컬
> 웹 애플리케이션
> 멀티쓰레드
> 동시성 문제
3. 스프링 AOP
> 개념, 용어정리
> 프록시 - JDK 동적 프록시, CGLIB
> 동작 원리
> 실전 예제
> 실무 주의 사항
- 기타
> 스프링 컨테이너의 확장 포인트 - 빈 후처리기
> 스프링 애플리케이션을 개발하는 다양한 실무 팁
- 타입 컨버터, 파일 업로드, 활용, 쿠키, 세션, 필터, 인터셉터, 예외 처리, 타임리프, 메시지, 국제화, 검증 등등
4. 프록시 패턴과 데코레이터 패턴
4.7. 데코레이터 패턴 - 예제 코드1
- 데코레이터 패턴에 대해 이해하기
- 프록시 패턴과 동일한 형태로 구현
package hello.proxy.puerproxy.decorator.code;
public interface Component {
String operation();
}
- hello.proxy.puerproxy.decorator.code.Component.java
- 예제이므로 단순 메소드 하나만 구현
package hello.proxy.puerproxy.decorator.code;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class RealComponent implements Component {
@Override
public String operation() {
log.info("RealComponent 실행");
return "data";
}
}
- hello.proxy.puerproxy.decorator.code.RealComponent.java
package hello.proxy.puerproxy.decorator;
import hello.proxy.puerproxy.decorator.code.DecoratorPatternClient;
import hello.proxy.puerproxy.decorator.code.RealComponent;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
@Slf4j
public class DecoratorPatternTest {
@Test
void noDecorator() {
RealComponent realComponent = new RealComponent();
DecoratorPatternClient client = new DecoratorPatternClient(realComponent);
client.execute();
}
@Test
void Decorator() {
RealComponent realComponent = new RealComponent();
DecoratorPatternClient client = new DecoratorPatternClient(realComponent);
client.execute();
}
}
- hello.proxy.puerproxy.decorator.DecoratorPatternTest.java
- 테스트 코드는 client -> realComponent 의 의존관계를 설정하고, client.execute() 를 호출한다.
//실행 결과
RealComponent - RealComponent 실행
DecoratorPatternClient - result=data
4.8. 데코레이터 패턴 - 예제 코드2
- 프록시로 부가 기능을 추가하는 것을 데코레이터 패턴이라 한다 (서버가 제공하는 기능에 더해서 부가 기능을 수행)
> 요청 값이나, 응답 값을 중간에 변형한다.
> 실행 시간을 측정해서 추가 로그를 남긴다.
- 응답 값을 꾸며주는 데코레이터 프록시를 만들어보자.
package hello.proxy.puerproxy.decorator.code;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class MessageDecorator implements Component {
private Component component;
public MessageDecorator(Component component) {
this.component = component;
}
@Override
public String operation() {
log.info("MessageDecorator 실행");
String result = component.operation();
String decoResult = "*****" + result + "*****";
log.info("MessageDecorator 꾸미기 적용 전 ={} ,적용 후 ={}", result, decoResult);
return decoResult;
}
}
- hello.proxy.puerproxy.decorator.code.MessageDecorator.java
public class DecoratorPatternTest {
...
@Test
void decorator1() {
RealComponent realComponent = new RealComponent();
MessageDecorator messageDecorator = new MessageDecorator(realComponent);
DecoratorPatternClient client = new DecoratorPatternClient(messageDecorator);
client.execute();
}
}
- hello.proxy.puerproxy.decorator.DecoratorPatternTest.java
//실행 결과
MessageDecorator - MessageDecorator 실행
RealComponent - RealComponent 실행
MessageDecorator - MessageDecorator 꾸미기 적용 전=data, 적용 후=*****data*****
DecoratorPatternClient - result=*****data*****
- MessageDecorator 가 RealComponent 의 응답 메시지를 꾸며서 반환한 것을 확인할 수 있다.
4.9. 데코레이터 패턴 - 예제 코드3
- 실행 시간을 측정하는 데코레이터 구현
package hello.proxy.puerproxy.decorator.code;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class TimeDecorator implements Component {
private Component component;
public TimeDecorator(Component component) {
this.component = 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 null;
}
}
- hello.proxy.puerproxy.decorator.code.TimeDecorator.java
public class DecoratorPatternTest {
...
@Test
void decorator2() {
RealComponent realComponent = new RealComponent();
MessageDecorator messageDecorator = new MessageDecorator(realComponent);
TimeDecorator timeDecorator = new TimeDecorator(messageDecorator);
DecoratorPatternClient client = new DecoratorPatternClient(timeDecorator);
client.execute();
}
}
- hello.proxy.puerproxy.decorator.DecoratorPatternTest.java
- client -> timeDecorator -> messageDecorator -> realComponent 의 객체 의존관계를 세팅하고 실햄
//실행 결과
TimeDecorator 실행
MessageDecorator 실행
RealComponent 실행
MessageDecorator 꾸미기 적용 전=data, 적용 후=*****data*****
TimeDecorator 종료 resultTime=7ms
result=*****data*****
- 실행 결과를 보면 TimeDecorator 가 MessageDecorator 를 실행하고 실행 시간을 측정해서 출력한 것을 확인할 수 있다
4.10. 프록시 패턴과 데코레이터 패턴 정리
- Decorator 기능에 일부 중복이 있다. 꾸며주는 역할을 하는 Decorator 들은 스스로 존재할 수 없이 항상 꾸며줄 대상이 있어야 한다.
- 따라서 내부에 호출 대상인 component 를 가지고 있어야 한다. 그리고 component 를 항상 호출해야 한다.
- 이 부분이 중복이다. 이런 중복을 제거하기 위해 component 를 속성으로 가지고 있는 Decorator 라는 추상 클래스를 만드는 방법도 고민할 수 있다.
- 이렇게 하면 추가로 클래스 다이어그램에서 어떤 것이 실제 컴포넌트 인지, 데코레이터인지 명확하게 구분할 수 있다.
- 여기까지 고민한 것이 바로 GOF에서 설명하는 데코레이터 패턴의 기본 예제이다.
* 프록시 패턴 vs 데코레이터 패턴
- Decorator 라는 추상 클래스를 만들어야 데코레이터 패턴일까? 프록시 패턴과 데코레이터 패턴은 그 모양이 거의 비슷한 것 같은데?
- 패턴의 구분은 intent에 있다.
*의도(intent)
- 프록시 패턴과 데코레이터 패턴은 그 모양이 거의 같고, 상황에 따라 정말 똑같을 때도 있다.
- 디자인 패턴에서 중요한 것은 해당 패턴의 겉모양이 아니라 그 패턴을 만든 의도가 더 중요하다.
- 따라서 의도에 따라 패턴을 구분한다
* 프록시 패턴의 의도: 다른 개체에 대한 접근을 제어하기 위해 대리자를 제공
* 데코레이터 패턴의 의도: 객체에 추가 책임(기능)을 동적으로 추가하고, 기능 확장을 위한 유연한 대안 제공
> 프록시를 사용하고 해당 프록시가 접근 제어가 목적이라면 프록시 패턴이고, 새로운 기능을 추가하는 것이 목적이라면 데코레이터 패턴이 된다.
'Spring 정리' 카테고리의 다른 글
스프링 핵심 원리 - 고급편 8 (0) | 2023.05.02 |
---|---|
스프링 핵심 원리 - 고급편 7 (0) | 2023.04.30 |
스프링 핵심 원리 - 고급편 6 (0) | 2023.01.25 |
스프링 핵심 원리 - 고급편 5 (0) | 2023.01.25 |
스프링 핵심 원리 - 고급편 4 (0) | 2023.01.16 |