Spring & SpringBoot/Spring

220427 Spring - Chapter 2. Spring MVC (Part 6)

Remind123 2022. 4. 27. 23:57

세션(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 애너테이션에 패키지를 입력하면 해당 패키지에만 적용할 수 있고

따로 패키지를 입력하지 않으면 모든 패키지에 적용할 수도 있다.

 


출처 : 스프링의 정석 : 남궁성과 끝까지 간다