본문 바로가기
Java/Spring

Spring) Model (ModelAndView, ModelMap, Model), @ModelAttribute, @SessionAttribute, @SessionAttributes

by 박채니 2022. 8. 23.

안녕하세요, 코린이의 코딩 학습기 채니 입니다.

 

개인 포스팅용으로 내용에 오류 및 잘못된 정보가 있을 수 있습니다.


Model

- mvc의 model이 아닌, view단에 데이터를 전달하기 위한 임시 저장소

- Map 객체

 

① ModelAndView

- model 기능 : addObject

- view 기능 : setView(view) | setViewName(String)

② ModelMap

- model : addAttribute

- view 없음 : handler(Controller의 메소드)에서 view정보를 문자열로 반환해야 함

 

③ Model

- model : addAttribute

- view 없음 : handler(Controller의 메소드)에서 view정보를 문자열로 반환해야 함

 

 

관련 어노테이션

@ModelAttribute

- 메소드 레벨 : 해당 controller의 전역 모델 속성 등록

- 메소드 매개변수에 작성 : 모델 속성에 대한 getter

 

@SessionAttribute

- 메소드 매개변수에 작성

- session scope에 저장된 속성에 대한 getter

 

@SessionAttributes

- 클래스 레벨 작성

- session scope에 저장될 속성명 관리

 


@ModelAttribute

메소드 레벨 작성 : 해당 controller의 전역 모델 속성 등록

@ModelAttribute("common")
public Model common(Model model) {
    log.debug("@ModelAttribute - common 호출!");
    model.addAttribute("email", "honggd@abc.com");
    model.addAttribute("tel", "010-1234-1234");

    return model;
}

@콘솔출력값
DEBUG: com.ce.spring2.demo.controller.DemoController - @ModelAttribute - common 호출!
INFO : com.ce.spring2.demo.controller.DemoController - /demo/devForm.do 요청!

 

devForm.jsp

<div id="demo-container" 
	 class="border border-secondary mx-auto p-3 rounded">
	<!-- https://getbootstrap.com/docs/4.1/components/forms/#readonly-plain-text -->
	${common.email}
	${common.tel}
	<form name="devFrm">
    .. 이하 생략

전역 모델 속성으로 등록된 common의 속성들을 잘 가져오는 것을 확인할 수 있습니다.

 


@SessionAttributes

- 클래스 레벨 작성 : session scope에 저장될 속성명 관리

@SessionAttributes({"loginMember"})
public class MemberController {
	@Autowired
	private MemberService memberService;
	
	@Autowired
	private BCryptPasswordEncoder bcryptPasswordEncoder;
	
    	@PostMapping("/memberLogin.do")
	public String memberLogin(@RequestParam String memberId, @RequestParam String password, Model model, RedirectAttributes redirectAttr) {
		// 1. memberId로 조회
		Member member = memberService.selectOneMember(memberId);
		
		String location = "/";
		// 2. member가 null이 아니면서, 비밀번호가 일치하면 로그인 성공
		if(member != null && bcryptPasswordEncoder.matches(password, member.getPassword())) {
			// model을 통해 session scope에 속성 저장 : 클래스레벨에 @SessionAttributes로 등록
			model.addAttribute("loginMember", member);
		}	
		// 로그인실패
		else {
			redirectAttr.addFlashAttribute("msg", "아이디 또는 비밀번호가 일치하지 않습니다.");
			location += "member/memberLogin.do";
		}
		
		return "redirect:" + location;
	}
}

Model은 기본적으로 request scope에 저장됩니다.

하지만 경우에 따라 session scope에 저장되어야 할 때가 있는데, 이 때! model을 통해 session scope에 지정하기 위해 클래스레벨에 @SessionAttributes로 등록해줍니다.

@SessionAttributes(속성명)으로 등록해주면 session scope로 사용됩니다.

 

세션 만료 처리

SessionStatus#setComplete() - 세션 만료처리 (세션 객체 유지, 안에 내용만 제거)

/**
 * @SessionAttributes로 세션 관리를 한다면, SessionStatus#setComplete으로 만료처리! 
 */
@GetMapping("/memberLogout.do") 
public String memberLogout(SessionStatus sessionStatus) {
    if(!sessionStatus.isComplete()) {
        // 세션을 다 사용했다는 의미 - 만료 처리(세션 객체는 유지하되, 안에 내용만 제거)
        sessionStatus.setComplete();
    }

    return "redirect:/";
}

@SessionAttributes로 세션 관리를 한다면 SessionStatus#setComplete으로 만료처리해줍니다.

세션을 다 사용했다는 의미로 세션 객체는 유지하되, 안에 내용만 제거하여 세션 객체의 효율성을 높입니다.

 


@SessionAttribute

- 메소드 매개변수에 작성 : session scope에 저장된 속성에 대한 getter

@GetMapping("/memberDetail.do")
public ModelAndView memberDetail(ModelAndView mav, @SessionAttribute Member loginMember) {
    log.debug("loginMember = {}", loginMember);

    mav.setViewName("member/memberDetail");
    return mav;
}

@콘솔출력값
DEBUG: com.ce.spring2.member.controller.MemberController - loginMember = Member(memberId=honggd, password=$2a$10$G7F3EfcXE8wJWUAsCznaoOqttim4MwJBNlWey5yuL5ZRMwRSdPvEi, name=홍길동, gender=M, birthday=1999-01-19, email=honggd@abc.com, phone=01012341234, address=경기 성남시 분당구 대왕판교로 477, hobby=[운동, 독서, 여행], createdAt=2022-08-22T12:23:43, updatedAt=null, enabled=true)

@SessionAttribute를 이용해 session scope에 저장된 속성(loginMember)을 가져온 것을 확인할 수 있습니다.

ModelAndView로 view단에 전달 하였습니다.

 

memberDetail.jsp

<%@page import="java.util.List"%>
<%@page import="java.util.Arrays"%>
<%@page import="com.ce.spring2.member.model.dto.Member"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
<jsp:include page="/WEB-INF/views/common/header.jsp">
	<jsp:param value="회원정보" name="title"/>
</jsp:include>
<style>
div#update-container{width:400px; margin:0 auto; text-align:center;}
div#update-container input, div#update-container select {margin-bottom:10px;}
</style>
<div id="update-container">
	<form name="memberUpdateFrm" action="${pageContext.request.contextPath}/member/memberUpdate.do" method="post">
		<input type="text" class="form-control" placeholder="아이디 (4글자이상)" name="memberId" id="memberId" value="${loginMember.memberId}" readonly required/>
		<input type="text" class="form-control" placeholder="이름" name="name" id="name" value="${loginMember.name}" required/>
		<input type="date" class="form-control" placeholder="생일" name="birthday" id="birthday" value="${loginMember.birthday}" />
		<input type="email" class="form-control" placeholder="이메일" name="email" id="email" value="${loginMember.email}"/>
		<input type="tel" class="form-control" placeholder="전화번호 (예:01012345678)" name="phone" id="phone" maxlength="11" value="${loginMember.phone}" required/>
		<input type="text" class="form-control" placeholder="주소" name="address" id="address" value="${loginMember.address}"/>
		<select class="form-control" name="gender" required>
		  <option value="" disabled selected>성별</option>
		  <option value="M" ${loginMember.gender eq 'M' ? 'selected' : ''}>남</option>
		  <option value="F" ${loginMember.gender eq 'F' ? 'selected' : ''}>여</option>
		</select>
<%
	Member loginMember = (Member) session.getAttribute("loginMember");
	String[] hobbies = loginMember.getHobby();
	List<String> hobbyList = hobbies != null ? Arrays.asList(hobbies) : null;
	pageContext.setAttribute("hobbyList", hobbyList);
	
%>
		<div class="form-check-inline form-check">
			취미 : &nbsp; 
			<input type="checkbox" class="form-check-input" name="hobby" id="hobby0" value="운동" ${hobbyList.contains('운동') ? 'checked' : ''}>
			<label for="hobby0" class="form-check-label" >운동</label>&nbsp;
			<input type="checkbox" class="form-check-input" name="hobby" id="hobby1" value="등산" ${hobbyList.contains('등산') ? 'checked' : ''}>
			<label for="hobby1" class="form-check-label" >등산</label>&nbsp;
			<input type="checkbox" class="form-check-input" name="hobby" id="hobby2" value="독서" ${hobbyList.contains('독서') ? 'checked' : ''}>
			<label for="hobby2" class="form-check-label" >독서</label>&nbsp;
			<input type="checkbox" class="form-check-input" name="hobby" id="hobby3" value="게임" ${hobbyList.contains('게임') ? 'checked' : ''}>
			<label for="hobby3" class="form-check-label" >게임</label>&nbsp;
			<input type="checkbox" class="form-check-input" name="hobby" id="hobby4" value="여행" ${hobbyList.contains('여행') ? 'checked' : ''}>
			<label for="hobby4" class="form-check-label" >여행</label>&nbsp;
		</div>
		<br />
		<input type="submit" class="btn btn-outline-success" value="수정" >&nbsp;
		<input type="reset" class="btn btn-outline-success" value="취소">
	</form>
</div>

<jsp:include page="/WEB-INF/views/common/footer.jsp"></jsp:include>