Spring 2.5 @Controller 사용시 BindingResult 주의 할 것.

스프링 레퍼런스에 BindingResult에 대한 언급은 단 한 줄.

org.springframework.validation.Errors / org.springframework.validation.BindingResult  validation results for a preceding command/form object (the immediate preceding argument).

이게 끝입니다. 한 줄이라고 무시하면 안 됩니다. 진짜 중요한 한 줄입니다.

public String update(@ModelAttribute(“model”) Foo model, BindingResult result, Bar bar)

public String update(@ModelAttribute(“model”) Foo model, Bar bar, BindingResult result)

이 두 줄의 코드는 어쩌면 아무런 차이가 없을 수도 있지만 Validator를 만들어 보시면 그 차이를 알 수 있습니다. 무슨 차이인지는 비밀입니다. ㅋㅋ 이미 스프링 레퍼런스에 다 설명이 나와있어서 비밀이랄 것도 없지만 말이죠.ㅋ

힌트 1.
validator.validate(model, result);

힌트 2.
public void validate(Object obj, Errors errors) {
    …
}

Spring 컨트롤러와 request scope bean

스프링 컨트롤러와 request scope관한 이메일이 왔습니다.

제가 느끼고 있는 궁금증은 Controller 사용시 bean scope에 관한 부분입니다.

몇번의 프로젝트에서  SpringMVC를 이용하여 프로젝트를 했었는데요.
하나의 Controller에서 요청을 처리를 하기 위해 MultiActionController를 사용했습니다.
 
BaseController를 정의해서 BaseController가 MultiActionController를 상속받도록 만들었고,

모든 Controller는 BaseController를 상속 받아 쓰는 형식으로 구조를 잡았습니다.
 
BaseController에는 handleRequestInternal() 메소드를 오버라이드 해서 모든 request 값을

파싱하여 Map에 담도록 해놓았구요. (map에서 값을 꺼내 요청을 처리하도록 말이죠.)
 
테스트를 위해 Controller 내에서 sleep() 을 준 뒤, 몇개의 요청을 날려보면
가장 나중에 요청된 정보로 앞의
정보들이 변경되더군요.
아마도 scope이 singleton이라 그런것 같더군요.
그래서 빈 설정시에 Controller에 대한
scope을 모두 request로 바꿔줬습니다.
 
<bean id=”memberController” class=”MemberController”
scope=”request”>
 
이렇게 바꾸어주니 이전과 같은 현상은 발생하지 않더군요.
과연 이렇게 하는 것이 맞는 것인지, 아니면 BaseController를 scope=”request”로 만들면 그걸

상속받는 다른 빈도 request가 되는 것인지 정확한 판단이 서질 않더군요.
제가 알기로는 Spring에서 Controller 이용시 threadsafe 한 설계는 개발자의 몫이라고
들었습니다.
어디를 봐도 정확한 가이드가 나와있지 않아서 혼자 헤매다 이렇게 메일을 드리게 되었습니다.


그리고 다음과 같이 답변해드렸습니다.

[#M_ more.. | less.. |

흠.. 일단 MemberController에 request를 붙이는 것은 아닌거 같습니다.
때네세요. request scope는 그런 용도가 아닙니다. 컨트롤러 객체를 매번 생성하는 것은 시스템의 효율이 떨어집니다.

위에서 발생한 문제는 짐작하신 내용이 맞는거 같습니다.
BaseController가 Threadsafe하지 않아서 생기는 문제 같습니다.

일단은.. BaseController의 이름은 BaseMultiActionController(BMAC)가 되는게 좋겠구요.

먼저 문제의 원인을 살펴보죠.

MultiActionController
의 handleRequestInternal을 오버라이딩해서 request 파라미터 값들을 맵핑해서 맵에다가 담아두고 그것들을
BMAC의 하위 클래스들에거 써내서 사용한다는 말씀이지요? 그 맵은 당연히 멤버변수겠지요? 이 때 그 map 객체는 어떻게
생성하나요?

class BMAC extends MAC {
   protected Map paramMap = new HashMap();
}

이렇게 되어 있나요? 자 이랬을 때, MAC 객체를 싱글톤으로 등록해두면, paramMap 객체도 한 번만 생성하겠죠. 그래서 원하는 결과를 얻지 못한 것입니다.

대신 request라는 scope은 매 요청이 들어올 때마다 새로운 객체를 만들어서 제공해주는 scope인데, 컨트롤러 객체를 그런식으로 생성하는 건 좀 비효율적이죠.

이렇게 하시는건 어떨까요? map 을 bean으로 등록하고 컨트롤러는 싱글톤으로 등록하고 해당 map 객체를 request scope의 빈으로 등록하시는 겁니다.

그러면
1. 컨트롤러는 싱글톤이라 효율적입니다.
2. map은 매 요청마다 새로 만들어서 가져오기 때문에 원하는 결과를 얻을 수 있습니다.

<bean id=”memberController” class=”whiteship.MemberController”>
    <property name=”paramMap” ref=”paramMa” />
</bean>

<bean id=”paramMap” class=”java.util.HashMap” scope=”request” />

이런식으로하면 되겠죠?

_M#]
request scope 빈을 써본지가 까마득한데 이런 경우에 유용하게 쓸 수 있겠네요. 내용 공유를 허락해주셔서 감사합니다.

rails로 생성한 scaffold 코드 살펴보기 – Controller

class CategoriesController < ApplicationController
  def index
    list
    render :action => ‘list’
  end

컨트롤러의 첫부분 입니다. list 메소드를 호출하고 list라는 이름의 뷰를 사용하도록 render :action 을 사용합니다.

  def destroy
    Category.find(params[:id]).destroy
    redirect_to :action => ‘list’
  end

컨트롤러의 마지막 부분으로 지우고 난 뒤 list 페이지로 이동하도록 redirect_to :action 을 사용합니다.

컨트롤러 곳곳에서 사용하고 있는 find, find_all, new, save, update_attributes, destroy 이런 메소드들은 ActiveRecord에 있는 메소드를 사용한다고 합니다. ActiveRecord에 대한 공부가 필요하군요. 이 전 글에서 제가 궁금해 했었던 Dao 역할을 하는 녀석이 바로 이 녀석이였던 것 같습니다.

RadRails의 RI 탭에서 검색하면 바로 레퍼런스를 참조 할 수 있습니다. 그런데 조금 오래 걸립니다. 뻗어버린 줄 알았습니다…
사용자 삽입 이미지

13.3. Controllers

컨트롤러는 MVC중 C에 해당하며 사용자의 입력을 받은 뒤 뷰에 보여주기 적당한 모델로 바꿔준다.

Spring은 다양한 컨트롤러들을 제공하는데 크게 폼 컨트롤러, 커맨드 기반 컨트롤러, 마법사 스타일의 컨트롤러로 나뉩니다.

Spring 컨트롤러의 가장 상위에 위치한 Controller 인터페이스는 다음과 같습니다.

public interface Controller {

    /**
     * Process the request and return a ModelAndView object which the DispatcherServlet
     * will render.
     */
    ModelAndView handleRequest(
        HttpServletRequest request,
        HttpServletResponse response) throws Exception;

}

모든 Controller 구현체들은 재사용 가능하며, Thread-safe 해야합니다.

Workflow
사용자 삽입 이미지Request 요청이 들어오면 DisptacherServlet에서 Locale, Theme 등등을 확인하고 HandlerMapping을 사용하여 요청을 담당할 컨트롤러를 물색합니다.

요청을 처리할 컨트롤러를 찾으면 그 컨트롤러의 handleRequest 메소드에게 HttpServletRequest와 HttpServletReponse 객체를 인자로 넘겨주며 호출합니다.

해당 컨트롤러는 요청을 처리하고 넘겨줄 ModelAndView 객체가 있다면 DispatcherServlet에게 해당 객체를 넘겨줍니다.

Notes on design and testing

Servlet API에 종속되어 기존 Servlet 의 기능을 모두 사용할 수 있습니다.

Servlet API에 의해 제공되는 HttpServletRequest 와 HttpServletResponse의 Mock 객체를 사용하여 handleRequest 메소드를 테스트 할 수 있습니다.

숙제 3

SimpleFormController
– Spring reference
[#M_ more.. | less.. | a form controller that provides even more support when creating a form with a corresponding command object. The SimpleFormController  let’s you specify a command object, a viewname for the form, a viewname for page you want to show the user when form submission has succeeded, and more._M#]- Spring API :: SimpleFormController
– Spring MVC :: p65(84)
– Pro Spring :: p547

CoC
– Convention over Configuration 의 약어로 “설정을 능가하는 규약” 정도의 의미.
http://softwareengineering.vazexqi.com/files/pattern.html
Convention vs Configuration
CoC in Spring MVC