Template-Callback Pattern

이창근·2023년 8월 9일
0

Spring공부

목록 보기
2/9

지난 시간에 이어서 결국 LogTrace클래스를 완성하였지만, 이를 Controller, Service, Repository에 적용함에 있어 문제가 생겼다.

OrderController.class

@RestController
@RequiredArgsConstructor
public class OrderController {

    private final OrderService orderService;
    private final LogTrace trace;

    @GetMapping("/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;
        }
    }
} 

위 코드를 보면, Controller에 LogTrace와 관련된 내용까지 적혀있는 것을 볼 수 있다. 정작 컨트롤러에서 관심 있는 코드는

orderService.orderItem(itemId);

이 한 줄 뿐이다. 이는 관심사의 분리가 되지 못한 패턴이며, 만약에 클래스가 100개, 1000개로 늘어나면 변경에 있어서도 큰 제약이 따르게 된다.

스프링에서는 이를 템플릿-콜백 패턴으로 해결한다.
템플릿-콜백 패턴을 쉽게 설명하면, 콜백은 실행 가능한 함수 조각이며, 클라이언트는 변경되는 부분만을 콜백으로 전달하여, 템플릿에게 실행하도록 위임하는 것이다. 이를 통해 클라이언트는 템플릿의 코드 구조를 알 필요가 없고, 관심사의 분리가 가능해진다.

이 패턴을 우리의 코드에 적용시켜보자.

TraceCallback.class

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

TraceTemplate.class

@RequiredArgsConstructor
public class TraceTemplate {

    private final LogTrace 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;
        }
    }
}

이 코드가 템플릿-콜백 부분이다. 위에서 본 Controller와 많이 닮아있음을 알 수 있다. 주요 관심사인 로그부분과 그 중간에 위치한 콜백 실행 부분이 있다. excute함수는 파라미터로 받은 콜백을 로그부분과 함께 실행시켜주는 것이다.
이러면 Controller는 어떻게 바뀔 수 있을까?

OrderController.class

@RestController
public class OrderController {

    private final OrderService orderService;
    private final TraceTemplate template;

    public OrderController(OrderService orderService, LogTrace logTrace) {
        this.orderService = orderService;
        this.template = new TraceTemplate(logTrace);
    }

    @GetMapping("/request")
    public String request(String itemId) {
        return template.execute("OrderController.request()", () -> {
            orderService.orderItem(itemId);
            return "ok";
        });
    }
}

LogTrace를 주입받고, 이를 통해 TraceTemplate을 초기화한다. 그러면 request함수에서는 TraceTemplate.excute() 함수와 함께 콜백만 전달해주면 되는 것이다.

template.execute("OrderController.request()", () -> {
orderService.orderItem(itemId);
            return "ok";
        });

이렇게 람다표현식으로 코드조각을 전달해주면 나머지 일은 TraceTemplate이 실행시켜주게 된다.

우리는 이 템플릿-콜백 패턴을 통해 관심사의 분리를 해냈다. 이번 시간을 통해 관심사의 분리에 대해 좀 더 깊게 생각할 수 있게 되었다.

profile
나중에 또 모를 것들 모음

0개의 댓글