220427 Spring - Chapter 2. Spring MVC (Part 6)
세션(Session) - 이론
1. 세션이란?
서로 관련된 요청, 응답들을 하나로 묶은 것 - 쿠키를 이용
browser마다 개별 저장소(session객체)를 서버에서 제공, 서버에 저장
2. 세션의 생성 과정
첫 요청 -> 서버에서 무조건 세션 객체(저장소)를 생성, 세션 ID를 가지게 됨
응답헤더의 쿠키에 세션 ID를 담아서 브라우저로 전송함. 브라우저에서 쿠키를 저장.
그 다음 요청부터는 쿠키에 세션 ID가 같이 전송되고 서버는 세션ID를 통해 같은 브라우저에서 온 요청인지 구별함.
세션 ID가 일치하는 세션 저장소를 사용할 수 있게 됨.
(브라우저가 다르면 세션 ID도 다름)
3. 세션 객체 얻기
컨트롤러에서
HttpSession session = request.getSession();
session.setAttribute("id","asdf");
다음과 같이 request 객체로부터 세션 객체를 만들고 데이터를 저장할 수 있음. (서버에서 세션 ID가 일치하는 객체에 저장하게 됨)
4. 세션과 관련된 메소드
5. 세션의 종료
수동 종료)
HttpSession session = request.getSession();
session.invalidate(); // 세션을 즉시 종료
session.setMaxInactiveInterval(30*60); // 예약 종료(30분 후)
자동 종료 - web.xml
<session-config>
<session-timeout>30</sesstion-timeout>
</session-config>
(분 단위, 이 경우에는 30분)
StandardManager가 timeout된 세션 객체를 자동으로 제거함
그래도 Session을 많이 사용하면 서버에 부담이 되기 때문에 최소한으로 사용해야 한다
6. 쿠키 vs 세션
세션(Session) - 실습
1. 게시판 이용시, 미로그인이면 로그인 화면으로 이동
package com.fastcampus.ch2;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/board")
public class boardController {
@GetMapping("/list")
public String list(HttpServletRequest request) {
if(!loginCheck(request))
return "redirect:/login/login"; // 로그인을 안 했으면 로그인 화면으로 이동
return "boardList"; // 로그인을 한 상태이면, 게시판 화면으로 이동
}
private boolean loginCheck(HttpServletRequest request) {
// 1. 세션을 얻어서
HttpSession session = request.getSession();
// 2. 세션에 id가 있는지 확인
return session.getAttribute("id")!=null;
}
}
2. 로그인 후, 게시판으로 이동
package com.fastcampus.ch2;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/board")
public class boardController {
@GetMapping("/list")
public String list(HttpServletRequest request) {
if(!loginCheck(request))
return "redirect:/login/login?toURL="+request.getRequestURL(); // 로그인을 안 했으면 로그인 화면으로 이동
return "boardList"; // 로그인을 한 상태이면, 게시판 화면으로 이동
}
private boolean loginCheck(HttpServletRequest request) {
// 1. 세션을 얻어서
HttpSession session = request.getSession();
// 2. 세션에 id가 있는지 확인
return session.getAttribute("id")!=null;
}
}
먼저 request 객체를 이용해 toURL 이라는 요소를 만든다.
로그인을 안 했을 때 /login/login 에 해당 요소를 전달한다.
toURL 요소를 받기 위해 loginForm.jsp 파일에 다음과 같이
<input type="hidden" name="toURL" value="${param.toURL}">
input 태그를 추가한다.
type속성을 hidden으로 하면 해당 값이 숨겨지도록 한다.
컨트롤러에서는
package com.fastcampus.ch2;
import java.net.URLEncoder;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
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.RequestMapping;
@Controller
@RequestMapping("/login")
public class LoginController {
@GetMapping("/login")
public String loginForm() {
return "loginForm";
}
@GetMapping("/logout")
public String logout(HttpSession session) {
// 1. 세션을 종료
session.invalidate();
// 2. 홈으로 이동
return "redirect:/";
}
@PostMapping("/login")
public String login(String id, String pwd, String toURL, boolean rememberId,
HttpServletRequest request, HttpServletResponse response) throws Exception {
// 1. id와 pwd를 확인
if(!loginCheck(id, pwd)) {
// 2-1 일치하지 않으면, loginForm으로 이동
String msg = URLEncoder.encode("id 또는 pwd가 일치하지 않습니다.", "utf-8");
return "redirect:/login/login?msg="+msg;
}
// 2-2. id와 pwd가 일치하면,
// 세션 객체를 얻어오기
HttpSession session = request.getSession();
// 세션 객체에 id를 저장
session.setAttribute("id", id);
if(rememberId) {
// 쿠키를 생성
Cookie cookie = new Cookie("id", id);
// 응답에 저장
response.addCookie(cookie);
} else {
// 쿠키를 삭제
Cookie cookie = new Cookie("id", id);
cookie.setMaxAge(0);
// 응답에 저장
response.addCookie(cookie);
}
toURL = toURL==null || toURL.equals("") ? "/" : toURL;
// 3. 홈으로 이동
return "redirect:"+toURL;
}
private boolean loginCheck(String id, String pwd) {
return "asdf".equals(id) && "1234".equals(pwd);
}
}
POST 메소드에 매개변수로 toURL을 추가하고 세션 객체를 얻어와서 id를 저장한다.
메소드 마지막에
toURL = toURL==null || toURL.equals("") ? "/" : toURL;
// 3. 홈으로 이동
return "redirect:"+toURL;
다음과 같은 코드를 추가했다.
toURL의 값이 존재한다면 그 URL로 이동할 수 있도록 하는 코드이다.
이렇게 해서 비로그인 상태에서 게시판을 클릭했을 때, 로그인 후 다시 게시판으로 이동할 수 있게 되었다.
3. 세션을 시작할까?
세션은 서버에 부담이 많이 가기 때문에 세션 유지기간을 가능한 짧게 해야 한다.
session = "true" -> 세션 없을 때 세션을 생성함.
session = "false" -> 세션 없을 때 세션을 생성하지 않음.
세션이 있을때는 true나 false의 차이가 없음. 두 경우 모두 추가 세션을 만들지 않음.
다만 세션이 없을 때 생성하느냐 마느냐의 차이인데,
세션이 필요없는 페이지에서는 세션이 없어도 생성할 필요가 없기 때문에 false로 설정하는게 좋다.
예외처리 - 실습
package com.fastcampus.ch2;
import java.io.FileNotFoundException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ExceptionController2 {
@ExceptionHandler({NullPointerException.class, FileNotFoundException.class})
public String catcher2(Exception ex, Model m) {
m.addAttribute("ex", ex);
return "error";
}
@ExceptionHandler(Exception.class)
public String catcher(Exception ex, Model m) {
m.addAttribute("ex", ex);
return "error";
}
@RequestMapping("/ex3")
public String main() throws Exception {
throw new Exception("예외가 발생했습니다.");
}
@RequestMapping("/ex4")
public String main2() throws Exception {
throw new FileNotFoundException("예외가 발생했습니다.");
}
}
다음과 같이 @ExceptionHandler 애너테이션을 붙여서 예외를 처리하는 메소드를 만들고,
모델에 예외를 저장하여 error.jsp 파일에 전달한다.
또는 다음과 같이, 따로 예외만 처리하는 컨트롤러를 만들수도 있다.
package com.fastcampus.ch2;
import java.io.FileNotFoundException;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice("com.fastcampus.ch2")
//@ControllerAdvice // 모든 패키지에 적용
public class GlobalCatcher {
@ExceptionHandler({NullPointerException.class, FileNotFoundException.class})
public String catcher2(Exception ex, Model m) {
m.addAttribute("ex", ex);
return "error";
}
@ExceptionHandler(Exception.class)
public String catcher(Exception ex, Model m) {
m.addAttribute("ex", ex);
return "error";
}
}
@ControllerAdvice 애너테이션에 패키지를 입력하면 해당 패키지에만 적용할 수 있고
따로 패키지를 입력하지 않으면 모든 패키지에 적용할 수도 있다.
출처 : 스프링의 정석 : 남궁성과 끝까지 간다