@ModelAttribute가 Controller 메소드의 매개변수로 선언된 Command 객체의 긴 이름을 짦은 이름으로 변경할때도 사용되지만(해당 포스트는 여기를 클릭),
Controller 클래스에 있는 특정 데이터를 View(.jsp 페이지)에서 사용할수 있도록 View로 넘기는 용도로도 사용되는 별스런 역할도 할수 있다.
일단 개념부터 정리를 해 보면...

어떤 컨트롤러 클래스 안에있는 특정 메소드에 @ModelAttribute 어노테이션이 붙어 있으면 해당 컨트롤러 클래스의 모든 @RequestMapping 어노테이션이 붙은 메소드가 호출될 때마다 그 메소드 호출 전에 @ModelAttribute가 붙은 메소드가 일단 먼저 호출되고 그 이후 @RequestMapping이 붙은 메소드가 호출되는데 이때 @ModelAttribute 메소드 실행 결과로 리턴되는 객체(데이터)는 자동으로 @RequestMapping 어노테이션이 붙은 메소드의 Model에 저장이되고 그 이후에 .jsp(View)에서 @ModelAttribute 메소드가 반환한 데이터를 사용할수 있다. 
놀라운 @ModelAttribute의 능력이랄까?

일단 코드에서 확인해 보자. 아래와 같은 컨트롤러 클래스가 있다.

@Controller
public class BoardController {
	... 전 략 ...
	
	//글 수정
	@RequestMapping("/updateBoard.do")
	public String updateBoard(BoardVO vo, BoardDAO bdDao) throws Exception {
		System.out.println("UpdateBoardController 글 수정 처리~");
		
		bdDao.updateBoard(vo);
		return "getBoardList.do";
	}
	
	//글 상세 조회
	@RequestMapping("/getBoard.do")
	public String getBoard(BoardVO vo, BoardDAO bdDao, Model model) throws Exception {
		System.out.println("GetBoardController 글 상세 조회 처리~");
		
		model.addAttribute("boardModel", bdDao.getBoard(vo)); 
		return "getBoard.jsp";
	}
	
	//글 목록 검색
	@RequestMapping("/getBoardList.do")
	public String getBoardList(@RequestParam(value="searchCondition", defaultValue="TITLE", required=true) String condition,
								@RequestParam(value="searchKeyword", defaultValue="", required=false) String keyWord,
								BoardVO vo, 
								BoardDAO bdDao, 
								Model model) throws Exception {
		System.out.println("$$$$$$$ GetBoardListController 글 목록 검색 처리~");
		
		model.addAttribute("boardListModel", bdDao.getBoardList(vo));
		return "getBoardList.jsp";
	}
	
	@ModelAttribute("myModelAttribute")
	public Map<String, String> joe() {
		System.out.println("▶▶▶▶▶▶▶ 여기는 joe()~~~ ▶▶▶▶▶▶▶");
		
		Map<String, String> infoMap = new HashMap<String, String>();
		
		infoMap.put("joe", "Web Developer");
		infoMap.put("kim", "Designer");
		infoMap.put("nana", "CEO of M&P");
		
		return infoMap;
	}
}

위의 BoardController 클래스안에는 @RequestMapping 어노테이션이 붙은 메소드가 3개가 있는데
-. public String updateBoard()
-. public String getBoard()
-. public String getBoardList()

이들 메소드가 호출될때마다 @ModelAttribute("myModelAttribute") 어노테이션이 붙은 아래 메소드가 먼저 호출된다.
public Map<String, String> joe()
그리고 joe() 메소드에서 반환하는 데이터(infoMap)가 클라이언트 요청으로 실행될 @RequestMapping이 붙은 메소드의 Model 객체에 자동으로 저장이 된다. 이렇게 저장된 데이터는 .jsp 페이지에서 사용할수 있게 되는데 이때 @ModelAttribute("myModelAttribute") 안에 지정한 문자열인 myModelAttribute가 객체 이름이 된다. 
역시 .jsp에서 어떻게 데이터에 접근하는지 코드에서 확인해 보자.

예를들어 http://xxx.xxx.xx/getBoard.do로 요청이 들어오면 먼저 
public Map<String, String> joe()가 먼저 호출이 되고 그 이후
public String getBoard(BoardVO vo, BoardDAO bdDao, Model model)가 호출이 되는데 이때 joe() 메소드에서 생성된 infoMap 데이터가 getBoard()의 model 객체에 자동으로 저장이 되고 getBoard.jsp에서는 다음과 같이 데이테어 접근할수 있게 된다.

... 전 략 ...
	<h1>글 상세보기</h1>
	<a href="logout.do">로그아웃</a>
	<hr>
	
	구성원1 : ${ myModelAttribute.joe }<br/>
	구성원2 : ${ myModelAttribute.nana }<br/>
	<hr/>
... 후 략 ...

위 .jsp 페이지의 출력 결과는 다음과 같이 될 것이다.

구성원1 : Web Devloper 
구성원2 : CEO of  M&P 

혹은 다음과 같이도 할수 있다.

	<c:forEach items="${ myModelAttribute }" var="item">
		item.key : ${item.key }<br/>
		item.value : ${item.value }<br/><br/>
	</c:forEach>
	<hr/>


그러면 다음과 같은 결과가 나올 것이다.

item.key : joe
item.value : Web Developer

item.key : nana
item.value : CEO of M&P

item.key : kim
item.value : Designer

@ModelAttribute 어노테이션이 이런 용도로도 사용될수 있다는 점이다. 
그런데 @ModelAttribute는 이것 외에 또 있으니 @SessionAttribute와 연결되면 또 요술을 부린다.

Spring MVC의 @ModelAttribute 어노테이션에 대한 개념 정리

 

Spring MVC에서 @ModelAttribute을 메소드의 파라미터로 사용할 경우 프로그램이 어떤 식으로 돌아가는지를 정리하고자 한다.

다른 어노테이션에 비해 @ModelAttribute는 내부적으로 돌아가는 부분이 많은 것 같다.

즉 Spring framework이 내부에서 알아서 처리해 주는 부분이 다른 어노테이션에 비해 더  많은 것 같다. 따라서 개발자의 손을 떠나 보이지 않는 가운데서 처리되는 부분에 대한 개념이 없다면 어둠 속에서 더듬이가 될수 밖에 없는 것이다.

 

여기 다음과 같은 빈 클래스가 있다고 할때

 

public class MemberInfo 

{

     private int seq;

     private String name;

     private int age;

 

     //이하 getter, setter는 생략

}

 

http://localhost:8080/member/info?name=Gildong&age=25seq=327

 

와 같이 접속되어 들어올 때 Controller 클래스의 아래 메소드가 실행될 것이다.

 

@Controller

@RequestMapping("/member/*")

public class MemberController

{

  ... 중 략 ...

@RequestMapping(value = "/info", method=RequestMethod.GET)

public void show(@RequestParam("seq") int seq, @ModelAttribute("myMEM") MemberInfo info, Model model)

{

     logger.info("####### info.getName() "+info.getName());

     logger.info("####### info.getAge() "+info.getAge());

 

     try {

          //service.read(seq)가 MemberVO 객체를 반환한다고 할 경우

          model.addAttribute(service.read(seq));

     }catch(Exception e) {

         e.printStackTrace();

     }

}

  ... 후 략 ...

}

 

이 메소드가 실행되면 info.jsp가 자동으로 실행되게 된다. 따라서 info.jsp가 만들어져 있어야 되고 없으면 404 Not found 에러가 발생할 것이다.

그리고 info.jsp의 아래 코드에서 결과가 나오게 될 것이다.

 

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<!DOCTYPE html>

<html>

<head>

<meta charset="EUC-KR">

<title>Insert title here</title>

</head>

<body>

     <h3>회원 이름(info.name) : ${info.name }</h3> <%-- 여기서는 아무것도 안 나옴 --%>

     <hr/>

     <h3>회원 이름(myMEM.getName()) : ${myMEM.getName() }</h3>   <%-- 회원 이름이 정상적으로 출력된다. --%>

     <h3>현재 이름(myMEM.name) : ${myMEM.name }</h3>             <%-- 회원 이름이 정상적으로 출력된다 --%>

     <hr/>

     <h3>회원 번호 : ${memberVO.seq }</h3>    <%-- 회원 번호가 정상적으로 출력된다 --%>

</body>

</html>

 

JSP의 코드는 위의 내용이 전부이다. 무엇이 어떻게 돌아가기에 http url로 들어온 회원 이름과 회원 번호가 Controller에 저장이되고 Controller에서 JSP로 특별하게 보내는 코드도 없어 보이는데 JSP 코드에서 저렇게 값이 정상적으로 출력이 된단 말인가?

이것이 @ModelAttribute의 위력이고 편리함이면서 동시에 개발자를 더듬이로 만드는 측면이기도 하다.

단순한 @ModelAttribute가 무슨 역할을 했단 말인가?

 

@ModelAttribute 선언 후 자동으로 진행되는 작업들은 다음과 같다.

     ① @ModelAttribute 어노테이션이 붙은 객체를 자동으로 생성한다. 

         위의 코드에서는 MemberInfo 클래스의 객체 info를 자동으로 생성한다. 

         이때 @ModelAttribute가 지정되는 클래스는 빈 클래스라야 한다. 

         즉 MemberInfo 클래스는 beans 클래스라야 한다.

         그리고 getter와 setter가 명명 규칙에 맞게 만들어져 있어야 한다.

 

   ② 생성된 오브젝트에(info) HTTP로 넘어 온 값들을 자동으로 바인딩한다. 

       위의 코드의 경우는 name=Gildong&age=25seq=327 이렇게 들어오는 

       name, age, seq의 값이 MemberInfo의 해당 변수의 setter를 통해서 

       해당 멤버 변수에로 binding된다.

 

   ③ @ModelAttribute 어노테이션이 붙은 객체가(여기서는 MemberInfo 객체) 
       자동으로 Model 객체에 추가되고 따라서 MemberInfo 객체가 .jsp 뷰단까지 전달이 된다.

 

이상의 작업이 개발자를 대신해서 Spring framework가 알아서 다 처리해 준다. 편리하긴하다. 

이때 @ModelAttribute() 괄호 안에 지정한 문자열(위의 경우에는 myMEM)의 의미를 알아야 한다. 이 문자열의 이름으로(이것이 객체이다) Model 객체에 자동으로 추가가 되고 따라서 JSP 뷰단으로 안전하게 넘어가게 된다. 즉 MemberInfo 객체가 Model 객체에 추가될 때 @ModelAttribute()의 괄호 안에 지정한 문자열의 이름으로 추가된다는 점이다. 그리고 이 문자열 이름은 MemberInfo의 객체인 것이다. 

만일 @ModelAttribute()의 괄호 안에 아무런 문자열도 지정하지 않으면 JSP 페이지에서 MemberInfo 객체에 저장되어 있는 값을 사용할수가 없게된다.

 

보이지 않는 가운데서 내부적으로 Spring에 의해 처리되는 이상의 작업들로 인해 info.jsp에서 다음 코드가 유효하게 동작하는 것이다.

 

<h3>회원 이름(myMEM.getName()) : ${myMEM.getName() }</h3>   <%-- 회원 이름이 정상적으로 출력된다. --%>

<h3>현재 페이지(myMEM.name) : ${myMEM.name }</h3>             <%-- 회원 이름이 정상적으로 출력된다 --%>

 

여기서 ${myMEM.getName()}과 ${myMEM.name}의 차이가 무엇인고 하면 전자의 경우는 MemberInfo의 메소드를 직접 호출해서 사용한 경우이고 후자의 경우는 MemberInfo의 멤버 변수 name을 JSTL에서 사용하면 자동으로 name의 getter인 getName()이 호출되게 되는 것이다.

+ Recent posts