스프링 비동기(3) - 비동기 인터셉터

2024. 3. 5. 21:56spring/비동기 처리

과제

서블릿 API로 정의한 서블릿 필터는 서블릿 웹 요청을 처리하기 전후마다 원하는 선/후처리 로직을 수행할 수 있다. 스프링 웹 애플리케이션 컨텍스트의 필터 비슷한 뭔가를 구성해 컨테이너의 장점을 이용해보자

 

또한 스프링 MVC 핸들러가 담당하는 웹 요청을 선/후처리하는 과정에서 모델 속성을 뷰에 반환하기 전 조작해보자

해결책

스프링 MVC에서는 핸들러 인터셉터를 사용해 웹 요청을 가로채고 원하는 선/후처리를 할 수 있다. 핸들러 엔터셉터는 스프링 웹 애플리케이션 컨텍스트에 구성하기 때문에 컨테이너 기능을 얼마든지 꺼내쓸 수 있고 컨테이너에 선언된 빈은 전부 다 참조 가능하다. 핸들러 인터셉터는 특정 URL로 들어오는 요청만 적용되도록 설정할 수 있다.

 

스프링 HandlerInterceptor 인터페이스에는 preHandle(), postHandle(), aftercompletion() 세 콜백 메서드가 있다. preHandle(), postHandle() 두 메서드는 각각 핸들러가 요청을 처리하기 이전에 afterCompetion() 메서드는 핸들러가 요청을 모두 처리한 이후에 (뷰가 렌더링된 이후에) 호출된다.

 

postHandle() 메서드는 핸들러가 돌려준 ModelAndView 객체에 접근할 수 있으므로 그 안에 포함된 모델 속성을 조작할 수 있다.

 

AsyncHandlerInterceptor는 비동기 처리용 인터셉터 인터페이스로, afterConcurrentHandlingStarted() 콜백 메서드가 하나 더 있다. 이 메서드는 비동기 처리를 시작하는 시점에 postHandle()이나 afterCompletion() 대신 호출된다. 비동기 처리가 다 끝나면 정상 흐름으로 복귀한다.

 

풀이

 

public class MesurementInterceptor implements AsyncHandlerInterceptor {

    public static final String START_TIME = "startTime";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                                                         Object handler) throws Exception {

        System.out.println("==========pre Handle===========");
        if (request.getAttribute(START_TIME) == null)
            request.setAttribute(START_TIME, System.currentTimeMillis());

        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response,
                           Object handler, ModelAndView modelAndView) throws Exception {

        System.out.println("==========post Handle===========");
        long startTime = (Long) request.getAttribute(START_TIME);
        request.removeAttribute(START_TIME);
        long endTime = System.currentTimeMillis();
        System.out.println("Response-Processing-Time" + (endTime - startTime) + "ms");
        System.out.println("Response-Processing-Thread" + Thread.currentThread().getName());
    }

    @Override
    public void afterConcurrentHandlingStarted(HttpServletRequest request,
                         HttpServletResponse response, Object handler) throws Exception {
        System.out.println("==========after completion===========");
        long startTime = (Long) request.getAttribute(START_TIME);
        request.setAttribute(START_TIME, System.currentTimeMillis());
        long endTime = System.currentTimeMillis();

        System.out.println("Response-Processing-Time" + (endTime - startTime) + "ms");
        System.out.println("Response-Processing-Thread" + Thread.currentThread().getName());
    }
}

 

MeasurementInterceptor 인터셉터에서

1.preHandle() 메서드는 시작 시각을 기록한다.

 

2.afterConcurrentHandlingStarted() 메서드는 앞서 보관된 시작 시각을 꺼내 비동기 요청 처리시간을 구한다. 그리고 시작 시간을 초기화 한다.

 

3.postHandle() 메서드는 요청 속성에 저장된 시작 시간을 꺼내 현재 시각가 비교해 총 소요 시간을 구한 다음 스레드명과 함께 콘솔에 출력한다.