13.11. Convention over configuration 3

마지막으로 살펴볼 CoC는 ModelAndView 객체의 View에 해당하는 논리적인 View의 이름에 관련된 것입니다.

요청 -> view 이름 :: DefaultRequestToViewNameTranslator

앞에서 살펴봤던 예제 코드를 다시 살펴보겠습니다.

    public ModelAndView list(HttpServletRequest request, HttpServletResponse response){
        return new ModelAndView(“issue/list”)
            .addObject(issueService.getAll());
    }

여기서 논리적인 뷰의 이름으로 issue/list 를 넘겨주고 있습니다. 이 이름은 다음과 같은 ViewReslover에 의해 WEB-INF/jsp/issue/list.jsp 를 랜더링 하게 됩니다.

    <bean id=”viewResolver”
        class=”org.springframework.web.servlet.view.InternalResourceViewResolver”>
        <property name=”viewClass”
            value=”org.springframework.web.servlet.view.JstlView” />
        <property name=”prefix” value=”/WEB-INF/jsp/” />
        <property name=”suffix” value=”.jsp” />
    </bean>

DefaultRequestToViewNameTranslator 를 사용하면 요청을 바탕으로 논리적인 View 이름을 생성하여 ViewResolver에 넘겨주게 됩니다. 특히나 이 클래스는 Spring의 DispatcherServlet  이 기본으로 가지고 있기 때문에 명시적으로 선언하지 않아도 됩니다.

샘플
 http://localhost:8080/gamecast/display.html -> display
 http://localhost:8080/gamecast/displayShoppingCart.html -> displayShoppingCart
 http://localhost:8080/gamecast/admin/index.html -> admin/index

위의 ModelAndView 코드를 다음과 같이 수정할 수 있습니다.

    public ModelAndView list(HttpServletRequest request, HttpServletResponse response){
        return new ModelAndView()
            .addObject(issueService.getAll());
    }

이렇게 보니까 ModelAndView 라는 이름이 어색하지 않게 보이네요. 🙂

13.11. Convention over configuration 2

이번에는 MVC중에서 M 즉 모델에 적용할 수 있는 Convention을 살펴보겠습니다.

Spring의 컨트롤러에서 모델을 넘겨줄 때 ModelAndView 객체를 사용합니다. 보통은 다음과 같이 사용합니다.

    public ModelAndView list(HttpServletRequest request, HttpServletResponse response){
        return new ModelAndView(“issue/list”, “issueList”, issueService.getAll());
    }

또는 뷰와 모델을 명확하게 구분하기 위해 다음과 같이 사용합니다.

    public ModelAndView list(HttpServletRequest request, HttpServletResponse response){
        return new ModelAndView(“issue/list”)
            .addObject(“issueList”, issueService.getAll());
    }

갑자기 기억나는 이야기지만 객체의 이름은 ModelAndView인데 View먼저 등록하니까 이름을 ViewAndModel로 해야하는 것 아니였나.. 라는 글이 생각납니다.

Anyway addObject 메소드가 오버로딩으로 또 다른 메소드 하나가 더 존재합니다. 그것을 사용하면 코드를 다음과 같이 변경 할 수 있습니다.

    public ModelAndView list(HttpServletRequest request, HttpServletResponse response){
        return new ModelAndView(“issue/list”)
            .addObject(issueService.getAll());
    }

모델의 이름이 생략됐습니다.

바로 이겁니다. 모델의 이름을 생략할 수 있습니다. 생략하고 보내면 View에서는 그럼 이 모델을 어떤 이름으로 찾게 될 것인가?

if(객체타입 == List || Set || 배열){
    객체의 camel-case 맨 뒤에 List를 붙인 이름을 가지게 됩니다.
} else {
    객체의 camel-case 이름을 가지게 됩니다.
}

샘플
x.y.Foo 타입의 객체는 foo 라는 이름을 가지게 됩니다.
x.y.z.FooBar 타입의 객체는 fooBar 라는 이름을 가지게 됩니다.
List<Foo> 타입의 객체는 fooList 라는 이름을 가지게 됩니다.
FooBar[] 타입의 객체는 fooBarList 라는 이름을 가지게 됩니다.

이 메소드를 사용할 때 한 가지 주의할 것이 있습니다.

Note: Empty Collections are not added to the model when using this method because we cannot correctly determine the true convention name. View code should check for null rather than for empty collections as is already done by JSTL tags.

비어있는 콜렉션을 추가할 수 없다는 군요.

13.11. Convention over configuration 1

Spring 2.0에 추가된 기능으로 네이밍으로 규약을 정하고 그것을 지키기만 하면 설정을 대폭 줄일 수 있는 방법이 마련되었습니다.

Request -> 컨트롤러 규약 :: ControllerClassNameHandlerMapping

즉 핸들러 맵핑을 통해 매번 각각의 요청을 처리할 컨트롤러들을 등록했었습니다. 보통 다음과 같은 소스코드를 확인하실 수 있습니다.
사용자 삽입 이미지
ControllerClassNameHandlerMapping 핸들러를 등록하면 다음과 같은 규약을 사용할 수 있습니다.

if(WelcomeController == MultiActionController){
    WelcomeController 는 /welcome/* 요청을 처리하게 됩니다.
} else {
    WelcomeController 는 /welcome* 요청을 처리하게 됩니다.
}

사용하는 방법은 간단합니다. ControllerClassNameHandlerMapping 을 등록해주기만 하면 됩니다.

    <bean id=”handlerMapping” class=”org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping”/>

    <bean class=”org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping”/>

BeanNameUrlHandlerMapping은 CoC를 사용하지 않고 직접 url과 매핑 시켜줄 컨트롤러들을 위해 등록해 줍니다.

이제부터는 요청을 처리하는 컨트롤러를 찾으려고 XXX-sevlet.xml에서 검색하지 않아도 됩니다. 그냥 요청의 이름만 보면 해당 요청을 어디서 처리할 지 알 수 있습니다.

    <bean class=”net.agilejava.nayoung.controller.IssueController” />

컨트롤러를 등록할 때 name이나 id를 주지 않아도 되기 때문에 컨트롤러를 등록할 때 ‘이 컨트롤러가 처리하는 url이 어떤거였더라..’ 라는 고민을 하지 않아도 됩니다.