핸들러 매핑은 HTTP 요청정보를 이용해서 이를 처리할 핸들러 오브젝트, 즉 컨트롤러를 찾아 주는 기능을 가진 DispatcherServlet 의 전략이다.

Spring은 기본적으로 5개의 핸들러 매핑을 제공한다. 이 중에 디폴트로 등록된 핸들러 매핑은 BeanNameUrlHandlerMapping 과 DefaultAnnotationHandlerMapping 이다. 그 외의 핸들러 매핑을 사용하려면 핸들러 매핑 클래스를 빈으로 등록해줘야 한다.

Spring 이 제공하는 5가지 핸들러 매핑 전략은 다음과 같다.

BeanNameUrlHandlerMapping

○ 빈의 이름에 들어 있는 URL을 HTTP 요청의 URL 과 비교해서 일치하는 빈을 찾아 준다.

○ 가장 직관적이고 사용하기 쉬운 핸들러 매핑 전략이다. 

○ URL 에는 ANT 패턴이라고 불리는, *나 **, ? 와 같은 와일드 카드를 사용하는 패턴을 넣을 수 있다.

○ 예를 들어 다음 빈 선언은 /s 로 시작하는 /s, /s1, /sabcd 같은 URL 에 매핑된다.

<bean name="/s*" class="com.happyhouse....Controller">

○ ** 는 하나 이상의 경로를 지정할 때 사용한다. 다음 매핑은 /root/sub, /root/a/sub, /root/a/b/c/d/sub 를 모두 매핑해 준다.

<bean name="/root/**/sub" class="com.happyhouse....Controller">
○ BeanNameUrlHandlerMapping 은 디폴트 전략이고 사용하기도 편리해서, 빠르고 쉽게 URL 매핑정보를 지정할 수 있다.

○ 반면에 컨트롤러 개수가 많아지면 URL 정보가 XML 빈 선언이나 클래스의 애노테이션 등에 분산되어 나타나므로 전체적인 매핑구조르르 한눈에 파악하고 관리하기 불편하다. 따라서 복잡한 애플리케이션에서는 잘 사용하지 않는다.

ControllerBeanNameHandlerMapping

○ ControllerBeanNameHandlerMapping 은 빈의 아이디나 빈 이름을 이용해 매핑해주는 핸들러 매핑 전략이다.

○ 다음과 같이 컨트롤러 빈이 선언되어 있다면, ControllerBeanNameHandlerMapping 은 hello 빈을 /hello URL 에 매핑해 준다.

<bean id="hello" class="com.happyhouse....Controller">
○ 다음과 같이 스테레오타입 애노테이션을 이용해 설정해도 /hello 에 매핑된다.
@Component("hello")
public class MyController implements Controller {

        ...
}
○ ControllerBeanNameHandlerMapping 은 빈 이름 앞뒤에 붙일 수 있는 prefix, suffix 를 지정할 수 있다. URL 이 모두 /app/sub/로 시작한다면 이를 ControllerBeanNameHandlerMapping 의 prefix 프로퍼티에 아래와 같이 등록해주면 위의 MyController 빈은 /app/sub/hello URL 에 매핑된다. 
<bean class="org.springframework.web.servlet.mvc.support.ControllerBeanNameHandlerMapping">
    <property name="urlPrefix" value="/app/sub/"/>
</bean>

○ 이렇게 특정 전략 클래스를 빈으로 등록한 경우에는 디폴트 전략은 모두 무시된다는 점에 주의해야 한다. 원래 DispatcherServlet 의 디폴트 핸들러 매핑 전략은 두 가지가 등록되어 있지만, 위와 같이 특정 전략을 빈으로 등록하면 디폴트 핸들러 매핑인 AnnotationMethodHandlerAdapter 와 DefaultAnnotationHandlerMapping 은 적용되지 않는다.

ControllerClassNameHandlerMapping

○ ControllerClassNameHandlerMappping 은 빈 이름 대신 클래스 이름을 URL에 매핑해 주는 핸들러 매핑 클래스다.

○ 다음과 같은 컨트롤러 클래스는 '/hello' URL 에 매핑된다. 기본적으로는 클래스 이름을 모두 URL로 사용하지만 Controller 로 끝날 때는 Controller 를 뺀 나머지 이름을 URL 에 매핑해 준다.

public class HelloController implements Controller{ ... }
○ 디폴트 전략이 아니므로 ControllerClassNameHandlerMapping 을 빈으로 등록해줘야 한다.

SimpleUrlHandlerMapping

○ BeanNameUrlHandlerMapping 은 빈 이름에 매핑정보를 넣기 때문에 매핑정보를 관리하기 불편하다는 단점이 있다. 

○ SimpleUrlHandlerMapping 은 URL 과 컨트롤러의 매핑정보를 한 곳에 모아 놓을 수 있는 핸들러 매핑 전략이다. 

○ 매핑정보는 SimpleUrlHandlerMapping 빈의 프로퍼티에 넣어준다.

○ 디폴트 핸들러 매핑 전략이 아니기도 하고 프로퍼티에 매핑정보를 직접 넣어줘야 하므로 SimpleUrlHandlerMapping 빈을 등록해야 사용할 수 있다.

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="mappings">
        <props>
            <prop key="/hello">helloController</prop>
            <prop key="/sub/*">myController</prop>
            <prop key="deep/**/sub">subController</prop>
        </props>
    </property>
</bean>

<bean id="helloController" .../>
<bean id="myController" .../>
<bean id="subController" .../>
○ mappings 프로퍼티는 Properties 타입이므로 다음과 같이 프로퍼티 파일 포멧을 이용해 간단히 프로퍼티 값을 지정할 수도 있다.
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="mappings">
        <value>
            /hello=helloController
            /sub/*=myController
            deep/**/sub=subController
        </value>
    </property>
</bean>

<bean id="helloController" .../>
<bean id="myController" .../>
<bean id="subController" .../>
○ SimpleUrlHandlerMapping 의 장점은 매핑정보가 한 곳에 모여 있기 때문에 URL을 관리하기가 편리하다는 것이다. 그래서 컨트롤러의 개수가 많은 대규모의 프로젝트에서는 SimpleUrlHandlerMapping 을 선호하기도 한다. 단점은 매핑할 컨트롤러 빈의 이름을 직접 넣어줘야 하기 때문에 오타 등의 오류가 발생할 가능성이 있다는 것이다.

○ 최근에 많이 사용되는 애노테이션을 이용한 매핑방식ㅇ느 매핑정보를 코드에 가깝게 두지만 매핑정보 자체는 흩어져 있다. 어떤 방식이 더 낫다고 말하기는 어렵다. 프로젝트의 규모, 개발생산성, 관리 편의성, 매핑정보 오류검증 편의성 등을 종합적으로 고려해서 선택해야 한다.

DefaultAnnotationHandlerMapping

○ @RequestMapping 이라는 애노테이션을 컨트롤러 클래스나 메소드에 직접 부여하고 이를 이용해 매핑하는 전략이다.

○ @RequestMapping 은 메소드 단위로도 URL 을 매핑해 줄 수 있어서 컨트롤러의 개수를 획기적으로 줄일 수 있다는 장점이 있다.

○ 또한 URL 뿐 아니라, GET/POST 와 같은 HTTP 메소드 정보, 심지어는 파라메터와 HTTP 헤더정보까지 매핑에 활용할 수 있다.

○ 예를 들어 같은 URL 이지만 GET과 POST 인 경우를 따로 분리한다거나, 특정 파라메터가 지정됐을 때만 따로 분리하는 식의 컨트롤러 매핑이 가능하다. 

○ 반면에 매핑 애노테이션의 사용정책과 작성 기준을 잘 만들어 두지 않으면, 개발자마다 제멋대로 매핑방식을 적용해서 매핑정보가 지저분해지고 관리하기 힘들어질 수도 있으니 주의해야 한다.

기타 공통 설정정보

○ 핸들러 매핑 전략은 매핑 방식을 매우 세밀하게 제어할 수 있는 다양한 프로퍼티를 제공한다. 여기서는 핸들러 매핑 설정에서 공통적으로 사용되는 주요 프로퍼티만 간단히 설명하겠다.

order

○ 핸들러 매핑은 한 개 이상을 동시에 사용할 수 있다. 기본적으로 이미 두 개의 핸들러 매핑이 등록되어 있다. 

○ 두 개 이상의 핸들러 매핑을 적용했을 때는 URL 매핑정보가 중복되는 경우를 주의해야 한다. 이런 경우를 위해 스프링에서는 핸들러 매핑의 우선 순위를 지정할 수 있다.

○ 핸들러 매핑은 모두 Ordered 인터페이스를 구현하고 있다. Order 인터페이스가 제공하는 order 프로퍼티를 이용해 적용 우선순위를 지정할 수 있다. 

○ 디폴트 핸들러 매핑전략에 order 프로퍼티를 설정해 주려면 빈으로 등록을 해줘야 한다.

defaultHandler

○ 핸들러 매핑 빈의 defaultHandler 프로퍼티를 지정해 두면 URL을 매핑할 대상을 차지 못했을 경우 자동으로 디폴터 핸들러를 선택해 준다.

○ 핸들러 매핑에서 URL을 매핑할 컨트롤러를 찾지 못하면 HTTP 404 에러가 발생한다. HTTP 404 에러를 돌려 주는 대신 디폴트 핸들러로 넘겨서 친절한 안내 메시지를 뿌려 주는 것이 좋은 방법이다.

<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
    <property name="defaultHandler" ref="defaultController"/>
</bean>
alwaysUseFullPath

○ URL 매핑은 기본적으로 웹 애플리케이션의 컨텍스트 패스와 서블릿 패스 두 가지를 제외한 나머지만 가지고 비교한다.

○ 예를 들어 웹 애플리케이션은 /sub 로 배포됐고, DispatcherServlet 의 매핑은 /app/* 로 됐다고 하자. 이때 /hello 라는 URL 에 매핑된 컨트롤러에 접근하려면 /sub/app/hello 라고 적어야 한다.

○ 하지만 특별한 이유가 있어서 URL 전체를 사용해서 컨트롤러를 매핑하기 원한다면 핸들러 매핑 빈의 alwaysUseFullPath 프로퍼티를 true 로 선언해주면 된다.

○ 사용은 비권장이다.

detectHandlersInAncestorContexts

○ 일반적으로 서블릿 컨텍스트의 부모 컨텍스트는 루트 컨텍스트다. 자식 컨텍스트의 빈은 부모 컨텍스트의 빈을 참조할 수 있다는 원칙에 따라서, 서블릿 컨텍스트의 빈들은 자유롭게 루트 컨텍스트에 정의한 DAO 나 서비스 계층 빈을 참조할 수 있다.

○ 하지만 핸들러 매핑의 경우는 다르다. 핸들러 매핑 클래스는 기본적으로 현재 컨텍스트, 즉 서블릿 컨텍스트 안에서만 매핑할 컨트롤러를 찾는다. 바로 detectHandlersInAncestorContexts 프로퍼티가 false 로 선언되어 있기 때문이다.

○ 그런데 detectHandlersInAncestorContexts 프로퍼티를 강제로 true 로 바꿔주면 부모 컨텍스트까지 뒤져서 매핑 대상 컨트롤러를 찾게 할 수 있다.

○ 하지만 절대 사용하지 말자.

반응형
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기