Spring AOP 로드맵
AOP를 학습하기 위해 김영한 강사님의 Spring 고급 편 강의를 듣고 있는데, AOP를 보다 더 완벽하게 이해하기 위해서는 아래의 단계를 따라야 한다.
학습해야 할 내용이 방대하지만 차근차근 나아가보자.
이번 글에서는 Spring이 사용하는 디자인 패턴 중 전략 패턴 그중에서도 템플릿 메서드 패턴을 다룬다.
1. 동시성 문제 - 쓰레드 로컬
2. 전략 패턴 - 템플릿 메서드 패턴과 콜백 패턴
3. 프록시 - 프록시 패턴과 데코레티어 패턴
4. 동적 프록시
5. Spring 지원 동적 프록시
6. 빈 후처리기
7. @Aspect AOP
8. Spring AOP
템플릿 메서드 패턴(Template Method Pattern)
등장 배경
템플릿 메서드 패턴은 반복되는 작업의 구조를 표준화할 필요성에서 출발한다. 코드 중복과 유지보수의 어려움을 해결하기 위해, 공통적인 프로세스의 흐름을 정의하고 그 중 변화가 필요한 부분만 서브클래스에서 확장할 수 있도록 한 디자인 패턴이다.
템플릿 메서드 패턴은 GOF Design 패턴의 하나로, 알고리즘의 골격을 정의하는 메서드를 하나 가지며, 일부 단계는 서브클래스에서 구현할 수 있도록 한다.
이 방식을 통해 알고리즘의 구조를 변경하지 않고도 특정 단계를 재정의할 수 있게 된다.
즉, 공통의 프로세스를 담은 메서드의 구조를 변경하지 않으면서, 특정 단계의 내용을 서브클래스에서 다르게 구현할 수 있게 하는 디자인 패턴이다.
강의에서의 로그 추적기 예제로 템플릿 메서드 패턴의 기본에 대해서 다룬다.
로그 추적기를 도입하기 전, 코드는 핵심 기능에만 집중되어 있었으나, 도입 후에는 로그를 출력하는 부가 기능 코드가 추가되어 복잡성이 증가한다. 이 문제를 템플릿 메서드 패턴을 통해 해결할 수 있다. 예제로 살펴보자.
로그 추적기 도입 전 코드 (V0)
@GetMapping("/v0/request")
public String request(String itemId) {
orderService.orderItem(itemId);
return "ok";
}
로그 추적기 도임 후 코드 (V3)
@GetMapping("/v3/request")
public String request(String itemId) {
TraceStatus status = null;
try {
status = trace.begin("OrderController.request()");
orderService.orderItem(itemId); // 핵심 기능
trace.end(status);
} catch (Exception e) {
trace.exception(status, e);
throw e;
}
return "ok";
}
템플릿 메서드 패턴의 적용
위와 같은 클래스 들이 1개라면 상관이 없지만 모두 똑같은 상황에서 로그를 추적해야하는 클래스가 100개라면? 모두 고칠 수 없다. 그렇기 때문에 공통된 부분을 추상 클래스(템플릿 역할)를 활용하여 해결할 수 있다.
변하지 않는 부분, 즉 로그 추적 로직을 상위 클래스에 정의하고, 변하는 부분인 핵심 기능을 서브클래스에서 구현한다.
이를 통해 코드의 중복을 최소화하며, 유지보수성과 확장성을 향상시킬 수 있다.
추상 클래스 정의 (템플릿 역할)
public abstract class AbstractTemplate {
public void execute() {
TraceStatus status = trace.begin("message");
try {
call(); // 변하는 부분, 서브클래스에서 구현
trace.end(status);
} catch (Exception e) {
trace.exception(status, e);
throw e;
}
}
protected abstract void call(); // 서브클래스에서 구현
}
서브클래스에서 구현
public class SubClassLogic1 extends AbstractTemplate {
@Override
protected void call() {
// 비즈니스 로직1 실행
}
}
템플릿 메서드 패턴을 사용하면 공통된 알고리즘의 구조를 유지하면서, 특정 단계의 구현을 서브클래스에게 위임하여 각 서브클래스에서 다양한 방식으로 해당 단계를 구현할 수 있게 된다.
이제 이를 활용해보자.
아래는 컨트롤러와 서비스에 템플릿 메서드를 적용한 모습이다.
/* Controller */
@RestController
@RequiredArgsConstructor
public class OrderControllerV4 {
private final OrderServiceV4 orderServiceV4;
private final LogTrace trace;
@GetMapping("/v4/request")
public String request(String itemId) {
// template method pattern
// 객체를 생성하면서 바로 자식클래스 생성
AbstractTemplate<String> template = new AbstractTemplate<>(trace) {
@Override
protected String call() {
orderServiceV4.orderItem(itemId);
return "ok";
}
};
return template.execute("OrderController.request()");
}
}
/* Service */
@Service
@RequiredArgsConstructor
public class OrderServiceV4 {
private final OrderRepositoryV4 orderRepositoryV4;
private final LogTrace trace;
public void orderItem(final String itemID) {
AbstractTemplate<Void> template = new AbstractTemplate<>(trace) {
@Override
protected Void call() {
orderRepositoryV4.save(itemID);
return null;
}
};
template.execute("OrderServiceV4.orderItem()");
}
}
디자인 패턴 없이 로그를 직접 다 추가했을 때보다는 훨씬 깔끔해졌다. 하지만 여전히 비즈니스 로직을 제외한 다른 코드들이 너무나 정신 없이 정리되어있다.
비즈니스 로직을 활용한 원본의 코드는 전혀 건들지 않고 로그 기능을 추가할 수는 없을까?
다음 글에서는 원본의 코드를 전혀 건들지 않고 추가하는 법을 프록시를 활용한 디자인 패턴으로 작성해보겠다.
'Spring > Spring AOP' 카테고리의 다른 글
Spring AOP 6. 포인트컷, 어드바이스, 어드바이저 (0) | 2024.03.20 |
---|---|
Spring AOP 5. ProxyFactory (0) | 2024.03.18 |
Spring AOP 4. 동적 프록시: JDK 동적프록시와 CGLIB (0) | 2024.03.08 |
Spring AOP 3. 디자인 패턴: 데코레이터 패턴 (0) | 2024.03.06 |
Spring AOP 2. 디자인 패턴: 프록시패턴 (0) | 2024.03.06 |