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였습니다.
'백엔드 > Spring' 카테고리의 다른 글
스프링 일기 12 - JPA(3) Create2 DTO 분석하기 (0) | 2023.06.16 |
---|---|
스프링 일기 8 - JPA(1) (SQL과 OOP 자바의 패러다임 불일치) (0) | 2023.06.14 |
스프링 일기 6 - Spring Legacy Project 만들어보기 (0) | 2023.06.13 |
스프링 일기 5 - Web MVC패턴 만들어보기 (0) | 2023.06.13 |
스프링 일기 4 - 컨테이너한테 AOP 시키기 (0) | 2023.06.13 |