2014년 2월 4일 화요일

Spring @ModelAttribute의 쓰임새

몇일전 @ModelAttribute와 @SessionAttributes의 이해와 한계라는 글을 읽다 내가 알고있는 @ModelAttribute의 역할과는 달라서 조금 길게 덧글을 써보고자 한다. 위 글에서는 @ModelAttribute의 역할을 다음과 같이 정의하고 있다.
@ModelAttribute는 다른 말로 커맨드 오브젝트라고도 불리는데 그 이유는 클라이언트가 전달하는 파라미터를 1:1로 전담 프로퍼티에 담아내는 방식이 커맨드 패턴 그 자체이기 때문이다. 위의 이미지와 같이 @ModelAttr ibute는 클라이언트의 파라미터를 완벽하게 이해하고 있는 자바빈 객체를 요구하며 이 객체에 @ModelAttribu te란 어노테이션를 붙이면 자동으로 바인딩해주고 중간 과정은 모두 자동으로 생략 해준다.
내가 아는 한 Spring @MVC는 @ModelAttribute와는 관계없이 데이터 바인딩과 모델에 담는 일을 처리해준다. 즉, 다음과 같이 코드를 작성하면 동일한 결과를 얻을 수 있다.
@Controller
public class HomeController {

    @RequestMapping("/command")
    public String command(CommandObject command) {
        return "command";
    }

    @RequestMapping("/modelCommand")
    public String modelCommand(@ModelAttribute CommandObject command) {
        return "command";
    }

}
 
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>



    이름: 
별명:
command() / modelCommand() 핸들러는 둘 다 데이터 바인딩 / 모델에 값 유지가 된다. 위 코드만 봐도 @ModelAttribute가 데이터 바인딩과는 관계가 없다는것을 알 수 있다. 그렇다면 @ModelAttribute는 무엇에 쓰는 녀석인가? 난 다음과 같이 총 4가지 유형으로 @ModelAttribute 쓰임새를 정리할 수 있다고 생각한다.

1. formBackingObject or referenceData 로 사용

formBackingObject/referenceData는 Spring 3.0 이전에 상속기반의 MVC에서 사용하던 용어로 View에 필요한 기본값을 내려보낼때 사용한다.
@Controller
public class HomeController {

    @ModelAttribute("commandTypes")
    public Set commandTypes() {
        Set commandTypes = new HashSet();
        commandTypes.add("commandType1");
        commandTypes.add("commandType2");
        commandTypes.add("commandType3");
        return commandTypes;
    }

    @RequestMapping("/command")
    public String command(@RequestParam(required=false) String commandType) {
        if(commandTypes().contains(commandType)) {
            // 명령 수행
        }
        return "command";
    }

}
View에서 ‘commandTypes’로 선택 가능한 명령을 보여주고, 입력받아서 처리하는 것이다.

2. 핸들러(@RequestMapping)에 넘어가는 인자의 이름 설정

첫번째 샘플코드에서 본것처럼 스프링은 핸들러가 받는 인자는 모델에 담아서 View로 보낸다. 여기서 정해진 이름이 없다면 스프링은 정해진 규칙(클래스 이름을 기준으로 첫자는 소문자로 이후는 유지)대로 이름을 만들어준다. CommandObject를 받을 때 이름을 설정하지 않았다면 ‘commandObject’라는 이름을 만들어주는 것이다. 개발자가 직접 이름을 지정할 필요가 있다면 다음과 같이 코드를 작성해서 설정해줄 수 있다.
@Controller
public class HomeController {

    @RequestMapping("/command")
    public String command(CommandObject command, Model model) {
        model.addAttribute("command", command);
        return "command";
    }

    @RequestMapping("/modelCommand")
    public String modelCommand(@ModelAttribute("command") CommandObject command) {
        return "command";
    }

}
3. @SessionAttributes로 유지된 객체를 받을 때
@Controller
@SessionAttributes("command")
public class HomeController {

    @ModelAttribute("command")
    public CommandObject commandObject() {
        return new CommandObject();
    }

    @RequestMapping("/command1")
    public String command1(@ModelAttribute("command") CommandObject command) {
        return "command";
    }

    @RequestMapping("/command2")
    public String command2(@ModelAttribute("command") CommandObject command) {
        return "command";
    }

}
command1()과 command2()에서 받는 command 객체는 commandObject()로 생성된 객체가 Session으로 유지되어 넘어온 객체이다.  

댓글 2개:

  1. @ModelAttribute는 생략가능하지 않나요? 결국 둘다 똑같은거 같은데 ㅠㅠ

    답글삭제
  2. @ModelAttribute 안에 선언한 속성이랑 CommandObject command에 변수선이랑 같게 해야하는건가요????

    답글삭제