@ModelAttribute가 하는 역할이 다양한데 
-. Command 객체의 이름을 변경하여 View에 넘기기 (해당 포스트는 여기를 참조)
-. Controller 클래스에 있는 데이터를 View로 넘기기 (해당 포스트는 여기를 참조)
-. @SessionAttribute가 세션에 저장한 데이터를 @ModelAttribute가 가져다 사용하기

이번 글에서는 이들 중에서 세번째에 대해서 다룬다.

@SessionAttribute("myData")의 의미
-. 특정 컨트롤러 클래스에 @SessionAttribute가 선언되어 있으면 
-. 해당 Controller 클래스의 특정 메소드에서 "myData"라는 이름으로 Model 객체에 저장되는 데이터가 있다면 그 데이터를 세션(HttpSession)에도 자동으로 저장하라는 어노테이션이다.
-. ex)

@Controller
@SessionAttributes("myData")
public class BoardController {
	
	@Autowired
	private BoardService boardService;

... 중 략 ...

	//글 상세 조회
	@RequestMapping("/getBoard.do")
	public String getBoard(BoardVO vo, Model model) throws Exception
	{
		System.out.println("GetBoardController 글 상세 조회 처리~");
		
		//아래와 같이 Model 객체에 myData라는 이름으로 저장하면
		//HttpSession에도 boardService.getBoard(vo)의 반환 데이터가
		//자동으로 저장이 된다.
		model.addAttribute("myData", boardService.getBoard(vo)); 
		
		return "getBoard.jsp";
	}

... 후 략 ...

} //class BoardController

위 코드에서와 같이 컨트롤러 클래스인 BoardController 클래스에 @SessionAttributes("myData")어노테이션이 적용되어 있으면 이 컨트롤러 클래스의 어느 메소드에서  Model에 "myData"라는 이름으로 데이터를 저장하면(여기서는 boardService.getBoard(vo)를 저장) 그 데이터를 세션(HttpSession)에도 자동으로 저장하게된다.
그러면 이렇게 세션에 저장된 데이터를 누가 어디서 어떤식으로 꺼내서 사용하는가?
그 역할 하는 것이 다름아닌 @ModelAttribute이다. 아래 코드와 같이

	//글 수정
	@RequestMapping("/updateBoard.do")
	public String updateBoard(@ModelAttribute("myData") BoardVO vo) throws Exception
	{
		System.out.println("UpdateBoardController 글 수정 처리~");
		System.out.println("▶ vo.getSeq(): "+vo.getSeq());
		System.out.println("▶ vo.getTitle(): "+vo.getTitle());
		System.out.println("▶ vo.getWriter(): "+vo.getWriter());
		System.out.println("▶ vo.getContent(): "+vo.getContent());
		System.out.println("▶ vo.getRegDate(): "+vo.getRegDate());
		System.out.println("▶ vo.getCnt(): "+vo.getCnt());
		
		boardService.updateBoard(vo);
		return "getBoardList.do";
	}

위의 updateBoard()메소드에 @ModelAttribute("myData") BoardVO vo가 내부적으로 동작하는 내용은
① 세션에 myData라는 이름으로 저장된 데이터가 있는지 확인하여(세션에는 key-value 형태로 저장된다), 데이터가 있으면 그 데이터를 세션에서 꺼내서 @ModelAttribute가 붙은 매개변수에(여기서는 BoardVO 객체인 vo에) 자동으로 저장한다. 세션에 해당 데이터가 없다면 당연히 이 과정을 일어나지 않을 것이다.

그런데 여기서 위의 updateBoard(@ModelAttribute("myData") BoardVO vo) 메소드는 게시글 수정시 호출되는 메소드이다.
따라서 게시글 수정은 기본적으로 form 형태로 이전 글 내용이 보여지고 여기서 수정하고자 하는 항목을 수정후 해당 form을 commit하는 방식이 될것이다(아래 form).
이러한 경우 Command 객체 개념이 BoardVO vo에 적용이된다. 이때 updateBoard() 메소드에는 세션에 저장된 데이터가 BoardVO vo에 저장이 되면서 동시에 form으로부터 넘어온 데이터가 Command 객체인 BoardVO vo에 또한 저장이된다. 이런 상황에서 세션 데이터와 form에서 넘어온 데이터가 어떤 식으로 Command 객체인 BoardVO vo에 저장이 되는가 하는 것이다.

Command 객체란
Controller 클래스(위에서는 BoardController)의 메소드에 매개변수로 VO 객체가 선언되어 있을때(위에서는 BoardVO vo) 이를 Command 객체라고 한다. 위에서는 updateBoard() 메소드의 BoardVO vo가 Command 객체이다. Spring 컨테이너는 이러한 Command 객체에 대해서는 내부적으로 다음과 같은 특별한 처리를 자동으로 해준다. 
-. Command 객체인 BoardVO vo 객체를 자동으로 생성하고
-. form 태그에서 입력한 값들을 추출하여 BoardVO 객체인 vo에 저장한다. 이때 BoardVO 클래스의 setter 메소드들이 호출된다.
여기서 중요한 것은 form 태그의 name 속성에 지정한 이름과 BoardVO의 멤버 변수 이름이 동일해야 한다. 
아래의 form의 
name="title"
name="content"과 VO의 멤버 변수 이름이 동일해야 한다.

	<form action="updateBoard.do" method="post">
		<table border="1" cellpadding="0" cellspacing="0">
			<tr>
				<td bgcolor="orange" width="70">제목</td>
				<td align="left"><input type="text" name="title" value="${ joe_Board.title}" /></td>
			</tr>
			<tr>
				<td bgcolor="orange">작성자</td>
				<td align="left">${ boardModel.writer}</td>
			</tr>
			<tr>
				<td bgcolor="orange">내용</td>
				<td align="left"><textarea name="content" cols="40" rows="10">${joe_Board.content}</textarea></td>
			</tr>
			... 후 략 ...
		</table>
	</form>

VO의 멤버 변수 이름이 form 태그의 name 속성에서 지정한 이름과 동일해야 한다. 아래와 같이

public class BoardVO {
	private String title;
	private String content;

	public String getTitle() { return title; }
	public void setTitle(String title) { this.title = title; }
	public String getContent() { return content; }
	public void setContent(String content) { this.content = content; }
... 후 략 ...
}

② 따라서 세션에 myData라는 이름으로 저장된 데이터를 BoardVO 객체에 할당 이후에 사용자가 게시글 수정한 정보(form 태그 안에 있는 데이터)가 Command 객체인 BoardVO 객체에 덮어쓰기로 할당이 된다.

이상이 @ModelAttribute가 @SessionAttribute와 연동할때 내부적으로 데이터가 어떻게 처리되는지를 살펴보았다.
스프링은 많은 것을 내부에서 처리하는 자동화 메커니즘을 가지고 있기 때문에 개발자는 이러한 자동화 메커니즘을 잘 숙지하고 익숙해 있어야 한다.

+ Recent posts