본문 바로가기
프로그래밍/스프링프레임워크

실행 흐름에 끼어들기(Filter,Interceptor,AOP) 2 - Interceptor

by pentode 2018. 4. 2.

Java 웹프로그래밍에서 실행 흐를에 끼어들 수 있는 방법에 대해 알아보고 있습니다. 앞의 글에서 서블릿 필터에 대해 알아 봤습니다. 이번에는 스프링 프레임웍에서 사용할 수 있는 인터셉터에 대해서 알아보겠습니다.

 

필터와 인터셉터는 적용 시점이 다릅니다. 필터는 스프링 프레임웍과는 무관하게 지정된 자원에 대해 동작합니다. 스프링은 Dispatcher 서블릿으로 부터 시작됨으로 필터는 스프링 컨텍스트의 바깥에 존재 하게 됩니다.

 

인터셉터는 스프링의 Dispatcher 서블릿이 컨트롤러를 호출할 때 전, 후에 끼어듭니다. 그러므로 스프링 컨텍스트 내부에 존재하게 됩니다.

 

인터셉터는 여러개를 사용할 수 있으며, 실행 순서는 <mvc:interceptors> 에 나오는 순서 입니다. 인터셉터를 주로 사용하는 곳은 로그인 체크, 권한 체크, 프로그램 실행시간 계산 작업 로그 처리, 업로드 파일 처리 등에 많이 사용됩니다.

 

파일 업로드 처리의 경우 Servlet 3.0 이전에는 아주 유용하였습니다. 그때는 Multipart 처리를 직접하거나 thrird party 라이브러리를 사용해서 처리해야 했으므로 필터 보다는 인터셉터에서 처리하는게 편리했습니다. Servlet 3.0 부터는 자체적으로 HttpServletRequest 에 Multipart 처리기능이 포함되었습니다.

 


인터셉터는 각각 특정 시점에 동작하는 네 개의 메소드를 사용해서 구현합니다.

 

1. preHandle() - 컨트롤러 메소드 실행 직전에 수행됩니다. true 를 반환하면 계속 진행이 되고  false 를 리턴하면 실행 체인(다른 인터셉터, 컨트롤러 실행)이 중지되고 반환 됩니다.  필터의 응답 처리가 있다면 그것은 실행이 됩니다.

 

2. postHandle() - 컨트롤러 메소드 실행 직후에 실행 됩니다. View 페이지가 렌더링 되기전에   ModelAndView 객체를 조작 할 수 있습니다.


3. afterCompletion() -  View 페이지가 렌더링 되고 난 후 에 실행됩니다.


4. afterConcurrentHandlingStarted() - Servlet 3.0 부터 비동기 요청이 가능해 졌습니다.   비동기 요청시 postHandle와 afterCompletion 은 실행되지 않고, 이 메소드가 실행됩니다.

 

 

예제를 보도록 하겠습니다.

 

1. 인터셉터를 만듭니다. src/main/java/com/tistory/pentode/interceptor/FirstInceptor.java 파일 입니다.

package com.tistory.pentode.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

public class FirstInterceptor extends HandlerInterceptorAdapter {
	private static final Logger logger = LoggerFactory.getLogger(FirstInterceptor.class);

	// 컨트롤러 실행 직전에 수행됩니다.
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

		logger.info("preHandle call......");

		if (handler instanceof HandlerMethod) {
			// HandlerMethod 는 후에 실행할 컨트롤러의 메소드 입니다.
			// 메소드명, 인스턴스, 타입, 사용된 Annotation 들을 확인할 수 있습니다.
			HandlerMethod method = (HandlerMethod) handler;
			logger.info("handler method name : " + method.getMethod().getName());
		}

		return true;
	}

	// 컨트롤러 실행 직후에 수행됩니다.
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
	logger.info("postHandle call......");
	}

	// View 렌더링이 끝난 직후에 수행됩니다.
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
		logger.info("afterCompletion call......");
	}

	// 비동기 호출시 수행됩니다.
	@Override
	public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		logger.info("afterConcurrentHandlingStarted call......");
	}
}

 

2. 인터셉터를 등록합니다. /WEB-INF/spring/appServlet/servlet-context.xml 파일 입니다.

 

<interceptors>
	<interceptor>
		<!-- 인터셉터가 적용될 URL 입니다. -->
		<mapping path="/*.do" />
		<!-- 인터셉터가 제외될 URL 입니다. -->
		<exclude-mapping path="/login.do"/>
		<!-- 적용될 인터셉터를 지정합니다. -->
		<beans:bean id="firstInterceptor" class="com.tistory.pentode.interceptor.FirstInterceptor" />
	</interceptor>
</interceptors>

 

적용 제외 URL 이 먼저 평가되고, 다음에 적용 URL이 평가 됩니다.

 

 

3. 테스트에 사용된 컨트롤러와 JSP 페이지 입니다. src/main/java/com/tistory/pentode/TestController.java 파일 입니다.

 

package com.tistory.pentode;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class TestController {
	private static final Logger logger = LoggerFactory.getLogger(TestController.class);

	@RequestMapping(value = "/firstInterceptor.do", method = RequestMethod.GET)
	public String firstInterceptor(Model model) {
		logger.info("call firstInterceptor.do");
        
		return "firstInterceptor";
	}
}

 

/WEB-INF/views/firstInterceptor.jsp 파일 입니다.

 

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	<title>First Interceptor</title>
</head>
<body>
	<h1>First Interceptor!</h1>
</body>
</html>

 

 

4. 실행 결과 입니다.

 

interceptor 실행결과

 

콘솔 출력 결과 입니다.

 

INFO : com.tistory.pentode.filter.FirstFilter - before doFilter
INFO : com.tistory.pentode.interceptor.FirstInterceptor - preHandle call......
INFO : com.tistory.pentode.interceptor.FirstInterceptor - handler method name : firstInterceptor
INFO : com.tistory.pentode.TestController - call firstInterceptor.do
INFO : com.tistory.pentode.interceptor.FirstInterceptor - postHandle call......
INFO : com.tistory.pentode.interceptor.FirstInterceptor - afterCompletion call......
INFO : com.tistory.pentode.filter.FirstFilter - after doFilter

 

인터셉터 I1, I2 가 있다면 실행 순서는 I1 pre -> I2 pre -> Controller -> I2 post -> I1 post -> I2 afterCompletion -> I1 afterCompletion 순입니다.

 

컨트롤러 실행중 예외가 발생하면 postHandle은 실행되지 않습니다. afterCompletion은 실행이 됩니다.

 

이것으로 Spring Framework 에서 Interceptor 에 대해서 알아 봤습니다. 다음에는 AOP 에 대해서 알아 봅니다.

 

반응형