<Spring> 템플릿 콜백 패턴

라모스·2022년 6월 20일
0

Spring☘️

목록 보기
13/18
post-thumbnail

템플릿 콜백 패턴

전략 패턴을 학습할 때 보았던 것 처럼, 애플리케이션 의존 관계를 설정하듯 선 조립, 후 실행을 원하는 것이 아니다. 변하는 부분과 변하지 않는 부분을 분리하는 것이 목표이고 이를 위해 코드를 실행할 때 변하지 않는 템플릿이 있고, 그 템플릿 안에서 원하는 부분만 살짝 다른 코드를 실행하고 싶을 뿐이다.

Context는 변하지 않는 템플릿 역할을 한다. 변하는 부분은 파라미터로 넘어온 Strategy의 코드를 실행해서 처리한다. 이렇게 다른 코드의 인수로서 넘겨주는 실행 가능한 코드를 콜백이라 한다.

📌 콜백?
프로그래밍에서 콜백(callback) 또는 콜애프터 함수(call-after function)는 다른 코드의 인수로서 넘겨주는 실행 가능한 코드를 말한다. 콜백을 넘겨받는 코드는 이 콜백을 필요에 따라 즉시 실행할 수도 있고, 아니면 나중에 실행할 수도 있다.

쉽게 말해, 코드가 호출(call)은 되는데 코드를 넘겨준 곳의 뒤(back)에서 실행된다는 뜻이다.

  • 스프링에선 전략 패턴을 템플릿 콜백 패턴이라 한다. 전략 패턴에서 Context가 템플릿 역할을 하고, Strategy 부분이 콜백으로 넘어온다 생각하면 된다.
  • 템플릿 콜백 패턴은 GoF 디자인 패턴은 아니고, 스프링 내부에서 이런 방식을 자주 사용하기 때문에, 스프링 안에서만 이렇게 부른다. 전략 패턴에서 템플릿과 콜백 부분이 강조된 패턴이라 생각하면 된다.
  • JdbcTemplate, RestTemplate, TransactionTemplate, RedisTemplate 처럼 스프링에서 xxxTemplate 이름이 있다면 템플릿 콜백 패턴으로 만들어져 있다고 생각하면 된다.

적용 예제

public interface TraceCallback<T> {
    T call();
}

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();
            
            trace.end(status);
            return result;
        } catch (Exception e) {
            trace.exception(status, e);
            throw e;
        }
    }
}

템플릿 콜백 패턴을 적용한 OrderControllerV5는 다음과 같다.

@RestController
pubilc 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";
            }
        });
    }
}

콜백으로 익명 클래스를 전달하였는데, 람다를 사용하면 코드가 더 줄어들게 된다.

정리

템플릿 메서드 패턴, 전략 패턴, 템플릿 콜백 패턴으로 변하는 코드와 변하지 않는 코드를 분리했고, 콜백에 람다까지 적용하여 코드를 최소화 하였다.

하지만, 결국 로그 추적기를 적용하기 위해 원본 코드를 수정해야 한다는 점이 한계점이다. 클래스가 늘어나면 늘어나는 만큼 전부 수정해야한다. 이를 해결하기 위한 방식은 프록시를 활용하는 것이다.

References

profile
Step by step goes a long way.

0개의 댓글