Spring MVC에서 Session을 이용한 상태유지

oyeon·2021년 2월 8일
0
post-custom-banner

@SessionAttributes & @ModelAttribute

  • @SessionAttributes 파라미터로 지정된 이름과 같은 이름이 @ModelAttribute에 지정되어 있을 경우 메소드가 반환되는 값은 세션에 저장된다.
  • 아래의 예제는 세션에 값을 초기화하는 목적으로 사용되었다.
@SessionAttributes("user")
public class LoginController{
	@ModelAttribute("user")
    	public User setUpUserForm(){
        	return new User();
        }
}
  • @SessionAttributes의 파라미터와 같은 이름이 메서드의 인자로 @ModelAttribute에 있을 경우 세션에 있는 객체를 가져온 후, 클라이언트로 전송받은 값을 설정한다.
@Controller
@SessionAttributes("user")
public class LoginController {
......
  @PostMapping("/dologin")
  public String doLogin(@ModelAttribute("user") User user, Model model) {
......
  }
}

@SessionAttribute

  • 메소드에 @SessionAttribute가 있을 경우 파라미터로 지정된 이름으로 등록된 세션 정보를 읽어와서 변수에 할당한다.
@GetMapping("/info")
public String userInfo(@SessionAttribute("user") User user){
//...
//...
return "user";
}

SessionStatus

  • SessionStatus 는 컨트롤러 메소드의 파라미터로 사용할 수 있는 스프링 내장 타입이다.
  • 이 오브젝트의 setComplete() 메서드를 이용하면 현재 컨트롤러의 @SessionAttributes에 의해 저장된 오브젝트를 제거할 수 있다.
@Controller
@SessionAttributes("user")
public class UserController {
...... 
    @RequestMapping(value = "/user/add", method = RequestMethod.POST)
    public String submit(@ModelAttribute("user") User user, SessionStatus sessionStatus) {
    	......
    	sessionStatus.setComplete();
    	......
   }
 }

Spring MVC - form tag 라이브러리

  • modelAttribute속성으로 지정된 이름의 객체를 세션에서 읽어와서 form태그로 설정된 태그에 값을 설정한다.
<form:form action="login" method="post" modelAttribute="user">
Email : <form:input path="email" /><br>
Password : <form:password path="password" /><br>
<button type="submit">Login</button>
</form:form>

실습

요구사항

  • 관리자는 /loginform에서 암호를 입력해 로그인을 한다.
  • 관리자가 암호를 맞게 입력할 경우 세션에 로그인 정보가 저장된다.
  • 세션에 로그인 정보가 있을 경우 방명록에는 "삭제" 링크가 보여진다.
  • 삭제 링크를 누르면 삭제가 된다. 삭제 작업에서도 로그인 정보가 있는지를 검사해야 한다.
  1. GuestbookAdminController.java
package kr.or.connect.guestbook.controller;

import javax.servlet.http.HttpSession;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

@Controller
public class GuestbookAdminController {
	@GetMapping(path="/loginform")
	public String loginform(){
		return "loginform";
	}
	
	@PostMapping(path="/login")
	public String login(@RequestParam(name="passwd", required=true) String passwd,
			HttpSession session,
			RedirectAttributes redirectAttr){
		if("1234".equals(passwd)){
			session.setAttribute("isAdmin", "true");
		}else{	// redirect는 요청이 1번이 아니라 두 번 오는 것. vs forward
			// redirect를 한다는 것은 loginform을 다시 보내겠다는 것.
			// forward의 경우 하나의 요청이기 때문에 request영역에 넣어 사용가능했지만
			// redirect의 경우 요청이 달라지기 때문에 그럴 수 없다.
			// 그래서 session에 저장하는 등 다른 방법을 써야한다.
			// redirectAttr을 사용하면 더 효율적으로 할 수 있다. 
			redirectAttr.addFlashAttribute("errorMessage", "암호가 틀렸습니다.");
			return "redirect:/loginform";
		}
		return "redirect:/list";
	}
	
	@GetMapping(path="/logout")
	public String login(HttpSession session){
		session.removeAttribute("isAdmin");
		return "redirect:/list";
	}
}
  1. loginform.jsp
<%@ page language="java" contentType="text/html; charset=EUC-KR"
    pageEncoding="EUC-KR"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">
<title>Insert title here</title>
</head>
<body>
	<h1>관리자 로그인</h1>
	<br>
	<br> ${errorMessage}
	<br>
	
	<form method="post" action="login">
		암호 : <input type="password" name="passwd"><br>
		<input type = "submit">
	</form>
</body>
</html>
  1. GuestbookController.java
package kr.or.connect.guestbook.controller;

import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttribute;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import kr.or.connect.guestbook.dto.Guestbook;
import kr.or.connect.guestbook.service.GuestbookService;

@Controller
public class GuestbookController {
	@Autowired
	GuestbookService guestbookService;
	
	@GetMapping(path="/list")
	public String list(@RequestParam(name="start", required=false, defaultValue="0") int start,
			ModelMap model,
			@CookieValue(value="count", defaultValue="0", required=true) String value,
			HttpServletResponse response
			){
		try{
			int i = Integer.parseInt(value);
			value = Integer.toString(++i);
		}catch(Exception ex){
			value = "1";
		}
		
		Cookie cookie = new Cookie("count", value);
		cookie.setMaxAge(60 * 60 * 24 * 365); // 1년간 유지
		cookie.setPath("/"); // /경로 이하에 모두 쿠키 적용
		response.addCookie(cookie);
		
		// start로 시작하는 방명록 목록 구하기
		List<Guestbook> list = guestbookService.getGuestbooks(start);
		
		// 전체 페이지 수 구하기
		int count = guestbookService.getCount();
		int pageCount = count / GuestbookService.LIMIT;
		if(count % GuestbookService.LIMIT > 0)
			pageCount++;
		
		// 페이지 수 만큼 start의 값을 리스트로 저장
		// ex. 페이지 수가 3이면 0, 5, 10 이 저장
		// list?start=0, list?start=5, list?start=10 으로 링크 걸림
		List<Integer> pageStartList = new ArrayList<>();
		for(int i = 0; i < pageCount; i++){
			pageStartList.add(i * GuestbookService.LIMIT);
		}
		model.addAttribute("list", list);
		model.addAttribute("count", count);
		model.addAttribute("pageStartList", pageStartList);
		model.addAttribute("cookieCount", value);
		
		return "list";
	}
	
	@PostMapping(path="/write")
	public String write(@ModelAttribute Guestbook guestbook,
			HttpServletRequest request){
		String clientIp = request.getRemoteAddr();
		System.out.println("clientIp:" + clientIp);
		guestbookService.addGuestbook(guestbook, clientIp);
		return "redirect:list";
	}
	
	// 메소드에 @SessionAttribute가 있을 경우 파라미터로 지정된 이름으로 등록된 세션 정보를 읽어와서 변수에 할당
	@GetMapping(path="/delete")
	public String delete(@RequestParam(name="id", required=true) Long id,
			@SessionAttribute("isAdmin") String isAdmin,
			HttpServletRequest request,
			RedirectAttributes redirectAttr){
		if(isAdmin == null || !"true".equals(isAdmin)){ // 세션값이 true가 아닐 경우
			redirectAttr.addFlashAttribute("errorMessage", "로그인을 하지 않았습니다.");
			return "redirect:loginform";
		}
		String clientIp = request.getRemoteAddr();
		guestbookService.deleteGuestbook(id, clientIp);
		return "redirect:list";
	}
}
  1. list.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>방명록 목록</title>
</head>
<body>
<h1>방명록</h1><br>
<h4>방명록 전체 수 : ${count}	방문한 수 : ${cookieCount}</h4><br>

<c:forEach items="${list}" var="guestbook">
	${guestbook.id} <br>
	${guestbook.name} <br>
	${guestbook.content} <br>
	${guestbook.regdate} <br>
	<c:if test="${sessionScope.isAdmin == 'true'}">
		<a href="delete?id=${guestbook.id}">삭제</a><br><br>
	</c:if>
</c:forEach>
<br>

${pageStartList }<br>
<c:forEach items="${pageStartList}" var="pageIndex" varStatus="status">
		<a href="list?start=${pageIndex}">${status.index + 1 }</a>&nbsp; &nbsp;
</c:forEach>
<br><br>
<form method="post" action="write">
	name : <input type="text" name="name"><br>
	<textarea name="content" cols="60" rows="6"></textarea><br>
	<input type="submit" value="등록">
</form>
</body>
</html>
profile
Enjoy to study
post-custom-banner

0개의 댓글