Interceptor란 컨트롤러의 핸들러 메소드 호출 전 후에 특정 작업을 수행하도록 지정하는 녀석입니다.

 

메소드의 호출 전 후에 특정 작업 수행?? 어 ? 이거 완전... AOP 아닌가요?

 

AOP와 비슷하게 작동하지만 다른 목적을 가지고 있다고 하는데, 자주 사용하면서 차이점을 느껴봐야 할 것 같습니다.


인터셉터를 만들어 봅시다

 

인터셉터는 HandlerInterceptor 인터페이스를 구현해서 만들 수 있습니다. 

 

HandlerInterceptor를 구현한 뒤 ctrl+space를 누르면 해당 인터페이스의 오버라이딩 해야하는 메서드들이 나오니 그것들을 모두 구현해주도록 합시다.

 

package com.duljji.springweb.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class MyInterceptor implements HandlerInterceptor  {
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		System.out.println("preHandle 실행");
		
		return false;
	}
	
	
	
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		System.out.println("postHandle 실행");
		
	}
	
	
	
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		System.out.println("afterCompletion 실행");
		
	}
}

이렇게 3개의 메서드를 구현해야 하는군요. 

 

이제 이 인터셉터가 어떤 Controller를 매핑할 때 동작하게 할 지 추가를 해주어야 합니다.

 

Controller를 매핑해주는 곳은 servlet이니 servlet-context.xml에 가서 추가를 해줍니다.

<interceptors>
    <interceptor>
        <mapping path="/*"/>
        <beans:bean class="com.duljji.springweb.interceptor.MyInterceptor" />
    </interceptor>
</interceptors>

interceptors를 추가해주고  해당 interceptor가 작동할 url을 매핑해주고, 인터셉터 빈을 생성해 의존성을 주입하면?

 

이제 스프링이 알아서 핸들러 메서드를 실행시키기 전 후에 인터셉터를 호출할겁니다.

url에 접근하려고 한다면 preHandle이 실행되고 preHandle값이 false라면 그 이후로 진행이 되지 않습니다.

 

현재 페이지가 나오지 않는 걸 확인 해 볼 수 있는데요.

 

response.sendReidrect 같은 메서드들을 이렇게 빈 페이지를 보여주지 않고 다른 곳으로 돌리는 작업을 할 때 용이할 것 같습니다.

 

preHandle 메서드의 리턴값을 true로 바꾸니 postHandle과 afterCompletion 모두 잘 실행이 되는 모습을 보여줍니다.

 

postHandle은 Viewresolver가 뷰를 렌더링 하기 이전에 호출되는 메서드이며,

 

afterCompletion은 Viewresolver가 뷰를 렌더링 한 이후에 호출되는 메서드입니다.

 

간단하게 About Page를 만들고 해당 페이지는 로그인을 한 유저만 들어갈 수 있게 만들어봅시다.

 

package com.duljji.springweb;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.duljji.springweb.dto.Member;

/**
 * Handles requests for the application home page.
 */
@Controller
public class HomeController {
	
	
	/**
	 * Simply selects the home view to render by returning its name.
	 */
	@GetMapping("/")
	public String index() {
		return "base";
	}
	
	
	@GetMapping("/home")
	public String home() {
		return "home";
	}
	
	@GetMapping("/about")
	public String about() {
		return "about";
	}
	
}

Spring MVC에서는 RequestMapping으로 method를 지정해주어도 되지만 버전이 업데이트 되면서 @GetMapping으로 바로 Get method를 받아줄 수도 있게 되었습니다.

 

만약 @GetMapping이 제대로 작동하지 않는다면 pom.xml에 들어가셔서 Spring 버전을 올려주셔야 합니다.

 

About 페이지로 이동을 하도록 만들었고, 인터셉터도 여전히 잘 작동하는 모습입니다.

 

그럼 이제 about페이지를 이동할 때만 인터셉터를 작동하게 만들고, 만약 로그인이 되어있지 않다면 메인페이지를 다시 띄워주게끔 만들어 보겠습니다.

 

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans
	xmlns="http://www.springframework.org/schema/mvc"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
		http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

	<!-- DispatcherServlet Context: defines this servlet's request-processing 
		infrastructure -->

	<!-- Enables the Spring MVC @Controller programming model -->
	<annotation-driven />

	<!-- Handles HTTP GET requests for /resources/** by efficiently serving 
		up static resources in the ${webappRoot}/resources directory -->
	<resources mapping="/resources/**" location="/resources/" />

	<!-- Resolves views selected for rendering by @Controllers to .jsp resources 
		in the /WEB-INF/views directory -->
	<beans:bean
		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<beans:property name="prefix" value="/WEB-INF/views/" />
		<beans:property name="suffix" value=".jsp" />
	</beans:bean>

	<context:component-scan
		base-package="com.duljji.springweb" />

	<interceptors>
		<interceptor>
			<mapping path="/about"/>
			<beans:bean class="com.duljji.springweb.interceptor.MyInterceptor" />
		</interceptor>
	</interceptors>


</beans:beans>

이제 /about 으로 접근하려고 할 때 해당 인터셉터가 동작을 하게 됩니다.

 

우리는 로그인이 되어있는지를 체크하여 해당 페이지로의 접근을 막거나,

 

다른 페이지로 이동하게끔 할 수 있습니다.

 

인터셉터를 사용하면 애초에 controller에 오는 것 자체를 막아버릴 수가 있게 되는 겁니다.

package com.duljji.springweb.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

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

		HttpSession session = request.getSession();
		if(session.getAttribute("id") == null) {
			response.sendRedirect("/home");
			return false;
		}
		return true;
	}
	
	
	
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		System.out.println("postHandle 실행");
		
	}
	
	
	
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		System.out.println("afterCompletion 실행");
		
	}
}

이런식으로 말이죠.

 

그럼 로그인 기능을 아직 만들지 않았으니 About페이지에 들어가도

 

Home  페이지로 들어가지는 걸 확인할 수 있습니다.

 

그런데 문득 궁금해집니다.

 

sendRedirect로 페이지를 이동시켰을 때 이후의 인터셉터들을 작동을 할까요?

 

과연 return false가 아니라 return true로 true값을 반환한다면 어떻게 될까요?

 

!!!!

 

이후의 인터셉터들도 작동을 하는걸 볼 수 있습니다. 컨트롤러와 이후의 메서드들의 작동도 막기 위해서는 false를 잘 리턴해주어야 한다는 걸 알 수 있었습니다.

 

그 말은 sendRedirect로 페이지를 렌더링 해주어도, 핸들러 매핑까지 작동이 이어진다는거네요.

 

신기한 스프링의 interceptor였습니다.

+ Recent posts