티스토리 뷰

컨트롤러


스프링에서는 컨트롤러의 유연한 개발을 위해 컨트롤러가 될 수 있는 대상에 대해 제한을 두고 있지 않는다.


스프링은 사용자의 모든 요청이 DispatcherServlet(web.xml에 서블릿으로 정의되어 있음)을 거친다.

직접 컨트롤러 클래스를 작성하기 위해서는 스프링 MVC가 지원하는 컨트롤러를 구현해야 하고 DispatcherServlet에 연결해주는 컨트롤러 어댑터가 필요하다.



  • Servlet, SimpleServletHandlerAdapter

이 컨트롤러와 컨트롤러 어댑터는 기존 표준 서블릿(HttpSErvlet을 상속받아 정의하는 서블릿) 클래스 코드를 그대로 유지하면서 사용할 수 있도록한다.

 


  • HttpRequestHandler, HttpRequestHandlerAdapter

이 컨트롤러와 어댑터를 이용하면 자바의 RMI(Remote Method Invocation 원격지 메소드 호출) 기술을 HTTP기반으로 사용할 수 있다.

이 컨트롤러는 모델과 뷰의 개념이 없는 서비스를 개발할 때 사용한다.



  • Controller, SimpleControllerHandlerAdapter

이 컨트롤러는 스프링 MVC기술을 확장해서 전용 컨트롤러를 만들어 사용하려고 할 때 유용하다.

컨트롤러가 특정 클래스를 상속하는데 문제가 없다면, Controller타입 인터페이스를 구현하는 공통 컨트롤러 클래스를 만들고 구현된 공통 컨트롤러를 상속받아서 개별적인 컨트롤러를 만들어 사용한다. 



+공통 컨트롤러

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//Controller인터페이스를 구현해서 만든 공통 컨트롤러
public abstract class CustomControl implements Controller{
 
    private String viewName;
    
    
    public void setViewName(String viewName){
        this.viewName = viewName;
    }
    
    
    @Override
    public ModelAndView handleRequest(HttpServletRequest request,
            HttpServletResponse response) throws Exception {
        
        ModelAndView view = new ModelAndView();
        Map<StringString> map = new HashMap<StringString>();
        
        Enumeration<String> names = request.getParameterNames();
        String key = null;
        String value = null;
        while(names.hasMoreElements()){
            key = names.nextElement();
            value = request.getParameter(key);
            map.put(key, value);
        }
        
        this.handleControl(map);
        
        view.addObject("result", map);
        view.setViewName(this.viewName);
        
        return view;
    }
    
    
    
    public abstract void handleControl(Map<StringString> map) throws Exception;
    
 
}
cs


+개별 컨트롤러

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class TestControl extends CustomControl{
    
    public TestControl(){
        this.setViewName("/WEB-INF/views/testControl.jsp"); //이 컨트롤러가 만들어질 때 view를 결정
    }
    
    //공통 컨트롤러 Customcontrol의 추상메서드 구현
    @Override
    public void handleControl(Map<StringString> map) throws Exception {
        String content = map.get("content").replaceAll("(바보|멍청이)""고운말");
        map.put("content", content);
    }
 
}
 
cs


+구현한 컨트롤러를 사용하기 위해 등록 (스프링 웹 애플리케이션 컨텍스트 xml파일)

1
2
3
4
5
6
7
8
9
<beans:bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
 
<beans:bean name="/testControl" class="dev.wedding.kr.controller.TestControl">
</beans:bean>
cs


위와 같은 스프링이 지원하는 컨트롤러는 기본적으로 지정이 되어 있기 때문에 직접 커스터마이징 방식을 사용하려는게 아니라면 따로 설정할 필요가 없다.




핸들러 매핑


핸들러 매핑은 HTTP의 요청정보를 이용해서 진입할 컨트롤러를 찾아주는 일을 담당한다.

핸들러 매핑은 앞서 얘기한 컨트롤러의 타입과 상관이 없다. 즉, 하나의 핸들러 매칭이 여러가지 타입의 컨트롤러를 선택할 수 있다.


스프링은 다섯가지 핸들러 매핑을 제공하고 기본적으로 다음 두 가지 매핑이 등록되어 있다.

- BeanNameUrlHandlerMapping, DefaultAnnotationHandlerMapping

주의 해야할 점은 기본 등록 매핑 외에 다른 매핑 빈을 등록한다면 기본 매핑 방식은 모두 무시된다.


*또한 스프링 3.2부터는 DefaultAnnotationHandlerMapping는 deprecated되었고, 이 대신에 RequestMappingHandlerMapping를 사용한다.


  • BeanNameUrlHandlerMapping

기본적으로 등록되어 있는 매핑 방법

요청한 url과 빈의 이름이 일치하는 빈을 찾아준다.

가장 사용하기 쉬운 매핑 이며 *, **, ?같은 와일드카드를 사용 가능하다.


1
2
3
4
5
6
7
8
<!-- 요청한 url이 test일 경우 매핑 -->
<beans:bean name="/test" class="dev.wedding.kr.controller.TestControl"></beans:bean>
<!-- 요청한 url이 test로 시작할 경우 매핑 ex) test1, testgo -->
<beans:bean name="/test*" class="dev.wedding.kr.controller.TestControl"></beans:bean>
<!-- 요청한 url이 test로 시작하면서 하나 이상의 경로일 때 ex) test/a/b, testgo/a/b -->
<beans:bean name="/test**" class="dev.wedding.kr.controller.TestControl"></beans:bean>
<!-- 요청한 url이 test로 시작 go로 끝나고 중간에는 하나 이상의 경로일 때 ex) test/good/bad/go -->
<beans:bean name="/test/**/go" class="dev.wedding.kr.controller.TestControl"></beans:bean>
cs



  • DefaultAnnotationHandlerMapping

기본적으로 등록되어 있는 매핑 방법

@RequestMapping이라는 애노테이션을 컨트롤러 클래스나 메서드에 선언하여 사용

    현재 대부분 이 방식으로 개발한다.



  • ControllerBeanNameHandlerMapping

빈의 id나 name을 이용해 매핑하는 방법

기본 등록 핸들러 매핑 빈이 아니므로 이 빈을 등록하면 기본 등록 빈은 무시된다.


+빈의 적용

1
<beans:bean class="org.springframework.web.servlet.mvc.support.ControllerBeanNameHandlerMapping" />
cs


1
2
3
4
@Component("test")
public class TestControl extends CustomControl{
    
}
cs

이렇게 스테레오 타입 애노테이션으로 빈의 이름을 지정하면 /test로 접근하는 url은 이 컨트롤러에 진입한다.

만약 빈의 이름을 지정하지 않고 단독으로 @Component와 같이 했을 경우 빈 스캔시 자동으로 등록되는 빈의 이름이 되는 testControl이 되기때문에 /testControl로 접근하는 url에 매핑된다.


xml을 통하여 빈 등록을 한 경우에는 다음과 같이 한다.

1
<beans:bean id="test" class="dev.wedding.kr.controller.TestControl"></beans:bean>
cs

이렇게 하면  /test로 들어오는 url이 해당 컨트롤러에 매핑된다.


핸들러에 다음과 같이 urlPrefix나 urlSuffix프로퍼티를 추가해서 시작과 끝 url을 지정할 수 있다.

1
2
3
4
<beans:bean class="org.springframework.web.servlet.mvc.support.ControllerBeanNameHandlerMapping" >
    <beans:property name="urlPrefix" value="/web/url"></beans:property>
    <beans:property name="urlSuffix" value="/page"></beans:property>
</beans:bean>
cs

이렇게 프로퍼티를 등록하면 /web/url/test/page로 접근하는 url은 TestControl컨트롤러에 매핑된다.



  • ControllerClassNameHandlerMapping

빈의 이름 대신에 url과 컨트롤러 클래스의 이름을 매핑시켜주는 방법이다.

기본 등록 핸들러 매핑 빈이 아니므로 이 빈을 등록하면 기본 등록 빈은 무시된다.


1
2
3
<beans:bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping" ></beans:bean>
 
<beans:bean  class="dev.wedding.kr.controller.TestController"></beans:bean>
cs



/test라는 url로 접근하면 다음과 같은 컨트롤러 클래스와 매핑시켜준다..

1
2
3
public class TestController extends CustomControl {}
 
public class Test extends CustomControl {}
cs

클래스 이름을 모두 url로 사용하지만 Controller로 끝난다면 Controller를 포함시키지 않은채로 매핑 한다.



  • SimpleUrlHandlerMapping

이 매핑 방식은 url과 컨트롤러의 매핑할 정보를 프로퍼티로 한곳에 모아놓고 설정하는 방식이다.

기본 등록 핸들러 매핑 빈이 아니므로 이 빈을 등록하면 기본 등록 빈은 무시된다.


1
2
3
4
5
6
7
8
9
10
11
12
<beans:bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping" >
        <beans:property name="mappings">
            <beans:props>
                <beans:prop key="test">test</beans:prop>
                <beans:prop key="testTwo">testTwo</beans:prop>
            </beans:props>
        </beans:property>
</beans:bean>
 
    
<beans:bean id="test" class="dev.wedding.kr.controller.Test"></beans:bean>
<beans:bean id="testTwo" class="dev.wedding.kr.controller.TestTwoController"></beans:bean>
cs


핸들러 매핑 빈의 mappings프로퍼티의 properties형식에서 key에는 접근할 url을 값에는 빈의 id를 선언한다.

그래서 /testTwo라는 url을 접근하면 그 키의 값인 testTwo를 id로 하는 컨트롤러 빈과 매핑이 된다.




핸들러 매핑 빈의 공통 프로퍼티 종류


  • order 프로퍼티

핸들러 매핑은 두개 이상을 동시에 사용할 수 있다. 두개 이상의 매핑을 적용했을 때에는 특정 URL접속시 서로 다른 두개의 매핑 방식에 모두 부합될 경우 자칫 오류를 발생시킬 수 있다.

이런 경우에 핸들러 매핑에서는 프로퍼티에서 order를 설정할 수 있다.

만약 기본 등록 핸들러 매핑인 BeanNameUrlHandlerMapping, DefaultAnnotationHandlerMapping에 order프로퍼티를 지정하려면 직접 빈으로 등록해줘야 한다.



  • defaultHandler 프로퍼티

핸들러 매핑 빈에 이 프로퍼티를 지정하면 접속할 URL을 찾지 못할 경우에 프로퍼티에 지정된 컨트롤러 빈을 선택한다.

URL을 찾지 못했을 때 에러 메세지를 보여주는 역할 같은 것을 수행할 수 있다.





핸들러 인터셉터


인터셉터는 DispatcherServlet이 컨트롤러를 호출하기 전과 후에 응답을 참조하고 가공할 수 있는 일종의 필터 역할을 한다.


핸들러 인터셉터를 등록하지 않았다면 바로 컨트롤러에 진입하지만 하나 이상의 핸들러 인터셉터를 지정했을 때는 순서에 따라 잍너셉터를 먼저 거친후에 컨트롤러를 호출한다.


핸들러 인터셉터는 HttpServletRequest, HttpServletResponse, 진입할 컨트롤러의 객체, 컨트롤러가 돌려주는 ModelAndView, 예외 등을 제공받을 수 있기 때문에 서블릿 기술에서 제공하는 필터보다 더 정교한 작업이 가능하다.


인터셉터는 HandlerIntercepter인터페이스를 구현해서 사용한다.


+HandlerInterceptor구현체 작성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class CustomHandlerIntercepter implements HandlerInterceptor {
 
    // 컨트롤러가 실행되기 전에 호출한다.
    @Override
    public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {
 
        String accept = request.getParameter("accept");
        if (accept != null && accept.equals("yes")) {
            return true;
        }
        return false;
 
    }
 
    // 컨트롤러가 실행된 후 호출된다.
    @Override
    public void postHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler, ModelAndView view)
            throws Exception {
 
        view.addObject("postHandle""인터셉터의 postHandle에서 왔습니다.");
 
    }
 
    // 모든작업이 다 완료된 후에 실행한다
    @Override
    public void afterCompletion(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
 
        if (ex != null) {
            System.out.println("작업에 익셉션이 발생하였습니다.");
        }
 
    }
 
}
cs

preHandle메서드의 리턴값이 false라면 postHandle, afterCompletion메서드를 호출하지 않는다.

postHandle메서드에서는 컨트롤러가 돌려주는 ModelAndView에 대하여 조작이 가능하다.


+인터셉터 적용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<beans:bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping" >
    <beans:property name="mappings">
        <beans:props>
            <beans:prop key="test">test</beans:prop>
            <beans:prop key="testTwo">testTwo</beans:prop>
        </beans:props>
    </beans:property>
    <beans:property name="interceptors">
            <beans:list>
                <beans:ref bean="customInterceptor" />
            </beans:list>
    </beans:property>
</beans:bean>
 
<beans:bean id="customInterceptor" class="dev.wedding.kr.interceptor.CustomHandlerIntercepter"></beans:bean>
    
<beans:bean id="test" class="dev.wedding.kr.controller.Test"></beans:bean>
<beans:bean id="testTwo" class="dev.wedding.kr.controller.TestTwoController"></beans:bean>
cs



- 서블릿의 필터

web.xml에 등록 해줘야 한다.

애플리케이션에 들어오는 모든 요청에 필터를 적용할 수 있다.

필터에서 스프링 빈을 사용하는 코드를 작성할 수 있다.


- 스프링의 인터셉터

DispatcherServlet에 진입하는 요청에 대해서만 적용이 된다.

컨트롤러 객체에 접근이 가능하다.

컨트롤러가 리턴하는 ModelAndView객체에 접근하여 데이터처리를 할 수 있다.

web.xml에 등록하지 않아도 된다.



공통로직을 처리하는 AOP 기술도 있지만 컨트롤러에 공통적으로 적용할 기능이라면 핸들러 인터셉터 기술을 사용하는 편이 낫다.

'SPRING > 정리' 카테고리의 다른 글

spring 4 + sockjs (websocket) with tomcat7+ jdk 1.7+  (0) 2016.09.02
@RequestMapping 핸들러 매핑  (0) 2016.08.24
빈 초기화 메서드 지정  (0) 2016.08.21
스코프(scope) 빈  (0) 2016.08.20
프로토타입(prototype) 빈  (0) 2016.08.19
Comments
최근에 올라온 글
최근에 달린 댓글
TAG
more
Total
Today
Yesterday