2014년 8월 18일 월요일

[스프링웹MVC,오라클자바커뮤니티/스프링교육/학원]Spring MVC(@SessionAttributes,@ModelAttribute,스프링 프레임워크 컨트롤러)

[스프링웹MVC,오라클자바커뮤니티/스프링교육/학원]Spring MVC(@SessionAttributes,@ModelAttribute,스프링 프레임워크 컨트롤러)

@SessionAttributes를 이용하여 model 객체를 세션에 저장하기

@SessionAttributes Annotation은 컨트롤러에서 사용되며 어노테이션에서 설정한 모델객체를 세션에 저장하는 역할을 하고 이후부터 해당 모델객체가 사용되면 세션에서 불러와서 사용하고 이를 뷰에서 이름으로 접근이 가능하도록 한다.

@SessionAttributes 애노테이션을 컨트롤러 클래스에 붙이고 모델 이름을 인자로 넣어준다. 컨트롤러의 메소드가 만드는 모델 정보 중에 이름이 같은 것이 있으면 세션에도 저장하고  컨트롤러 메소드의 @ModelAttribute인자를 HTTP요청이 아니라 먼저 세션에서 검색하여 사용한다.

@SessionAttributes로 세션에서 사용 완료된 객체는  사용 후에 SessionStatus.setComplete() 메소드로  세션에 저장한 정보를 제거 해야 한다.

지속적으로 사용자의 입력 값을 유지시키기를 원하거나 여러 단계에 걸쳐 submit 되면서 완성되는 폼을 구성하는 경우에 사용가능 하다.

고객정보 입력화면 등에서 한번 입력 후 입력 값이 잘못 입력되어 경고 창을 띄우고 다시 페이지를 만든다고 생각할 때 최초 입력한 정보를 세션에 두고 이를 바탕으로 입력화면을 다시 그려준다면 좋을 것 이다. 이때 @SessionAttributes를 사용하면 된다.

또한 고객정보 등록화면이 여러 페이지에 걸쳐 입력되는 경우 @SessionAttributes를 사용한다면 미리 모델 객체를 생성하여 첫번째 고객입력화면의 내용을 객체에 저장해 두고, 다음 사용자 입력화면에서 나머지 고객정보를 입력하여 세션의 객체에 추가로 저장하면 된다. 물론 이 경우 데이터 입력 값 검증 시 에도 유용하다. 

이번에는 많은 컬럼을 가진 고객정보를 수정하는 경우를 생각해보자. 고객ID를 받아서 수정을 위해 고객정보를 화면에 뿌려주는 부분과 수정된 정보를 서버로 전송하는 단계이다. 사용자 정보 수정 화면에서 고객의 정보를 모두 다 보여주고 수정하지는 않을 것이다. 로그인 아이디 등은 대부분 사이트에서 수정할 수 없고 일부 사용자 정보는 수정이 불필요하여 수정 폼에 나타나지 않거나 나타난다 하더라도 읽기 전용으로 출력될 것이다. 

사용자가 폼을 수정하고 저장 버튼을 눌렀을 때는 원래의 고객정보객체에 담겨 있던 내용 중에서 고객정보 수정화면에서 출력했던 항목만 서버로 전송된다는 문제가 발생한다. 폼의 서브밋을 받는 컨트롤러 수정 메소드에서 만약 다음과 같이 User 오브젝트로 폼의 내용을 바인딩하게 했다면 어떻게 될 것 인가를 고민해 보자.

@RequestMapping(value="/user/updateok", method=RequestMethod.POST)
public String updateok(@ModelAttribute User user) {
    userService.updateUser(user);
    return “success";
}

@ModelAttribute 가 붙은 User 타입 메소드 파라미터를 선언했으므로 폼에서 전달된 정보는 User 오브젝트의 프로퍼티에 바인드 될 것이고, 폼 태그 내에 정의한 필드(서브밋 되는 항목)의 정보만 들어 갈 것이다. 단순히 화면에 읽기 전용으로 출력했던 항목이나 출력되지 않은 항목은 폼에서 전달되지 않으므로 updateok() 메소드의 파라미터로 전달되는 user 오브젝트에는 이런 프로퍼티 정보가 비어있게  된다.

일부 항목이 빠진 user 오브젝트를 도메인 오브젝트로 사용해서 비즈니스 로직을 처리하는 서비스 계층, DB에 결과를 업데이트해주는 DAO쪽 빈에 전달한다고 했을 때 user 도메인 오브젝트를 이용해 비즈니스 로직을 처리하도록 만든 서비스 계층의 코드는 폼에서 어떤 내용만 다시 돌려지는지 알지 못하기 때문에 user 오브젝트를 받았다면 모든 프로퍼티 값을 다 사용하려 할 것이고 일부 비즈니스 로직에서는 잘못된 값이 사용될 수도 있다. 도메인 오브젝트를 사용하도록 만든 DAO단의 업데이트 메소드는 도메인 오브젝트의 수정 가능한 모든 필드를 항상 업데이트 하므로 일부 값이 null 이거나 0인 상태로 전달되어도 그대로 DB에 반영되어 버릴 수 있다. 

이러한 문제를 해결하기 위한 방법이 @SessionAttributes를 이용하는 것이다.


 @SessionAttributes 애노테이션을 클래스 레벨에 부여하고 폼의 정보를 담을 모델 이름을 인자로 기술하면 된다.
 
@Controller
@SessionAttributes("user")
public class UserController {
 
    @RequestMapping(value = "/user/update", method = RequestMethod.GET)
    public String update(@RequestParam int id, Model model) {
        model.addAttribute("user", userService.getUser(id));
        return "updateview";
    }
 
    //세션에서 user 객체를 검색해 있으면 메소드의 인자로 넣어주고 없으면 에러가 발생한다.
    @RequestMapping(value = "/user/update", method = RequestMethod.POST)
    public String updateok(@ModelAttribute User user, SessionStatus sessionStatus) {
……
SessionStatus.setComplete();   //사용한 후 세션에 저정한 user 객체를 제거…
return “updateok”;
    }  
}



@Controller
@SessionAttributes(“user”)
public class OracleJavaController {
 
    @RequestMapping(value=“/user.htm", method=RequestMethod.GET)
    public ModelAndView handleRequest(@ModelAttribute(“user”) User user) {
        user.setName(“onj”);
        return new ModelAndView(“userview");
    } 
}

스프링은 “user”라는 이름의 객체를 세션에서 검색하고 handleRequest() 메소드에 전달한다. 만약 세션에서 찾지 못한다면 HttpSessionRequiredException 예외가 발생된다. userview가 렌더링될 때 model의 객체 “user”가 HttpServletRequest, HttpSession에 “user”라는 키값을 가지고 복사된다.




@Controller
public class OracleJavaController {
 
    @RequestMapping(value=“/user.htm", method=RequestMethod.GET)
    public ModelAndView handleRequest(@ModelAttribute(“user”) User user) {
        user.setName(“onj”);
        return new ModelAndView(“userview");
    } 
}

User의 새로운 인스턴스가 생성되고 handleRequest() 메소드의 인자로 전달된다. 만약 User Type이 인터페이스나 추상클래스라면 BeanInstantiationException  예외가 발생된다.

댓글 없음:

댓글 쓰기