[Spring] 04. 쿠키/세션/예외처리

Hyeongmin Jung·2023년 6월 4일
0

Spring

목록 보기
4/17

🐣 쿠키

: 클라이언트 식별기술. name과 value의 쌍으로 구성된 작은 정보
✔️ 이름, 값, 유효기간, 도메인, 경로
✔️ 아스키 문자만 가능, 한글의 경우 URL인코딩
✔️ 서버에서 생성 후 전송, 브라우저에 저장, 유효기간 이후 자동삭제
✔️ 서버에서 요청시 domain, path가 일치하는 경우(하위경로 포함)에만 자동전송

✅ 상대시간: Max-Age=86400
✅ 절대시간: Expires=Thu, 1-Jun-2023 11:12:15 GMT

Domain: fastcampus.co.kr
path: /ch2/login
id=hello
Max-Age: 606024

🍪쿠키 생성과정

❶ 클라이언트가 페이지 요청
❷ 서버에서 쿠키 생성, HTTP 응답 헤더에 쿠키를 포함시켜 응답(Set-Cookie)
❸ 브라우저에 쿠키 저장, 같은 요청을 보내는 경우 자동적으로 HTTP 요청 헤더에 Cookie와 함께 보냄
❹ 서버에서 쿠키를 읽어 클라이언트 식별하여 응답

🍪쿠키 생성

Cookie cookie = new Cookie("id", "hello"); //쿠키생성
cookie.setMaxAge(60*60*24);  //유효기간 설정(sec)
response.addCookie(cookie);  //응답에 쿠키 추가

🍪쿠키 삭제

Cookie cookie = new Cookie("id", ""); //변경할 쿠키와 같은 이름의 쿠키 생성
cookie.setMaxAge(0);  //유효기간 0으로 설정(삭제)
response.addCookie(cookie); //응답에 쿠키 추가

🍪쿠키 변경

Cookie cookie = new Cookie("id", ""); //변경할 쿠키와 같은 이름의 쿠키 생성
cookie.setValue(URLEncoder.encode("김아무개"));  //값 변경
cookie.setDomain("www.fastcampus.co.kr");  //도메인 변경
cookie.setPath("/ch2");  //경로 변경
cookie.setMaxAge(60*60*24*7);  //유효기간 변경
response.addCookie(cookie); //응답에 쿠키 추가

🍪쿠키 읽어오기

Cookie[] cookies = request.getCookies();  //쿠키읽기, cookie 없으면 null
for(Cookie cookie:cookies){
	String name=cookie.getName();
    String value=cookie.getValue();
    
    System.out.printf("[cookie]name=%s, value=%s%n", name, value);
} 

실습

// loginController
// 1. id와 pwd를 확인
if(!loginCheck(id, pwd)) {
	// 2-1. 일치하지 않으면, loginForm으로 이동
	String msg = URLEncoder.encode("id 또는 pwd가 일치하지 않습니다.", "utf-8");
	// redirect 시 Get으로 이동
	return "redirect:/login/login?msg="+msg;
}
        
// 2-2. id와 pwd가 일치할 때
if(rememberId) {
	Cookie cookie = new Cookie("id", id); //쿠키 생성
	response.addCookie(cookie); //응답에 저장
} else {
	Cookie cookie = new Cookie("id", id); 
	cookie.setMaxAge(0); //쿠키 삭제
	response.addCookie(cookie); //응답에 저장
}

🐣 세션

쿠키를 이용하여 서로 관련된 독립적인 요청/응답들을 하나로 묶은 것. login-logout
✔️ 브라우저마다 개별저장소(session객체)를 서버에 제공
✔️ 쿠키는 브라우저마다 저장되기 때문에 서로 다른 PC또는 같은 PC라도 서로 다른 브라우저를 사용한다면 다른 세션 ID를 가짐
✔️ 쿠키를 허용하지 않는 브라우저의 경우 url태그를 사용하면 자동으로 get방식을 이용하여 세션id를 주소에 붙여줌 <c:url value='/login/login'/>

HttpSession session = request.Session; //Http요청헤더로 부터 받아오는 것이므로 Request
session.setAttribute("id", "hello");

⚙️세션 생성과정

❶ 클라이언트가 서버에 처음으로 요청을 보내면 무조건 세션 객체 생성
❷ 세션ID가 담긴 쿠키를 만들어 응답으로 보냄(Set-Cookie)
❸ 브라우저에 쿠키저장, 그 다음부터 요청을 보낼때 쿠키가 자동으로 따라감
❹ 서버는 같은 세션ID를 통해 같은 브라우저에서 온 요청인지 식별

메서드설명
String getId()세션id 반환
long getLastAccessedTime()세션 내 최근(마지막) 요청을 받은 시간 반환
boolean isNew()새로 생성된 세션인지 반환, request.getSeesion() 호출 후 사용
void invalidate()세션 객체 제거, 즉시종료 ex.logout
void setMaxInactiveInterval(int interval)예약종료
int getMaxInactiveInterval()예약된 세션종료 시간 반환

속성 관련 메서드설명
void setAttribute(String name, Object value)value를 name에 저장
Object getAttribute(String name)name에 저장된 value 반환
void removeAttribute(String name)지정된 name의 속성 삭제
Enumeration getAttributeNames()기본객체에 저장된 모든 속성의 이름 반환

⚙️세션 종료

✅ 수동종료: sec 단위 ex. 로그아웃

HttpSession session = request.getSession();
session.invalidate(); //즉시종료
session.setMaxInactiveInterval(30*60) //예약종료(30분후)

✅ 자동종료: web.xml에 추가, min 단위

<session-config>
	<session-timeout>30</session-timeout>
</session-config>

⚙️세션 시작

  • session: 세션이 있을 때 세션 만들지 않음

    ex. 로그인 전까지 false, 로그인 후 board 세션 true/home 세션 false
    <%@ page session="false" %>

✅ ="true": 세션 없을 때 생성

✅ ="false": 세션 없을 때 새로 생성 X
✔️ 세션에 필요없는 jsp화면
session="false"가 기존 세션에 영향X이미 세✔️ 션이 있는 경우 삭제하지는 않지만 없는 경우 새로 만들지 않음
✔️ sessionScope와 pageContext.session은 사용 불가, sessinScope.id를 pageContext.request.getSession(false).getAttribute("id")로 변경해야함
(sts에서 에러라고 표시해도 무시)_
cf. getSession(true)는 session이 없는 경우 session을 새로 생성하기 때문에 session이 없어도 새로 생성하지 않도록 getSession(false) 사용

쿠키 vs. 세션

@CookieValue 어노테이션

public String login(
@CookieValue("id") String cookieId,
@CookieValue("JSESSIONID") String sessionId
){}


실습

//loginController
@GetMapping("/logout")
public String logout(HttpSession session) {
	session.invalidate(); //세션종료
	return "redirect:/main"; //홈으로 이동
	}
//loginController-login
HttpSession session = request.getSession(); //세션객체 얻어오기
session.setAttribute("id", id); //새션객체에 id저장
//index.jsp

<c:set var="loginOutLink" value="${sessionScope.id==null ? '/login/login' : '/login/logout'}"/>
<c:set var="loginOut" value="${empty sessionScope.id ? 'Login' : 'Logout'}"/>

<li><a href="<c:url value='${loginOutLink}'/>">${loginOut}</a></li>    
  • from: request.getHeader("referrer")
  • to: request.getRequestURL()

    [http://localhost:8080/ch2/main] -> GET[/ch2/login/logout] 소요시간=95ms
    [http://localhost:8080/ch2/main] -> GET[/ch2/main] 소요시간=34ms
    [http://localhost:8080/ch2/main] -> GET[/ch2/main] 소요시간=11ms
    [http://localhost:8080/ch2/main] -> GET[/ch2/login/login] 소요시간=18ms
    [http://localhost:8080/ch2/login/login] -> POST[/ch2/login/login] 소요시간=89ms

//from->to, PerformanceFilter.java
HttpServletRequest req = (HttpServletRequest)request;
String referer = req.getHeader("referer");  //from
String method = req.getMethod();
System.out.print("["+referer+"] -> "+method+"["+req.getRequestURI()+"]");

//boardController:
return "redirect:/login/login?toURL="+request.getRequestURL();

loginForm에서 id와 pw를 입력받아 넘겨줄 때 toURL과 같이 넘겨줌

//loginForm
<input type="hidden" name="toURL" value=${param.toURL}>
//loginController-login
toURL = toURL==null || toURL.equals("") ? "/main" : toURL;
return "redirect:/"+toURL;

🐥 예외처리

try-catch

@RequestMapping("/ex")
public String main(Model m) throws Exception {
	try{
		throw new Exception("예외가 발생했습니다.");
    } catch(Exception e) {
    	return "error";
    }
}

@ExceptionHandler와 @ControllerAdvice

@ExceptionHandler: 예외처리를 위한 메서드를 작성하고 @ExceptionHandler를 앞에 붙임

//클래스 내 예외처리, 다른 컨트롤러에서 작동X
@Controller
public class ExceptionController {
	@ExceptionHandler(NullPointerException.class)
	public String catcher2(Exception ex) {
		return "error";
	}

	@ExceptionHandler(Exception.class)
	public String catcher(Exception ex) {
		return "error";
	}
	
	@RequestMapping("/ex")
	public String main(Model m) throws Exception {
		throw new Exception("예외가 발생했습니다.");
	}

	@RequestMapping("/ex2")
	public String main2() throws Exception {
		throw new NullPointerException("예외가 발생했습니다.");
	}
}

@ControllerAdvice: 전역 예외처리 클래스 작성 가능(패키지 지정 가능)
✔️ 예외처리 메서드가 중복된 경우, 컨트롤러 내(가까운 곳)의 예외 처리 메서드가 우선

//@ControllerAdvice("com.fastcampus.ch3") // 지정된 패키지에서 발생한 예외만 처리
@ControllerAdvice // 모든 패키지에 적용
public class GlobalCatcher {
	@ExceptionHandler({NullPointerException.class, FileNotFoundException.class})
	public String catcher2(Exception ex) {
		return "error";
	}
	
	@ExceptionHandler(Exception.class)
	public String catcher(Exception ex) {
		return "error";
	}
}

✴️ 처리할 예외가 여러 개일 시 {} 배열 이용

✴️ 예외처리 메서드의 Model객체와 컨트롤러 메서드의 Model객체는 다른 객체임

✴️ < %@ page isErrorPage=“true”> 을 작성하면
ExceptionController에 m.addAttribute(“ex”, ex) 문장없이 기본 exception 객체 사용 가능
error.jsp에 ex 변경 -> pageContext.exception

error.jsp: ex -> pageContext.exception

<%@ page isErrorPage="true"%>
<body>
<h1>예외가 발생했습니다.</h1>
발생한 예외 : ${pageContext.exception}<br>
예외 메시지 : ${pageContext.exception.message}<br>
<ol>
<c:forEach items="${pageContext.exception.stackTrace}" var="i">
	<li>${i.toString()}</li>
</c:forEach>
</ol>
</body>

@ResponseStatus

: 응답메세지의 상태코드를 변경할 때 사용
❶ 예외처리 메서드 앞에 사용
✔️ 에러가 발생하면 error.jsp로 이동하기 때문에 상태코드 200(요청처리성공), 하지만 에러가 발생했으므로 에러코드를 400(client) or 500(서버)에러로 바꿔줘야함
✔️ ex) 500(default, HttpStatus.INTERNAL_SERVER_ERROR)

@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED) // 405: Method not allowed(200 -> 405)
@ExceptionHandler({NullPointerException.class, ClassCastException.class})
public String catcher(Exception ex, Model m) {
	m.addAttribute("ex", ex);
	return "error";
}

❷ 사용자 정의 예외클래스 앞에 사용
✔️ @ResponseStatus을 쓰지 않으면 디폴트 500, 다른 결과값을 얻고싶을 때 사용

@ResponseStatus(HttpStatus.BAD_REQUEST) // 400: Bad Request(500 -> 400)
public MyException extends RuntimeException{
	MyException(String msg){
    	super(msg);
    }
	MyException(){
    	this("");
    }
}


< error-page >

:상태코드 별 뷰 맵핑, web.xml에 추가

// ExceptionController2
@ResponseStatus(HttpStatus.BAD_REQUEST) // 500 -> 400
class MyException extends RuntimeException {
	MyException(String msg) {
		super(msg);
	}
	MyException() { this(""); }
}
<!-- web.xml -->
<error-page>
	<error-code>400</error-code>
	<location>/error400.jsp</location>
</error-page>

SimpleMappingExceptionResolver

: 예외 종류 별 뷰 맵핑에 사용, servlet-context.xml에 등록
✔️ view-controller와 비슷
✔️ statusCodes로 뷰에대한 상태코드 설정(성공코드 200이 아닌 에러코드)

<beans:bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
	<beans:property name="defaultErrorView" value="error"/>
    	<beans:property name="exceptionMappings">
      		<beans:props>
        		<beans:prop key="com.fastcampus.ch2.MyException">error400</beans:prop>
      		</beans:props>
    	</beans:property>
	<beans:property name="statusCodes">
		<beans:props>
        		<beans:prop key="error400">400</beans:prop>
		</beans:props>
	</beans:property>
 </beans:bean>

예외처리 동작


참고) 자바의 정석 | 남궁성과 끝까지 간다

0개의 댓글