개발/스프링

22. 스프링 프레임워크 - 인터셉터[자바 웹을 다루는 기술]

괴발자-K 2022. 5. 30. 13:31

인터셉터(Interceptor)란?

인터셉터는 중간에서 가로챈다는 뜻입니다. 사전적 의미를 생각하며 스프링 인터셉터의 기능을 생각해 보자면

Client(브라우저)의 Request를 Controller에서 받아 실행 하기 전에 중간에서 가로채는 역할을 하고 

Controller 실행 후 DispacherServlet이 View로 보내기 전에 가로채는 역할을 합니다.

 

스프링 HandlerInterceptor 클래스의 메서드

메서드 기능
preHandle() 컨트롤러 실행 전 호출 됩니다.
postHandle() 컨트롤러 실행 후 DispathcerServlet이 뷰로 보내기 전에 호출 됩니다.
afterCompletion() 뷰까지 수행하고 나서 호출 됩니다.

 

servlet-context.xml

<?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:mvc="http://www.springframework.org/schema/mvc" //<mvc:~> 캐그를 사용하기 위해 추가
	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">
..................
<mvc:interceptors>
   <mvc:interceptor>
      <mvc:mapping path="/test/*.do"/>
      <mvc:mapping path="/*/*.do"/>
      <beans:bean class="com.myspring.pro28.ex05.LocaleInterceptor"/>
   </mvc:interceptor>
</mvc:interceptors>

/test/*.do 와 .do 끝나는 모든 요청에 대해서 인터셉터가 실행 되도록 설정 합니다.

 

message-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    
    <!--스프링의 SessionLocaleResolver 클래스를 이용해 locale 정보를 세션에 저장해서 사용  -->
    <!--패키지 locale에서 message.properties를 읽어 드림  -->
    <!--properties 파일이 변경되었는지 확인하는 주기를 지정, 60초 간격으로 지정  -->
     <!-- 메시지 파일로 프로퍼티 형식 사용을 위한 messageSource 구현체 클래스 빈 설정 -->
	<bean id="localeResolver" 
                  class="org.springframework.web.servlet.i18n.SessionLocaleResolver" />
    <bean id="messageSource"
          class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
         <property name="basenames">
            <list>
              <value>classpath:locale/messages</value>
            </list>
         </property>
         <property name="defaultEncoding" value="UTF-8"/>
         <property name="cacheSeconds" value="60"/>
    </bean>



</beans>

다국어 기능과 관련된 빈과 메시지 파일을 읽어 들이는 message-context.xml을 작성 합니다.

locale에서 message.properties 파일을 읽어 들입니다.

 

다국어를 정리한 프로퍼티스 파일 생성

 

LocaleInterceptor.java

 

package com.myspring.pro28.ex05;
..................

//사용자 정의 인터셉터는 반드시 HandlerInterceptorAdapter를 상속 받아야 함 
public class LocaleInterceptor extends HandlerInterceptorAdapter{
    
	@Override
	//컨트롤러 실행 전 호출
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		HttpSession session = request.getSession();
		
		//브라우저에서 전달한 locale 정보를 가져옵니다.
		String locale = request.getParameter("locale");
		
		//최초 요청시  locale을  한국어로 설정
		if(locale==null) locale="ko";
		//LOCALE 속성 값을 세션에 저장, SessionLocaleResolver가 사용할 수 있게 함
	    session.setAttribute("org.springframework.web.servlet.i18n.SessionLocaleResolver.LOCALE", new Locale(locale));
	    
	    return true;
		
	}
	
	//컨트롤러 실행 후 호출
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		
	}
	//뷰(JSP)를 수행한 후 호출
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		
	}
	
}

 

LocaleController.java

package com.myspring.pro28.ex05;

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

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

@Controller
public class LocaleController {
    
	@RequestMapping(value="/test/locale.do", method=RequestMethod.GET)
	public String locale(HttpServletRequest request,
			             HttpServletResponse response) throws Exception{
		
		System.out.println("localeController 입니다.");
		
		//컨트롤러는 뷰이름만 반환
		return "locale";
	}
}

locale.jsp로 반환하는 controller를 작성 합니다.

 

locale.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="spring"  uri="http://www.springframework.org/tags" %>
<c:set var="contextPath" value="${pageContext.request.contextPath }"/>

<%
   request.setCharacterEncoding("utf-8");
%>    
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<!--spring:message 이용해 code속성에 프로퍼티 파일의 site.title 값을 표시  -->
<title><spring:message code="site.title" text="Member Info"/></title>
</head>
<body>
     <!-- 한국어, 영어를 요청  -->
     <a href="${contextPath }/test/locale.do?locale=ko">한국어</a>
     <a href="${contextPath }/test/locale.do?locale=en" >ENGLISH</a>
     <h1><spring:message code="site.title" text="Member Info"/></h1>
     
     <!-- 프로퍼티 site.name에 해당하는 값을 표시, 프로퍼티 name에 해당하는 값 표시  -->
     <p><spring:message code="site.name" text="no name"/> : 
        <spring:message code="name" text="no name"/></p>
     <p>
         <spring:message code="site.job" text="no job"/> :
         <spring:message code="job" text="no job"/></p>
         
     <input type="button" value="<spring:message code='btn.send'/>" />
     <input type="button" value="<spring:message code='btn.cancel'/> "/>
     <input type="button" value="<spring:message code='btn.finish' /> "/>
     <!--spring:message 태그를 이용해 프로퍼티 bin.send를 버튼 이름으로 설정  -->
</body>
</html>

<spring:message> 태그를 사용 할 수 있도록 <%@ taglib prefix="spring"  uri="http://www.springframework.org/tags" %>

를 지정 해줍니다.

<spring:message> code 속성에 프로퍼티스 파일의 site.title 값을 표시합니다.

그리고 한국어, 영어 요청에 따라 controller에 매개변수를 전달해 controller를 실행하고 뷰로 다시 반환합니다.

<spring:message code="properties의 키" text="기본값" /> 

 

인터셉터 사용해서 request명에서 뷰이름을 가져오기

package com.myspring.pro27.member.interceptor;

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

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

public class ViewNameInterceptor extends HandlerInterceptorAdapter{
       
	
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		
		String viewName = getViewName(request);
		request.setAttribute("viewName", viewName);
		
		return true;
		
	}
	
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
	
	}
	
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		
	}
	
	private String getViewName(HttpServletRequest request) throws Exception {
		String contextPath = request.getContextPath();
		String uri = (String) request.getAttribute("javax.servlet.include.request_uri");
		if (uri == null || uri.trim().equals("")) {
			uri = request.getRequestURI();
		}

		int begin = 0;
		if (!((contextPath == null) || ("".equals(contextPath)))) {
			begin = contextPath.length();
		}

		int end;
		if (uri.indexOf(";") != -1) {
			end = uri.indexOf(";");
		} else if (uri.indexOf("?") != -1) {
			end = uri.indexOf("?");
		} else {
			end = uri.length();
		}

		String viewName = uri.substring(begin, end);
		if (viewName.indexOf(".") != -1) {
			viewName = viewName.substring(0, viewName.lastIndexOf("."));
		}
		if (viewName.lastIndexOf("/") != -1) {
			viewName = viewName.substring(viewName.lastIndexOf("/",1), viewName.length());
		}
		return viewName;
	}
}

아래 getViewName()을 request에 viewName으로 바인딩하여 컨트롤에서 request.getAttribute("viewName");을 사용합니다.

 

#마무리

인터셉터를 통해 Controller에 도달하기 전에 뭔가 기능을 실행해야 하는경우가 있습니다. 

또한 AOP와도 같이 사용할 수 있으니 응용해 보시기 바랍니다.