2015년 11월 1일 일요일

@ModelAttribute에 대한 짧은 이야기


스프링 MVC에서 사용되는 @ModelAttribute 애노테이션은 말 그대로 객체를 모델의 속성으로 자동으로 지정하는 역할을 하게 됩니다. 이 기능을 이용하면 파라미터로 수집된 객체를 다시 모델로 처리해야하는 번거로운 과정을 줄일 수 있습니다.


일반적으로 컨트롤러에서는 다음과 같은 패턴의 작업이 이루어 집니다.

1) 전달받은 파라미터를 이용해서 결과 데이터를 만들어서 화면에 결과 데이터만을  전송하는 경우


2) 전달받은 파라미터를 이용해서 처리한 결과 데이터와 더불어 입력시에 사용한 파라미터를 다시 전송하는 경우


1)의 경우에는 결과 페이지로 새롭게 생성된 결과를 전송하기 때문에 굳이 @ModelAttribute를 이용하지 않아도 관계 없습니다.

반면에 2)의 경우는 만들어진 결과 뿐이 아니라, 입력된 값도 객체로 처리해서 재사용하는 경우입니다.

간단한 클래스를 이용해서 예제를 만들어 보도록 합니다.

Sample클래스는 id,pw의 속성과 price라는 속성을 가지고 있습니다.



컨트롤러에서 아래와 같은 메소드를 작성해 주었습니다.



samlePost( ) 메소드의 경우는 별도의 Model객체를 사용하지 않지만, @ModelAttribute를 이용해서 자동으로 모델로 처리되고, setPrice( )를 이용해서 추가적인 정보를 처리하고 있습니다.


JSP에서는 EL을 이용해서 간단히 처리할 수 있습니다.





@ModelAttribute는 이와 같이 Request로 전달된 데이터를 가공하는 데 있어서 좀 더 편리한 수단을 제공합니다.



PART 2에서의 게시판 처리 부분에 @ModelAttribute를 이용하는 부분은 아래와 같습니다.




라인 101에 선언된 listAll( )의 경우는 @ModelAttribute가 없는 상태입니다. listAll( )의 결과 페이지 역시 아래와 같이  Criteria를 사용하지 않고 있습니다.





라인 108의  listPage( )의 경우는 조금 얘기가 다릅니다. 라인 109에서 Criteria를 @ModelAttribute를 이용해서 cri라는 이름으로 저장했고,

라인 115에서 pageMaker.setCri( )를 통해서 저장해서 엄밀히 말해서 2중으로 결과 데이터로 전송하고 있습니다.


JSP에서는 아래의 코드를 이용하고 있습니다.




이 경우 PageMaker객체의 내부에 있는 Criteria객체를 이용하고 있기 때문에 @ModelAttribute로 저장한 cri는 사용하고 있지 않습니다.


굳이 Criteria를 2중으로 JSP로 전송한 이유는 사실 페이징 처리가 없는 무한 스크롤 페이징 처리를 고려한 것입니다.


즉 무한 스크롤에서는 페이징 처리는 없지만, 페이지에 대한 정보는 유지해야 하기 때문에 Criteria를 JSP로 전송하는 것이 좋습니다.

반면에 페이지 처리는 PageMaker만을 이용해서만 모든 것이 가능한 것이 좋다고 생각합니다.


따라서 Criteria를 @ModelAttribute로도 전송하고, PageMaker내부로도 저장해서 전송하는 형태가 되었습니다.



댓글 2개:

  1. 스프링 웹 프로젝트 때문에 질문드립니다.. 어디다 물어봐야 좋을지 몰라서..)
    part.4 mybaits설정까지 끝냈습니다. Test코드도 정상작동되구요. 그런데 test코드들은 잘돌아가는데 ex00 -> run as - tomcat 8 으로 실행하니까 잘되던게 에러가 뜨더군요.. 근데 이게 원래 그런건지.. 해결방법을 잘 모르겠네요ㅠㅠ 다른거 건드린게 없는데..


    11월 24, 2015 8:19:18 오후 org.apache.catalina.core.StandardContext listenerStop
    심각: Exception sending context destroyed event to listener instance of class org.springframework.web.context.ContextLoaderListener
    java.lang.IllegalStateException: BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext
    at org.springframework.context.support.AbstractRefreshableApplicationContext.getBeanFactory(AbstractRefreshableApplicationContext.java:170)
    at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:908)
    at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:884)
    at org.springframework.context.support.AbstractApplicationContext.close(AbstractApplicationContext.java:836)
    at org.springframework.web.context.ContextLoader.closeWebApplicationContext(ContextLoader.java:578)
    at org.springframework.web.context.ContextLoaderListener.contextDestroyed(ContextLoaderListener.java:115)
    at org.apache.catalina.core.StandardContext.listenerStop(StandardContext.java:4785)
    at org.apache.catalina.core.StandardContext.stopInternal(StandardContext.java:5404)
    at org.apache.catalina.util.LifecycleBase.stop(LifecycleBase.java:232)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:160)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1408)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1398)
    at java.util.concurrent.FutureTask.run(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

    답변부탁드려요 ㅠㅠ 고통받네요

    답글삭제
  2. 테스트 코드 까지 정상적으로 동작했다면 서버의 설정과 관련있을 가능성이 높습니다.

    http://java.ihoney.pe.kr/242

    와 같은 글을 먼저 참고 하시고.. 안 되시면 제게 메일로 한번 프로젝트를 보내 봐 주시면 감사하겠습니다.

    답글삭제