본 포스팅의 예제는 STS 또는 Eclipse를 사용하지 않고 Intellij를 통해 구현하고 있습니다.
그래서 기존의 STS(Spring Tool Studio)에서 생성된 Spring 프로젝트의 스프링 관련 설정 파일명과
프로젝트 구조가 약간 다를 수 있습니다.Intellij 스프링 mvc 프로젝트 생성 포스팅을 참고해주시면 감사하겠습니다.
Spring-MVC 기본 개념 및 테스트 예제 관련 포스팅 링크
순서 | 포스팅 제목 |
1 | Intellij에서 Spring MVC Project 생성하기 |
2 | Spring MVC - MariaDB 연결테스트 |
3 | Spring MVC - Mybatis 설정 및 테스트 |
4 | SpringMVC 구조 |
5 | SpringMVC + Mybatis |
6 | Spring MVC Controller 작성 연습 |
Spring-MVC 게시판 예제 이전 포스팅 링크
1. Filter와 Interceptor의 공통점과 차이점
# 공통점
Servlet 기술의 Filter와 Spring MVC의 HandlerInterceptor는 특정 URL에 접근할떄 제어하는 용도로 사용 됩니다.
# 차이첨
실행 시점에 속하는 영역(Context)에 차이점이 있다.
● Filter
○ 동일한 웹 어플리케이션의 영역 내에서 필요한 자원들을 활용 합니다.
○ 웹 어플리케이션 내에서 동작하므로, 스프링 Context에 접근하기 어렵습니다.
● Interceptors
○ 스프링에서 관리되기 때문에 스프링 내의 모든 객체에 접근이 가능합니다.
○ 스프링 Context 내에서 존재하므로 Context 내의 모든 객체를 활용 할 수 있습니다.
HandlerInterceptor의 경우 스프링 빈으로 등록된 컨트롤러나 서비스 객체를 주입받아 사용할 수 있기 떄문에 기존의 구조를 그대로 활용하면서도 추가적인 작업이 가능합니다.
2. Spring AOp와 HandlerInterceptor의 차이
특정 객체 동작 전에 또는 후에 처리는 이미 AOP를 통해 확인해보았지만, 일반적으로 컨트롤러를 이용 할 떄에는 AOP의 BeforeAdvice등을 활용하기 보다는 HandlerInterceptor 인터페이스 또는 HandlerInterceptorAdaptor 클래스를 활용하는 경우가 더 많습니다.
- AOP의 Advice : JoinPoint나 ProceedingJoinPoint 등을 활용해서 호출 대상이 되는 메서드의 파라미터를 처리하는 방식
- Interceptor의 HandlerInterceptor : Filter와 유사하게 HttpServletRequest, HttpServletResponse를 파라미터로 받는 구조
일반적인 경우라면 Controller에서 DTO나 VO 타입을 주로 파라미터로 활용하고, Servlet API에 해당하는 HttpServletRequest와 HttpServletResponse를 활용하는 경우는 그다지 많지 않습니다.
HandlerInterceptor는 기존의 컨트롤러에서는 순수하게 필요한 파라미터와 결과 데이터를 만들어내고, 인터셉터를 이용해 웹과 관련된 처리를 도와주는 역할을 한다. HandlerInterceptor의 메서드는 아래와 같이 사용 됩니다.
- preHandle(request, response, handler) : 지정된 컨트롤러의 동작 이전에 가로채는 역할로 사용 됩니다.
- postHandle(request, response, handler, modelAndView) : 지정된 컨트롤러의 동작 이후에 처리, Spring MVC 의 FrontController인 DispatcherServlet이 화면을 처리하기 전에 동작 합니다.
- afterCompletion(request, response, handler, exception) : DispatcherServletdml 화면 처리가 완료된 상태에서 처리됩니다.
HandlerInterceptorAdaptor 는 HandlerInterceptor 인터페이스를 구현한 추상 클래스로 설계되어 있는데 일반적으로 디자인패턴에서 Adaptor라는 용어가 붙게 되면 특정 인터페이스를 미리 구현해서 사용하기 쉽게 하는 용도인 경우가 많습니다. HandlerInterceptorAdaptor도 HandlerInterceptor를 쉽게 사용하기 위해서 인터페이스의 메서드를 미리 구현한 클래스 입니다.
3. Interceptor 예제
프로젝트에 Interceptor 적용하기
# Interceptor 클래스 작성
예제를 위해 아래와 같이 /tutorial/interceptor 패키지에 SampleInterceptor 클래스를 생성하고 HandlerInterceptorAdapter를 상속한다.
public class SampleInterceptor extends HandlerInterceptorAdapter {
}
# Interceptor 설정
위에서 작성한 SampleInterceptor를 스프링에서 인식하기 위해 dispatcher-servlet.xml에 아래와 같이 설정을 해줍니다. 그리고 인터셉터를 사용할 요청 url 를 작성해주면 됩니다.
<beans:bean id="sampleInterceptor" class="com.spring.practice.tutorial.interceptor.SampleInterceptor" />
<interceptors>
<interceptor>
<mapping path="/interceptor/doA"/>
<mapping path="/interceptor/doB"/>
<beans:ref bean="sampleInterceptor" />
</interceptor>
</interceptors>
# Interceptor을 위한 Controller, JSP 생성
간단한 테스트를 위해 /tutorial/controller 패키지에 아래와 같이 Controller를 생성하고 코드를 작성해주세요
@Controller
@RequestMapping(value = "/interceptor/*")
public class InterceptorController {
@GetMapping(value = "doA")
@LogException
public String doA() {
return "/tutorial/interceptor_test";
}
@GetMapping(value = "doB")
@LogException
public String doB(Model model) {
model.addAttribute("result", "doB result data...");
return "/tutorial/interceptor_test";
}
}
/views/tutorial 디렉토리에 interceptor_test.jsp를 생성하고 결과 페이지를 아래와 같이 작성해주세요
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ include file="../include/head.jsp" %>
<html>
<body class="hold-transition skin-green-light sidebar-mini" oncopy="return false" oncut="return false"
onpaste="return false">
<div class="wrapper">
<%@ include file="../include/top_menu.jsp" %>
<%@ include file="../include/left_menu.jsp" %>
<div class="content-wrapper">
<div class="col-lg-12">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">인터셉터 결과 데이터</h3>
</div>
<div class="box-body">
<a href="${path}/interceptor/doB">doB 페이지로 이동</a>
</div>
<div class="box-footer">
<p>결과데이터 : ${result}</p>
</div>
</div>
</div>
</div>
<%@ include file="../include/footer.jsp" %>
</div>
<%@ include file="../include/plugin_js.jsp" %>
</body>
</html>
# SampleInterceptor 코드 작성
SampleInterceptor 에서 preHandler()과 postHandler()을 이용해서 콘솔 출력 코드를 아래와 같이 작성해주세요
public class SampleInterceptor extends HandlerInterceptorAdapter {
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("post handle....");
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("pre handle....");
return true;
}
}
# 실행 및 결과
/interceptor/doA 경로로 이동하면 아래와 같이 콘솔창에 로그가 찍히는 것을 확인 할 수 있습니다.
pre handle....
2023-02-24 04:33:01,540 INFO [http-nio-8181-exec-8] aop.LogAdvice (LogAdvice.java:50) - 실행된 클래스 : InterceptorController
2023-02-24 04:33:01,555 INFO [http-nio-8181-exec-8] aop.LogAdvice (LogAdvice.java:51) - 실행된 메서드 : doA
2023-02-24 04:33:01,556 INFO [http-nio-8181-exec-8] aop.LogAdvice (LogAdvice.java:52) - 전달된 파라미터 : []
post handle....
4. Interceptor의 request, response 활용하기
인터셉터를 좀더 적극적으로 활용하면 컨트롤러가 웹에서 필요한 자원을 처리할 떄 겪는 불편을 해소하는데 도움이 됩니다.
# preHandle() 의 Object 파라미터
preHandle() 메서드의 경우 다음과 같은 3개의 파라미터를 사용합니다.
- HttpServletRequest request
- HttpServletResponse response
- Object handler
마지막 handler는 현재 실행하려는 메서드 자체를 의미하는데, 이를 활용해 현재 실행되는 컨트롤러를 파악하거나 추가적인 메서드를 실행하는 등의 작업이 가능합니다.
아래의 코드는 이전에 작성한 예제의 SampleInterceptor의 preHandle()을 이용하여 현재 실행되는 컨트롤러와 메서드 정보를 파악 할 수 있게 작성 하였습니다.
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("pre handle....");
HandlerMethod method = (HandlerMethod) handler;
Method methodObj = method.getMethod();
System.out.println("Bean : " + method.getBean());
System.out.println("Method : " + methodObj);
return true;
}
파라미터로 넘어온 handler는 HandlerMethod 타입으로 형변환한 후 원래 메서드와 객체를 확인 할 수 있습니다. 아래는 실행 결과 콘솔창에 출력된 결과 입니다.
pre handle....
Bean : com.spring.practice.tutorial.controller.InterceptorController@b444cfe
Method : public java.lang.String com.spring.practice.tutorial.controller.InterceptorController.doA()
2023-02-24 04:41:06,902 INFO [http-nio-8181-exec-17] aop.LogAdvice (LogAdvice.java:50) - 실행된 클래스 : InterceptorController
2023-02-24 04:41:06,919 INFO [http-nio-8181-exec-17] aop.LogAdvice (LogAdvice.java:51) - 실행된 메서드 : doA
2023-02-24 04:41:06,920 INFO [http-nio-8181-exec-17] aop.LogAdvice (LogAdvice.java:52) - 전달된 파라미터 : []
post handle....
# postHandle()을 이용해 추가적인 작업 수행하기
postHandle() 메서드의 경우 컨트롤러 메서드의 실행이 끝나고, 아직 화면처리가 되지 않은 상태이므로 필요하마면 메서드 실행 이후에 추가적인 작업이 가능합니다.
예를들어 특정 메서드이 실행결과를 HttpSession 객체에 같이 담아야할 경우, 컨트롤러에서는 Model객체에 결과 데이터를 저장하고, 인터셉터의 postHandle() 에서 이를 이용해 HttpSession에 결과를 담는다면 컨트롤러에서 HttpSession을 따로 처리할 필요가 없게 됩니다.
아래의 코드는 컨트롤러에서 result라는 변수가 저장되었다면 HttpSession 객체에 이를 보관하는 예제 입니다.
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("post handle....");
Object result = modelAndView.getModelMap().get("result");
if (result != null) {
request.getSession().setAttribute("result", result);
response.sendRedirect("/interceptor/doA");
}
}
컨트롤러의 코드에서 /doB를 호출하면, result라는 이름으로 하나의 문자열이 보관 되게 됩니다. 이후에 postHandle() 메서드에서 result 라는 변수가 ModalAndView에 존재하면 이를 추출해 HttpSession에 추가 합니다. 그리고 최종적으로는 /doA 페이지로 리다이렉트 됩니다.
실제로 화면에서 /doB를 호출하면 HttpSession에 보관된 result변수에 저장한 객체를 가지고 /doA로 이동하게 됩니다.
첫번쨰 사진은 /doA에서 /doB를 요청하기 전이고, 두번쨰 사진은 /doB 요청을 처리하고 리다이렉트 되어 /doA로 이동한 모습이다 위에서 설명한 것처럼 리다이렉트 되기 전에 인터셉터에서 result변수에 담긴 문자열을 HttpSession에 보관하여 /doA로 이동하게 되고 화면에 결과 데이터가 보여지게 됩니다.
5. 정리
위 예제와 같은 내용을 로그인 처리에서 유용하게 사용 할 수 있습니다. 컨트롤러에서 로그인 처리 후의 결과를 반환하고, 인터셉터를 이용해서 HttpSession에 로그인이 필요한 객체를 보관하는 형태로 작성하면 컨트롤러에서 직접 HttpSession을 사용하지 않는 코드를 만들 수 있습니다.
'스프링 프레임워크 > 스프링 기본 개념 정리 및 기본 예제' 카테고리의 다른 글
Spring MVC - Controller 작성 연습 (0) | 2023.02.13 |
---|---|
SpringMVC + Mybatis DAO 구현 (0) | 2023.02.13 |
Spring MVC 구조 (0) | 2023.02.09 |
Spring MVC - Mybatis 설정 및 테스트 (0) | 2023.02.09 |
Spring MVC - MariaDB 연결 테스트 (0) | 2023.02.08 |
댓글