-
템플릿 메서드 패턴스프링/스프링 AOP 2023. 9. 3. 23:38
@GetMapping("v3/request") public String request(String itemId) { TraceStatus status = null; try { status = trace.begin("OrderController.request()"); orderService.orderItem(itemId); // 핵심 로직 trace.end(status); return "ok"; } catch (Exception e) { trace.exception(status, e); throw e; } }
원래는 로직이 orderService.orderItem(itemId) 하나뿐인데 로그 추적기를 위해서 코드가 너무 복잡해졌다. 또한, Service와 Repository 계층에서도 같은 코드가 계속해서 반복된다.
템플릿 메서드 패턴은 핵심 기능과 부가 기능(로그 추적기)를 분리하는 디자인 패턴이다.
템플릿 메서드 패턴은 이름 그대로 템플릿을 사용하는 방식인데, 변하지 않는 부분을 모아둔 다음에 일부의 변하는 부분을 별도로 호출해서 해결하는 패턴이다.
@Slf4j public abstract class AbstractTemplate { public void execute() { long startTime = System.currentTimeMillis(); // 비즈니스 로직 실행 call(); // 상속 // 비즈니스 로직 종료 long endTime = System.currentTimeMillis(); long resultTime = endTime - startTime; log.info("resultTime={}", resultTime); } protected abstract void call(); }
AbstractTemplate이라는 추상 클래스를 만들었다. 반복되는 변하지 않는 로직들을 execute()에 넣은 다음에 call() 메서드를 통해서 각각 변하는 부분의 로직을 넣어주면 된다.
템플릿 메서드 패턴은 부모 클래스에 변하지 않는 템플릿 코드를 넣어주고 변하는 부분은 자식 클래스에서 상속과 오버라이딩을 사용해서 처리한다.
@Slf4j public class SubClassLogic1 extends AbstractTemplate { @Override protected void call() { log.info("비즈니스 로직1 실행"); } }
@Slf4j public class SubClassLogic2 extends AbstractTemplate { @Override protected void call() { log.info("비즈니스 로직2 실행"); } }
오버라이딩된 call()메서드를 구현하면 각각 필요한 메서드가 execute()에 들어가서 실행된다.
@Test void templateMethodV1() { AbstractTemplate template1 = new SubClassLogic1(); template1.execute(); AbstractTemplate template2 = new SubClassLogic2(); template2.execute(); }
코드의 중복이 없이 SubClassLogic 구현체를 통해서 비즈니스 로직을 설정한 다음 template.execute()로 동일한 부분을 호출할 수 있다.
익명 내부 클래스 활용
@Test void templateMethodV2() { AbstractTemplate template1 = new AbstractTemplate() { @Override protected void call() { log.info("비즈니스 로직1 실행"); } }; template1.execute(); AbstractTemplate template2 = new AbstractTemplate() { @Override protected void call() { log.info("비즈니스 로직2 실행"); } }; template2.execute(); }
별도로 구현 클래스를 만들지 않고 익명 내부 클래스로 만들어서 사용할 수도 있다.
템플릿 패턴의 실제 적용
테스트 코드가 아니라 실제 웹에서의 템플릿 패턴을 적용한 모습이다.
AbstractTemplate이라는 추상 클래스를 만들어준다.
public abstract class AbstractTemplate<T> { private final LogTrace trace; public AbstractTemplate(LogTrace trace) { this.trace = trace; } public T execute(String message) { TraceStatus status = null; try { status = trace.begin(message); // 로직 호출 T result = call(); trace.end(status); return result; } catch (Exception e) { trace.exception(status, e); throw e; // 예외를 꼭 다시 던져주어야 한다. } } protected abstract T call(); }
Controller
@RestController @RequiredArgsConstructor public class OrderControllerV4 { private final OrderServiceV4 orderService; private final LogTrace trace; @GetMapping("v4/request") public String request(String itemId) { AbstractTemplate<String> template = new AbstractTemplate<String>(trace) { @Override protected String call() { orderService.orderItem(itemId); return "ok"; } }; return template.execute("OrderController.request("); } }
컨트롤러만 봐도 코드가 훨씬 단순해진 것을 확인할 수 있다. 익명 내부클래스를 제외하면 한줄뿐이다.
Service와 Repository에도 동일하게 코드를 작성하면 된다.
'스프링 > 스프링 AOP' 카테고리의 다른 글
템플릿 콜백 패턴 - 템플릿 패턴, 전략 패턴의 최종 (0) 2023.09.05 템플릿 패턴의 한계와 전략 패턴 (0) 2023.09.05 쓰레드로컬(ThreadLocal) - 동시성 문제의 해결 (0) 2023.09.03 로그 추적기 필드 동기화 - 동시성 문제 발생 (0) 2023.09.03 로그 추적기 (0) 2023.09.03