Web/Spring

[Spring] Ch02.Spring MVC (23) 쿠키(Cookie)란 ~ (26) 세션(Session) - 실습 (2)

OptimizerStart 2025. 6. 8. 22:50

쿠키 Cookie

정적 리소스 파일 위치

  • webapp > resources 폴더 내

ex. css 파일

Cookie ⭕ 암기

정의

  • 클라이언트 식별 기술. 신분증

특징

  • Map <name, value> 쌍으로 구성된 작은 정보
  • 쿠키의 이름은 알파벳과 숫자만 이용. 아스키 문자만 저장 가능 (URLEncoder.encode())
    • 혹은 암호화해야함. 신용 때문.
  • 값은 공백과 세미콜론을 제외한 아스키 가능
  • 한글은 URL인코딩/디코딩 필요
  • 쿠키 가져올 때 값 없으면 null 반환

장점

  • 누구인지 매번 알려주지 않아도됨. 

원리

  • 서버가 쿠키를 만들고 클라이언트에게 전송.
    • 응답 헤더에 쿠키 추가
  • 클라이언트가 쿠키를 브라우저에 저장 
  • 유효기간 지나면 자동 삭제
  • 서버에 요청 시 domain, path (하위 경로 포함✅ 예시.)가 일치하는 경우에만 쿠키 정보 <id, value> 가 요청 헤더에 자동으로 붙여 서버로 전송

ex. 도서관별 카드

사용 방법

  • 요청이 오면 서버에서 실행되는 코드

1. 쿠키 생성

Cookie cookie = new Cookie("id", "asdf"); // 쿠키 생성 (name, value) = ("id", "asdf")
cookie.setMaxAge(60*60*24); // 유효 기간 설정 (초) -> 24시간
response.addCookie(cookie); // 응답 헤더에 쿠키 추가 (Set-Cookie 에 들어가는 값)

 

2. 쿠키 삭제

  • 쿠키의 유효기간을 0으로 바꿈
  • name 지정 필수, value는 선택 (””)
Cookie cookie = new Cookie("id", ""); // 변경할 쿠키와 같은 이름 쿠키 생성
	// 값은 안넣어도됨. name 중요 
cookie.setMaxAge(0); // 유효기간을 0으로 설정(삭제)
response.addCookie(cookie; // 응답 헤더에 쿠키 추가 

 

3. 쿠키 변경

  • setValue()로 값변경
Cookie cookie = new Cookie("id", ""); // 변경할 쿠키와 같은 이름 쿠키 생성
cookie.setValue(URLEncoder.encode("남궁성")); // 값 변경 ("" -> "남궁성" )
cookie.setDomain("www.fastcampus.co.kr"); // 도메인 변경 
cookie.setPath("/ch2"); // 경로 변경
cookie.setMaxAge(60*60*24*7); // 유효기간 변경 24시간 -> 7일

response.addCookie(cookie); // 응답 헤더에 쿠키 추가 

 

4. 쿠키 읽어오기

Cookies[] cookies = reqeust.getCookies() // 쿠키 읽기 null or value 

for(Cookie cookie : cookies) {
	String name = cookie.getName(); 
	String value = cookie.getValue();
	
	System.out.printf("[cookie]name=%s, value=%s\%n", name, value);
}

서버가 클라이언트로부터 받은 요청 헤더

  • path와 domain이 일치하는 쿠키를 Cookie 헤더에 값 추가 (여러 개일 수 있음, ;로 구분)

상대, 절대 시간

  • 브라우저 별로 유효기간 설정 방법 달라 절대시간, 상대 시간 둘다 표기
    • 서버와 클라이언트의 시간이 맞는다는 보장 X. → 상대 시간 사용 추천
  • 상대 시간 maxAge 생성 후 지정초 만에 폐기
  • 절대 시간 dd-month-year hh:mm:ss gmt

종류

  • domain
  • path
    • 클라이언트가 지정하지 않으면, 서버의 응답 가지고 판단함
  • maxAge

🔵설계 - 결과 만들고 시작

개발자 도구에서 Elements 에서 최종 결과를 html로 만들고, 어떻게 만들면 될지 고민

  1. 쿠키 있으면
    • id 보여주고
    • 아이디 기억 체크
  2. 쿠키 없으면
    • X

@CookieValue(”key이름”)

  • key이름 가진 cookie 값을 옆 매개변수로 넘겨줌
public String login(HttpServletRequest request, @CookieValue("id") String id, String pwd, boolean rememberId, String toURL,  HttpServletResponse response) throws Exception{

 

세션 Session

 

세션

정의

쿠키를 이용해 서로 관련된 여러 요청과 응답 쌍인 HTTP Tx을 하나로 묶은 것

  • 요청 간에는 서로 독립적 = 관계가 없음

서버의 개인 저장소

ex. 로그인 부터 로그아웃하기까지

특징

  • 브라우저마다 개별 저장소(session 객체)를 서버에 제공cf. 쿠키 : 같은 PC 여도 다른 브라우저이면, 다른 Session ID를 서버로부터 받음
  • → 왜? 사용자를 쿠키로 식별하고, 쿠키는 브라우저에 저장하니까
  • 세션 : 서버 메모리, 쿠키 : 브라우저에 저장
  • 서버와 브라우저는 일대일 대응
  • 같은 Session ID를 가지면, 같은 세션으로 그룹화 됨.
    • 같은 Session 객체 저장소를 사용(R,W)

독립적 = 서로 관련 없음

사용 방법

  1. 세션 종료 / 삭제 -2 ⭕
    • 수동 종료
      • invalidate() 로그아웃. 즉시 종료
      • setMaxInactiveInterval(60 * 30) 초단위 (30분). 예약 종료
      HttpSession session = request.getSession();
      session.invalidate(); // 1.세션 즉시 종료
      session.setMaxInactiveInterval(30* 60); // 2.예약 종료 (30분후)
      
    • 자동 종료 Timeout ⭕ 필수
      • ex. 사용자가 실수로 로그아웃 안한 경우
      • session timeout 발생하면 서버가 응답에서 새로운 session id를 생성 후 클라이언트에게 줌
        • 기존에 사용하던 session 객체 저장소 사용 불가.
      <session-config>
      		<session-timeout>30<session-timeout> 분단위
      </session-config>
      
  2. 세션 객체 얻기 R, W
    • request.getSession() 서버가 클라이언트의 요청 중 Cookie의 JSESSIONID와 동일한 값을 가진 서버의 Session 객체를 찾는다. 반환값으로 일치하는 세션 객체 주소 반환
HttpSession session = request.getSession(); 
session.setAttribute("id", "asdf"); // 세션 객체에 name, value 쌍을 저장

클라이언트의 요청의 sessionid와 동일한 id를 가진 세션 객체 얻기

 

 

  • HttpSession session
    • request 객체가 아닌 메서드 매개변수에 HttpSession 작성해서 session 객체 얻을 수 있음
	@GetMapping("logout")
	public String logout(HttpSession session) { // request 객체 통해서가 아닌 직접 세션 객체를 얻을 수 있음. 
	//public String logout(HttpServletRequest request) {
		// 1. 세션 종료
		//HttpSession session = request.getSession();
		session.invalidate();
		// 2. 홈으로 이동
		return "redirect:/";
	}

 

 

[참고] 브라우저 별로 Cookie 같은 걸로 다른걸로 판단하는 기준

같은 브라우저, 같은 사용자 프로필 공유 같은 cookie store ID 사용
같은 브라우저, 다른 프로필 분리 프로필마다 별도 폴더 & 쿠키 DB
일반 창 ↔ 시크릿(Incognito) 창 분리 시크릿은 메모리 전용 임시 저장소 (developer.chrome.com)
크롬 ↔ 브레이브 (또는 파이어폭스) 분리 실행 파일·프로필 경로가 다름
도메인/서브도메인, Path 가 다른 경우 설정에 따라 다름 쿠키 속성 Domain·Path 조건 불일치 시 전송 안 됨 (developer.mozilla.org)

 

 

원리

  • 클라이언트가 서버에 요청을 하면 서버가 무조건 session 객체(저장소) 생성
    • session 객체 마다 sessionID 가짐
  • 서버에서 sessionID가 담긴 Cookie를 생성하고 응답헤더에 저장하여 클라이언트에게 응답 보냄
  • 클라이언트가 서버에게서 온 Set-Cookie 내용 보고 브라우저에 Cookie를 저장
  • 클라이언트가 요청 보낼 때 Cookie에 서버에게 받은 값 목록을 포함해 요청
  • 서버는 클라이언트가 보낸 요청의 Cookie 값 목록을 확인 후 , JSESSIONID의 값이 같으면 같은 브라우저로 판담함. 같을 때 동일 Session 객체 저장소사용(R,W) 가능

[참고 ] Cookie 헤더

  • Set-Cookie 서버 → 브라우저 : 이값을 브라우저에 쿠키로 저장
  • Cookie 브라우저 → 서버 : 클라이언트가 서버에게 받은 쿠키 목록을 이름=값 쌍만 모아 전송

서버가 응답헤더에 생성한 쿠키 Set-Cookie - SessionId가 쿠키에 저장
클라이언트가 서버에게 보낼 요청에 Cookie 포함한 내용

  • 서버가 클라이언트의 요청에서 Cookie의 값 목록 중 JSESSIONID를 보고 같은 브라우저에서 온 요청인지 확인함.

 

 

활용

  • 로그인: 세션을 얻어서 세션에 id가 있는지 확인

세션 관련 메서드

  • String getId() 세션 ID 반환
  • long getLastAccessedTime()
  • void invalidate() 세션 즉시 종료. 세션 객체 제거
  • void setMaxInactiveInerval(int interval) 세션 예약 종료

 

 

standard manager

  • 세션 객체 저장소를 관리
  • 사용자에게 요청이 오면 standard manager가 세션 객체 무조건 생성 후 세션 ID로 만든 쿠키 응답으로 반환
  • 세션 끝나면 자동으로 세션 객체 자동 제거
  • 주의사항
    • 최소한의 데이터를 세션 객체에 저장해야함. → 오랜 시간 동안 세션 객체가 메모리에 존재할 수 있기 때문.

쿠키 vs. 세션

쿠키 세션

브라우저에 저장 서버에 저장
서버 부담 x 서버 부담 o
보안 불리 보안 유리
서버 다중화 유리 서버 다중화 불리

두 세션 객체 동기화 떄문에 서버 다중화 불리하여 쿠키에 세션을 저장하는 것이 유리

 

 

사용자가 쿠키를 허용하지 않는 경우 🟥 실습

  • 요청 URL 에 세션 ID를 GET 방식으로 붙임 <c:url value=”url”>
  • 서버의 경우 클라이언트가 쿠키 허용 여부를 몰라서 첫요청에 JESSION ID를 2가지 방식으로 제공
    1. URL 주소 뒤에 JSESSIONID
      • 모든 링크에 붙여짐 (url 재작성)
        서버가 클라이언트에게<c:url>태그로 session id를 태그에 포함해 전달한 결과
    2. Set-Cookie 헤더에 JSESSIONID 추가
      1. 요청에 Cookie 있으면 서버는 <c:url>에 JSESSIONID를 추가하지 않음. 
      2. 브라우저가 쿠키 허용 시 서버 응답의 Set-Cookie: JSESSIONID=값; Path=/경로; 를 보고 다음 요청부터 요청 헤더에 Cookie: JSESSIONID=값이 자동으로 붙음

모든 URL에 <c:url> 태그 써야하는 이유

  • 세션 객체 새로 생성 = Session ID 계속 바뀜
  • 같은 브라우저의 요청에 대해서도 session 객체가 요청 마다 생성

 

JSESSIONID

  • Java의 세션 식별용 쿠키 이름

세션 시작할까?

  • 세션 유지 기간은 가능한 짧게 가져가야 함.

방법

  • session=true
  • session=false ⭕ 중요

session=false

  1. 세션이 필요없는 JSP 화면
  2. session=false가 기존 세션에 영향을 미치치 않음( 세션 끊게 만들지 않음!)
<%@ page session="false"%>

특징

  • sessionScope와 pageContext.session은 사용 불가.
  • sessionScpoe.id를 pageContext.request.getSession(false).getAttribute(”id)로 변경해야함.
  • STS에서 에러라고 표시해도 무시하면 됨

참고

  • getSession(true)는 session이 없는 경우 session을 새로 생성하기 때문에, session이 없어도 새로 생성하지 않도록 getSession(false)를 사용

 

  공통점 차이점 예시
session=true (디폴트) 세션 있을 때
세션 안 만듦.
세션 없을 때 세션 생성 O  
session=false 세션 없을 때 세션 생성 X 홈페이지, 로그인 페이지
  • session=false는 새로운 session을 생성하지 않는다 의미
  • session이 시작되면 중간에 session=false를 만나도 session이 중단되지 않음

실습

🔵 설계. 처리해야할 로직을 단계별로 적고, 단계별로 코드를 적어보면서 구현. 에러 잡기 좋음

1) 세션에 id를 저장

리팩토링

  • session에 저장된 (”id”, “asdf”)를 읽을 때 ${sessionScope.id} 로 읽음
  • html 안에서 변하는 값에 ${} 넣기
    • <c:choose> if-else x
<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<c:set var="loginOut" value="${sessionScope.id == null ? 'Login' : 'Logout'}"/>
<c:set var="loginOutLink" value="${sessionScope.id == null ? '/login' : '/logout'}"/>
<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
    <title>fastcampus</title>
    <link rel="stylesheet" href="<c:url value='/css/menu.css'/>">
</head>
<body>
<div id="menu">
	<ul>
	    <li id="logo">fastcampus</li>
	    <li><a href="<c:url value='/home'/>">Home</a></li>
	    <li><a href="<c:url value='/boardPrac/list'/>">Board</a></li>
	    <li><a href="<c:url value='/loginPrac/${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 BOARD!!!</h1>
	<h1>This is BOARD!!!</h1>
	<h1>This is BOARD!!!</h1>
	<h1>This is BOARD!!!</h1>
	<h1>This is BOARD!!!</h1>
</div>
</body>
</html>

 

 

2) 게시판 버튼 클릭 했다는 것을 기억하고 로그인 후 게시판 화면으로 이동

페이지 이동 시 URL이 어디서 왔고, 어디로 가는지 작성

from request.getHeader(”referer”)

  • referer 요청자. 이 링크를 타고 여기로 오기 직전 사용자가 보고 있던 페이지 URL
  • 직접 주소창에 입력하거나 즐겨찾기에서 바로 열었을 경우 Referer: null or “” 빈문자열
  • 보안 Referer-Policy 헤더

to request.getRequestURI()

  • 사용자가 현재 요청한 URI

어느 페이지의 url이 정보를 가지고 있는지 확인

  • hidden <input> 만들어 데이터를 전송1

🟡 개발. <input type=”text”>는 개발 시 보는 용도, 값 확인 후 <input type=”hidden”>으로 변경

 

질문

 

[BoardControllerPrac.java] 에서 toURL을 toURL=http://localhost:8080/ch2/boardPrac/list 로 전달했는데 [LoginControllerPrac.java]에서 redirect 시 에러가 발생 안하나?
redirect:/login/login 이면 context root가 자동으로 붙어지는데 말이다.

 

[BoardControllerPrac.java

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

[LoginControllerPrac.java]

	// 원래 board를 클릭해서 login 성공한 경우는 /board/list로 전달
	toURL = toURL == null || toURL.equals("") ? "/home" : toURL;
	return "redirect:" + toURL;

[<http://localhost:8080/ch2/home>] -> GET[/ch2/boardPrac/list]소요시간 = 2ms
[<http://localhost:8080/ch2/home>] -> GET[/ch2/loginPrac/login]소요시간 = 7ms
id=asdf
pwd=1234
toURL=http://localhost:8080/ch2/boardPrac/list
아이디 기억 해제인 경우 
[<http://localhost:8080/ch2/loginPrac/login?toURL=http://localhost:8080/ch2/boardPrac/list>] -> POST[/ch2/loginPrac/login]소요시간 = 4ms
[<http://localhost:8080/ch2/loginPrac/login?toURL=http://localhost:8080/ch2/boardPrac/list>] -> GET[/ch2/boardPrac/list]소요시간 = 11ms

 

 

 

대답

redirect + 절대경로이면 에러 발생하지 않음
클라이언트에게 절대경로를 Location(재요청 url)으로 302 응답을한다.

// 클라이언트에 Location: http://localhost:8080/ch2/board 로 302 응답
return "redirect:http://localhost:8080/ch2/board";

 

redirect + 상대경로

컨텍스트 루트부터 시작하는 상대 경로 (/ch2/board) 를 주면 Spring은 앞에 자동으로 request.getContextPath() 를 붙여서 내부 리다이렉트

// 클라이언트에 Location: <contextPath>/ch2/board 로 302 응답
return "redirect:/ch2/board";