-
템플릿 콜백 패턴 - 템플릿 패턴, 전략 패턴의 최종스프링/스프링 AOP 2023. 9. 5. 17:45
콜백의 정의
프로그래밍에서 콜백 또는 콜애프터 함수는 다른 코드의 인수로서 넘겨주는 실행 가능한 코드를 말한다.
코드가 호출이 되기는 하지만 코드를 넘겨준 곳의 뒤에서 실행된다는 뜻이다.
이전의 전략패턴에서 예시를 들면 클라이언트에게 직접 Strategy를 실행하는 것이 아니라, 클라이언트가 ContextV2.execute()를 실행할 때 Strategy를 넘겨주고, Context2 뒤에서 Strategy가 실행되는 것이다.
템플릿 콜백 패턴은 GOF 패턴은 아니고, 스프링 내부에서 이런 방식을 자주 사용하기에 스프링에서만 있는 패턴이다.
Callback 인터페이스
public interface Callback { void call(); }
Callback 인터페이스의 구현체인 TimeLogTemplate
@Slf4j public class TimeLogTemplate { public void execute(Callback callback) { long startTime = System.currentTimeMillis(); //비즈니스 로직 실행 callback.call(); //위임 //비즈니스 로직 종료 long endTime = System.currentTimeMillis(); long resultTime = endTime - startTime; log.info("resultTime={}", resultTime); } }
테스트에서의 템플릿 콜백 패턴
@Slf4j public class TemplateCallbackTest { /** * 템플릿 콜백 패턴 - 익명 내부 클래스 */ @Test void callbackV1() { TimeLogTemplate template = new TimeLogTemplate(); template.execute(new Callback() { @Override public void call() { log.info("비즈니스1 로직 실행"); } }); template.execute(new Callback() { @Override public void call() { log.info("비즈니스2 로직 실행"); } }); } }
코드를 보면 알겠지만 저번 포스트의 전략 패턴의 아래부분은 V2 버전이 템플릿 콜백 패턴이다. 공통 부분은 인터페이스의 구현체에 있고, 애플리케이션 로딩 시점에 정해지는 콜백 패턴이다. 코드는 저번 포스트와 너무 비슷하기 때문에 따로 설명을 하지는 않겠다.
실제 애플리케이션에 적용하기
1. TraceCallback 인터페이스 생성
public interface TraceCallback<T> { T call(); }
2. TraceTemplate 구현
public class TraceTemplate { private final LogTrace trace; public TraceTemplate(LogTrace trace) { this.trace = trace; } public <T> T execute(String message, TraceCallback<T> callback) { TraceStatus status = null; try { status = trace.begin(message); // 로직 호출 T result = callback.call(); // callback에서 넘어온 거에서 call 메서드 호출 trace.end(status); return result; } catch (Exception e) { trace.exception(status, e); throw e; // 예외를 꼭 다시 던져주어야 한다. } } }
3. 각 계층(컨트롤러, 서비스, 리포지토리)에 적용
@RestController public class OrderControllerV5 { private final OrderServiceV5 orderService; private final TraceTemplate template; public OrderControllerV5(OrderServiceV5 orderService, LogTrace trace) { this.orderService = orderService; this.template = new TraceTemplate(trace); } @GetMapping("v5/request") public String request(String itemId) { return template.execute("OrderController.request()", new TraceCallback<>() { @Override public String call() { orderService.orderItem(itemId); return "ok"; } }); } }
@RequiredArgsConstructor 어노테이션을 빼고 생성자로 주입해줬는데, 저렇게 해주면 TraceTemplate을 싱글톤으로 사용할 수 있기 때문이다. Service와 Repository도 같으니 Controller로 설명하겠다. 컨트롤러의 함수 안에 template.execute를 사용한다. template의 execute()메서드는 파라미터로 message와 callback을 받아오는데, 메시지와 new TraceCallback<>을 통해 전략패턴의 코드를 깔끔하고 유연하게 설계할 수 있었다.
'스프링 > 스프링 AOP' 카테고리의 다른 글
프록시 패턴 (0) 2023.09.10 프록시 - 프록시 패턴과 데코레이션 패턴이란? (0) 2023.09.10 템플릿 패턴의 한계와 전략 패턴 (0) 2023.09.05 템플릿 메서드 패턴 (0) 2023.09.03 쓰레드로컬(ThreadLocal) - 동시성 문제의 해결 (0) 2023.09.03