서론
이번에는 쿠키와 세션에 대해서 공부해보았다.
강의를 들으며 예제를 따라해보니 쉽고 재밌게 공부할 수 있어서 좋은 것 같다!.
쿠키 (Cookie)
- 사용자가 웹 사이트를 탐색하는 동안 웹 서버에서 생성되고, 사용자의 웹 브라우저에 저장되는 작은 데이터
- 클리아언트에 300개까지 저장 가능
- 하나의 도메인 당 20개의 값만 가질 수 있음
- 하나의 쿠키 값은 4KB까지 저장 가능
- 유효시간 이후 자동으로 삭제 된다.
유효시간 설정 방법
cookie.setMaxAge(10 * 60); //10 * 60초 (10분)
구성요소
- 이름 : 쿠키를 구별하는데 사용되는 이름
- 값 : 쿠키의 이름과 관련된 값
- 유효시간 : 쿠키가 유지되는 시간
- 도메인 : 쿠키를 전송할 도메인
- 경로 : 쿠키를 전송할 요청 경로
동작방식
- 클라이언트가 페이지를 요청
- 서버에서 쿠키를 생성
- Responese 헤더에 쿠키를 포함 시켜 응답
- 브라우저가 종료되어도 쿠키 만료 기간이 있다면 클라이언트에서 보관
- 같은 요청을 할 경우 Request 헤더에 쿠키를 포함시켜 요청
- 서버에서 쿠키를 읽어 상태 정보를 변경할 필요가 있을 때 쿠키를 업데이트하고,
변경된 쿠키를 Response 헤더에 포함시켜 응답
실습
- 로그인 화면에서 아이디를 기억하는 기능을 사용하기 위함
시나리오
- 클라이언트가 로그인 페이지를 요청 + 아이디 기억 선택 후 로그인
- 서버에서 아이디 기억을 확인 후 true이면, 쿠키를 생성 후 Response 헤더에 쿠키를 추가하여 응답
- 브라우저는 쿠키를 저장
- 이후, 다시 로그인 페이지를 요청 시 Request 헤더에 쿠키를 포함시켜 요청
- 서버에서 쿠키를 읽어 Response 헤더에 포함시켜 응답
@PostMapping("/login")
public String login(String id, String pwd, boolean rememberId,
HttpServletRequest request, HttpServletResponse response) throws Exception {
if(!loginCheck(id, pwd)) {
String msg = URLEncoder.encode("id 또는 pwd가 일치하지 않습니다.", "utf-8");
return "redirect:/login/login?msg="+msg;
}
if(rememberId) {
Cookie cookie = new Cookie("id", id);
response.addCookie(cookie);
}
else {
Cookie cookie = new Cookie("id", id);
cookie.setMaxAge(0);
response.addCookie(cookie);
}
return "redirect:/";
}
아이디 기억을 선택한 경우, 새로운 쿠키를 생성하고, response에 쿠키를 추가하여 응답한다.
만약 아이디 기억을 선택하지 않은 경우, setMaxAge(0)을 통해, 저장되어 있던 쿠키도 삭제하도록 한다.
여기서 setMaxAge(0)은 쿠키의 유지시간을 0으로 만드는 것으로 쿠키 삭제를 하기 위함이다.
현재 쿠키 저장소를 확인해보면, id와 관련된 쿠키가 존재하지 않는 것을 확인할 수 있다.
왼쪽처럼 아이디 기억을 체크하고 로그인을 수행하였다.
그 결과 오른쪽 이미지의 Response 헤더에 Set-Cookie에 id쿠키가 포함되어 응답된 것을 확인할 수 있다.
즉, 서버에서 쿠키를 만들어 클라이언트로 전송한 것이다.
또한, 쿠키 저장소에 자동으로 쿠키가 저장된 것을 확인할 수 있다.
다시, 클라이언트가 로그인 페이지를 요청하면 Request 헤더에 쿠키가 포함되어 요청하는 것을 확인할 수 있으며,
오른쪽 이미지 처럼, ID가 자동으로 채워져 있는 것을 확인할 수 있다.
사용 방법
//쿠키 생성
Cookie cookie = new Cookie("name", "value"); //이름, 값으로 쿠키 생성
cookie.setMaxAge(10 * 60); //쿠키의 유효시간 설정 (초)
//쿠키 삭제
cookie.setMaxAge(0); //쿠키의 유효시간을 0초로 설정
//쿠키 수정
cookie.setValue(URLEncoder.encode("값")); //쿠키의 값을 변경, 한글사용하려면 인코딩해야함
cookie.setDomain("www.example.com"); //도메인 변경
cookie.setPath("/app"); //경로 변경
//응답에 쿠키를 추가
response.addCookie(cookie);
//쿠키 읽기 (여러개 이므로 쿠키 배열)
Cookie cookies[] = request.getCookies();
for(Cookie cookie : cookies){
String name = cookie.getName();
String value = cookie.getValue();
}
@CookieValue("name")
@PostMapping("/login")
public String login(@CookieValue("id") String cookieId,
String id, String pwd, String toURL, boolean rememberId,
HttpServletRequest request, HttpServletResponse response) throws Exception {
}
요청에 들어온 쿠키 중 선택하여, 가져올 수 있다.
ex) JSESSIONID를 가져오려면
@CookieValue("JSESSIONID") String jsessionId
세션 (Session)
- 서로 관련된 요청들을 하나로 묶은 것 (쿠키를 이용한다.)
- 브라우저마다 개별 저장소 (session객체)를 서버에서 제공
- 사용자가 많아질수록 서버 메모리를 많이 차지한다.
동작방식
- 클라이언트가 서버에 접속 시 세션 ID를 받음
- 클라이언트는 세션 ID를 쿠키를 사용해서 저장
- 클라이언트는 서버에 요청할 때, 세션 ID를 같이 서버에 전달해서 요청
- 서버는 세션 ID를 전달 받아서 별다른 작업 없이 세션 ID로 세션에 있는 클라이언트 정보를 가져와서 사용
- 클라이언트 정보를 가지고 서버 요청을 처리하여 클라이언트에게 응답
세션 ID 발급 방법
1. 쿠키를 사용하는 경우
위처럼, 쿠키를 사용하여, Response 헤더에 세션 ID를 추가하여 보내주는 방법이다.
쿠키 저장소에 저장되는 것을 확인할 수 있다.
2. 쿠키를 허용하지 않은 경우
만약 클라이언트에서 쿠키를 허용하지 않은 경우에는, Set-Cookie가 아무런 효과가 없다. (저장되지 않기 때문에)
따라서, 모든 요청에 세션 ID를 URL 뒤에 붙혀서 처리해주어야 한다.
서버에서 jstl의 <c:url> 을 통해 자동으로 추가된다.
실습
- 게시판을 보기 위해서는 로그인을 해야함
- 로그인이 된 것을 세션에 저장해두어, 다시 로그인 하게 되는 일이 없도록 하기 위함
- 로그인이 된 경우 LogOut, 로그인이 되지 않은 경우, LogIn이 나오도록 하기 위함
- 게시판에서 로그인이 되지 않은 상태에서 로그인 시
홈 화면이 아닌 게시판으로 다시 이동하는 것이 자연스러운 흐름임
시나리오
- 로그인 시 세션에 id를 저장
- 게시판 이동 시
1) 세션에 id가 있다면 게시판 화면을 클라이언트에 전송
2) 세션에 id가 없다면 로그인 화면으로 리다이렉트 - 로그인 된 상태인 경우 LogOut, 반대의 경우 LogIn이 나오도록
- 게시판에서 로그인 화면으로 이동 후 로그인 하면 다시 게시판으로 이동
@GetMapping("/list")
public String list(HttpServletRequest request) {
//로그인되지 않은 경우 로그인화면으로 이동
//로그인 후 다시 게시판으로 이동하기 위해서 toURL을 추가
if(!loginCheck(request)) {
return "redirect:/login/login?toURL=" + request.getRequestURL();
}
return "boardList";
}
private boolean loginCheck(HttpServletRequest request) {
HttpSession session = request.getSession();
return session.getAttribute("id") != null;
}
세션을 확인하여, id가 있으면 로그인이 된 것이므로 boardList를 전송,
id가 없으면, RequestURL을 추가하여 로그인페이지로 리다이렉트
Home 화면에서, 로그인 되지 않은 상태 (Login이 나옴)에서 Board로 이동
이동 시 URL에 추가된 toURL과, 이를 통해 추가된 hidden태그의 value를 채워주게 된다.
@PostMapping("/login") //toURL 추가
public String login(String id, String pwd, String toURL, boolean rememberId,
HttpServletRequest request, HttpServletResponse response) throws Exception {
if(!loginCheck(id, pwd)) {
String msg = URLEncoder.encode("id 또는 pwd가 일치하지 않습니다.", "utf-8");
return "redirect:/login/login?msg="+msg;
}
//로그인 성공 시 세션에 id를 저장
HttpSession session = request.getSession();
session.setAttribute("id", id);
if(rememberId) {
Cookie cookie = new Cookie("id", id);
cookie.setMaxAge(10 * 60);
response.addCookie(cookie);
}
else {
Cookie cookie = new Cookie("id", id);
cookie.setMaxAge(0);
response.addCookie(cookie);
}
//toURL이 있을 시 toURL로 이동시키기 위해서 (없으면 홈으로 리다이렉트)
toURL = toURL == null || toURL.equals("") ? "/" : toURL;
return "redirect:"+toURL;
}
세션을 저장하는 부분과, toURL을 추가하여, 로그인 시 요청 URL로 리다이렉트
게시판에서 로그인 후 성공하여 다시 게시판으로 바로 이동된 것을 확인할 수 있다.
@GetMapping("/logout")
public String logout(HttpSession session)
session.invalidate();
return "redirect:/";
}
마지막으로 로그아웃 시 session.invalidate()를 통해 세션을 종료한다.
<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session = "true" %>
<c:set var="loginOutLink" value="${sessionScope.id == null ? '/login/login' : 'login/logout'}"/>
<c:set var="loginOut" value="${sessionScope.id == null ? 'Login' : 'Logout'}"/>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Spring</title>
<link rel="stylesheet" href="<c:url value='/resources/css/menu.css'/>">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.8.2/css/all.min.css"/>
</head>
<body>
<div id="menu">
<ul>
<li id="logo">Spring</li>
<li><a href="<c:url value='/'/>">Home</a></li>
<li><a href="<c:url value='/board/list'/>">Board</a></li>
<!--${loginOutLink}와 ${loginOut}을 사용하는 부분-->
<li><a href="<c:url value='${loginOutLink }'/>">${loginOut}</a></li>
<li><a href="<c:url value='/register/add'/>">Sign in</a></li>
<li><a href=""><i class="fas fa-search small"></i></a></li>
</ul>
</div>
<div style="text-align:center">
<h1>This is HOME</h1>
<h1>This is HOME</h1>
<h1>This is HOME</h1>
</div>
jstl의 <c:set>과 3항연산자를 통해 세션에 id가 있으면 로그인 된 것이므로, 로그아웃을,
반대의 경우 로그인을 화면에 보여주도록 한다.
사용 방법
//세션 가져오기
HttpSession session = request.getSession();
//세션에 값 저장하기
session.setAttribute("name", "value");
//세션 값 조회하기
session.getAttribute("name");
//세션 값 삭제하기
session.removeAttribute("name"); //name에 해당하는 세션 정보를 삭제
session.invalidate(); //모든 세션을 한번에 삭제
<%@ page session = (true, false) %>
세션은 서버에 부하가 많이 가기 때문에, 최대한 적게 사용할수록 좋다.
그렇다면, 세션이 필요하지 않은 페이지에서 세션을 생성하지 않고 필요한 상황에 세션을 생성하게 된다면,
서버에 부담을 줄여줄 수 있다.
(예를 들어, 로그인 화면에서는 id를 저장하기 위한 세션이 필요하지 않다.
로그인이 된 상태에서 부터 세션에 id를 저장하기 위해서 필요하기 때문이다.)
따라서, loginForm.jsp에 세션을 false로 하게 되면, 해당 페이지에서 세션을 시작하지 않는다.
<%@ page session = "false" %>
page session = | "true" | "false" |
세션이 이미 있을 때 | 둘 다 세션을 새로 만들지 않는다. | |
세션이 없을 때 | 생성 | 생성하지 않음 |
page session의 의미는 세션을 시작할 것인지를 의미하며, 기존 세션에 영향을 미치지는 않는다.
헷갈릴 수 있지만, false라는 것은 세션이 없을 때 세션을 시작하지 않는다는 것을 의미한다.
page session = "false"인 경우
sessionScope와 pageContext.session은 사용이 불가능하다.
<c:set var = "loginOutLink" value = "${sessionScope.id == null ? '/login/login' : '/login/logout'}" />
<c:set var = "loginOut" value = "${sessionScope.id == null ? 'Login' : 'Logout' }"/>
따라서 아래와 같이 변경해주어야 한다.
<c:set var = "loginOutLink" value =
"${pageContext.request.getSession(false).getAttribute("id") == null ?'/login/login' : '/login/logout'}"/>
<c:set var = "loginOut" value =
"${pageContext.request.getSession(false).getAttribute("id") == null ? 'Login' : 'Logout' }"/>
getSession(true)의 경우, session이 없으면 새로 생성하기 때문에 getSession(false)를 사용한다.
쿠키 vs 세션
쿠키 (Cookie) | 세션 (HttpSession) |
브라우저에 저장 | 서버에 저장 |
서버 부담 X | 서버 부담 O |
보안에 불리 | 보안에 유리 |
서버 다중화에 유리 | 서버 다중화에 불리 |
서버 다중화 :서버가 여러대 인 경우, 클라이언트가 요청을 했을 때 1번 서버에 세션 ID를 저장, 다음 요청은 2번 서버에 요청하게 될 경우, 세션 ID를 동기화 해주어야 한다.
따라서, 쿠키를 암호화 하는 방법도 사용함
참고 : https://interconnection.tistory.com/74
'프로그래밍 > Spring' 카테고리의 다른 글
[Spring] 13. 프로젝트 설정 (IntelliJ) (0) | 2023.08.25 |
---|---|
[Spring] 12.예외처리 (0) | 2023.08.24 |
[Spring] 10. STS 프로젝트 환경설정 (0) | 2023.08.06 |
[Spring] 9. 리다이렉트(redirect) & 포워드 (forward) (0) | 2023.08.05 |
[Spring] 8.@RequestParam & @ModelAttribute (0) | 2023.08.01 |