Web

상태유지기술 (Cookie & Session)

WakaraNai 2021. 7. 8. 17:05
728x90
반응형

웹에서의 상태 유지 기술

  • HTTP프로토콜은 상태 유지가 안되는 프로토콜입니다.
    - 이전에 무엇을 했고, 지금 무엇을 했는지에 대한 정보를 갖고 있지 않습니다. (로그인, 장바구니 등)
    - 웹 브라우저(클라이언트)의 요청에 대한 응답을 하고 나면 해당 클라이언트와의 연결을 지속하지 않습니다. 
    • 이 클라이언트가 두 번째, 세 번째 요청을 할 때 누구인지 정보를 알게 하는 것
  • 상태 유지를 위해 Cookie와 Session기술이 등장합니다.

 

쿠키(Cookie)와 세션(Session)

  • 쿠키
    - 유지해야 할 정보를 사용자 컴퓨터에 저장
    • 저장된 정보를 다른 사람 또는 시스템이 볼 수 있는 단점 (공용 PC에서...)
    • 유효시간이 지나면 사라짐
  • 세션
    - 서버에 저장
    - 서버가 종료되거나 유효시간이 지나면 사라집니다.

 

쿠키 동작 이해

  • 클라이언트가 서버에 요청을 보냄
  • 이 때 유지해야 할 정보가 있다면 서버는 유지할 정보를 가지고 쿠키를 생성
    • 이 때 쿠키는 이름과 같은 값으로 구성되어 있고, 유지 시간 등의 정보를 가지게 됨
  • 만든 쿠키는 반드시 응답 결과에 포함되어서 클라이언트에게 전송됨
  • 그럼 클라이언트는 그 쿠키를 다른 요청에도 꼭 포함시켜서 서버에게 전송
  • 서버는 이전에 만들어둔 쿠키를 검사하여 확인
    • 있다면 이 사용자가 전에 접속한 사용자라든지, 이 사용자가 유지해야하는 정보를 알아낼 수 있음

 

세션 동작 이해

  • 클라이언트가 요청했을 때 유지해야할 정보가 있다면,
  • 서버는 세션키를 생성
  • 세션키를 이용한 저장소를 하나 생성. 여기에 유지해야하는 정보를 저장
  • 서버에서 세션키를 담은 쿠키 생성
    • 저장소만 만들어놓고 끝나버리면, 다시 클라이언트가 요청했을 때 이 클라이언트의 저장소가 어떤 곳인지 서버가 알아낼 길이 없음
    • 생성한 쿠키를 클라이언트에게 보내서, 클라이언트가 다시 요청할 때마다 그 쿠키를 가지고 오도록 함
    • 그럼 서버는 이 쿠키로부터 세션키를 얻어냄
    • 이 세션키에 해당하는 저장소를 찾아서 저장소의 정보를 사용. 또는 정보 저장.
  • HttepSession 객체 : 세션의 정보를 담기 위해 생성

 

 

 


쿠키

정의

클라이언트 단에 저장되는 작은 정보으 ㅣ단위

클라이언트에서 생성하고 저장될 수 있고, 서버 단에서 전송한 쿠키가 클라이언트에 저장될 수 있다.

 

이용방법

  • 서버에서 클라이언트의 브라우저로 전송되어 사용자의 컴퓨터에 저장
  • 저장된 쿠키는 다시 해당 웹 페이지에 접속 시, 브라우저에서 서버로 쿠키 전송
  • 쿠키는 이름(name)과 값(value) 쌍으로 정보를 저장
    • 이름-값 쌍 외에도,
    • 도메인(domain), 경로(path), 유효기간(max-age, expires), 보안(secure), HttpOnly 속성을 저장 할 수 있다

 

쿠키의 수와 크기에 제한

  • 브라우저 별로 제한 값을 다르게 가진다
  • 하나의 쿠키는 4K Byte 크기로 제한
  • 브라우저는 각각의 웹사이트 당 20개의 쿠키를 허용
  • 모든 웹사이트 합쳐 최대 300개 허용
  • 그러므로 클라이언트 당 쿠키의 최대 용량은 1.2M Byte

쿠키에는 사용자에 대한 사용 정보가 들어있다. 해당 정보가 많아질 수록 보안에 취약.

또다르 이유는...

 

javax.servlet.http.Cookie

서버에서 쿠키 생성, Response의 addCookie 메소드를 이용해 클라이언트에게 전송

  • 쿠키는 (이름, 값)의 쌍 정보를 입력하여 생성합니다.
  • 쿠키의 이름은 일반적으로 알파벳과 숫자, 언더바로 구성합니다. 정확한 정의를 알고 싶다면 RFC 6265(https://tools.ietf.org/html/rfc6265) 문서 [4.1.1 Syntax] 항목을 참조하세요.
Cookie cookie = new Cookie(이름, 값);
response.addCookie(cookie);

 

클라이언트가 보낸 쿠키 정보 읽기

  • 쿠키 값이 없으면 null이 반환됩니다.
    • NullPointException 처리 꼭 하기
  • Cookie가 가지고 있는 getName()과 getValue()메소드를 이용해서 원하는 쿠키정보를 찾아 사용합니다.
Cookie[] cookies = request.getCookies();

 

클라이언트에게 쿠키 삭제 요청

  • 쿠키를 삭제하는 명령은 없다. 쿠키는 웹 클라이언트가 관리하기 때문.
  • 똑같은 이름의 쿠키를 생성하여, 보내주기.
    • 즉, 클라이언트는 같은 이름의 쿠키를 가질 수 없다
  • maxAge가 0인 같은 이름의 쿠키를 전송합니다.
    • 유지시간을 0으로 주면 사라짐
Cookie cookie = new Cookie("이름", null);
cookie.setMaxAge(0);
response.addCookie(cookie);

 

쿠키의 유효기간 설정

  • 메소드 setMaxAge()
    - 인자는 유효기간을 나타내는 초 단위의 정수형
    - 만일 유효기간을 0으로 지정하면 쿠키의 삭제
    - 음수를 지정하면 브라우저가 종료될 때 쿠키가 삭제
  • 유효기간을 10분으로 지정하려면
    - cookie.setMaxAge(10 * 60); //초 단위 : 10분
    - 1주일로 지정하려면 (7*24*60*60)로 설정합니다.

javax.servlet.http.Cookie

 

Spring MVC에서 Cookie 사용

  • @CookieValue 애노테이션 사용
    - 컨트롤러 메소드의 파라미터에서 CookieValue애노테이션을 사용함으로써 원하는 쿠키정보를 파라미터 변수에 담아 사용할 수 습니다.
  • 컨트롤러 메소드
    • (@CookieValue(value="쿠키이름", required=false, defaultValue="기본값") String 변수명)

 

실습 1

guestbook 프로젝트를 이용

 

GuestbookController.java의 list 메소드를 다음과 같이 수정

package kr.or.connect.guestbook.controller;

import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

import kr.or.connect.guestbook.dto.Guestbook;
import kr.or.connect.guestbook.service.GuestbookService;

@Controller
public class GuestbookController {
	@Autowired
	GuestbookService guestbookService;
	
	@GetMapping(path="/list")
	public String list(@RequestParam(name="start", required=false, defaultValue="0") int start,
			ModelMap model,
			HttpServletRequest request,
			HttpServletResponse response) {
		// 클라이언트가 서버에 요청 했을 때
		// 실제 이 클라이언트가 내가 원하는 쿠키를 가지고 있는가? 먼저 확인하기
		String value = null;
		boolean find = false;
		Cookie[] cookies = request.getCookies(); // 클라이언트로부터 쿠키 배열을 얻옴
		if (cookies != null) { // 반드시 null 값 처리
			for (Cookie cookie : cookies) {
				if ("count".equals(cookie.getName())) {
					find = true;
					value = cookie.getValue();
                    break; // 쿠키를 하나만 찾고 그만 찾아도 된다면 적어주기 
				}
			}
		}
		
		if (!find) // 처음 요청이 들어왔을 때
			value = "1"; // 쿠키에 문자열 value를 넣을 수 있따
		else { // 쿠키를 찾았다면
			try {
				int i = Integer.parseInt(value);
				value = Integer.toString(++i);
			} catch (Exception ex) {
				value = "1";
			}
		}
		
        // 변경된 쿠키 값을 클라이언트 쪽에 적용하게 하려면
        // 반드시 쿠키는 매번 새로 만들어서 보내주어야 함
		Cookie cookie = new Cookie("count", value); // 쿠키 생성
		cookie.setMaxAge(60*60*24*365); // 1년 동안 유지 (유지 시간)
		// -1을 주면 브라우저가 닫힐 때 쿠키도 없어짐. 브라우저를 껐다 켰을 때 다시 1 값으로 시작되게 됨
		cookie.setPath("/"); //  / 경로 이하에 모두 쿠키 적용
		response.addCookie(cookie); // 응답 결과 보내주기
	
		생략
		
		// jsp에서 사용하도록 model에 넣어주기
		model.addAttribute("list", list);
		model.addAttribute("count", count);
		model.addAttribute("pageStartList", pageStartList);
		model.addAttribute("cookieCount", value);
		
		return "list";  // list.jsp 뷰로 열기
	}

 

 

list.jsp에 cookieCount 추가

	<br> 방명록 전체 수 : ${count }, 방문한 수 : ${cookieCount }

 

 

실습 2 - @CookieValue

GuestbookController.java의 list 메소드의 입력값으로

@CookieValue 추가 후 불필요한 부분 삭제하기

@GetMapping(path="/list")
	public String list(@RequestParam(name="start", required=false, defaultValue="0") int start,
			ModelMap model,
			@CookieValue(value="count", defaultValue="0") String value,
			HttpServletRequest request,
			HttpServletResponse response) {
		
		/*
		// 클라이언트가 서버에 요청 했을 때
		// 실제 이 클라이언트가 내가 원하는 쿠키를 가지고 있는가? 먼저 확인하기
		String value = null;
		boolean find = false;
		Cookie[] cookies = request.getCookies(); // 클라이언트로부터 쿠키 배열을 얻옴
		if (cookies != null) { // 반드시 null 값 처리
			for (Cookie cookie : cookies) {
				if ("count".equals(cookie.getName())) {
					find = true;
					value = cookie.getValue();
				}
			}
		}
		
		if (!find) // 처음 요청이 들어왔을 때
			value = "1"; // 쿠키에 문자열 value를 넣을 수 있따
		else { // 쿠키를 찾았다면*/
			try { // CookieValue를 찾고 null 값일 때의 지정값에 대해 안 적어도 된다
				int i = Integer.parseInt(value);
				value = Integer.toString(++i);
			} catch (Exception ex) {
				value = "1";
			}
		//}
        
        
        생략

 

 

 


 

세션(Session)

정의

클라이언트 별로 서버에 저장되는 정보

로그인 정보, 장바구니 정보 등

 

이용 방법

  • 웹 클라이언트가 서버측에 요청을 보내게 되면
  • 서버는 이 클라이언트를 식별하는 session id를 생성   - 클라이언트마다 생성
  • 서버는 session id를 이용해서 key와 value를 이용한 저장소인 HttpSession을 생성
  • 서버는 session id를 저장한 쿠키를 생성하여 클라이언트에 전송
  • 클라이언트는 서버측에 요청을 보낼때 session id를 가진 쿠키를 전송
  • 서버는 쿠키에 있는 session id를 이용해서 그 전 요청에서 생성한 HttpSession을 찾고 사용
    • HttpSession 객체는 서버가 알아서 만든다.

 

세션 생성 및 얻기

  • request의 getSession()메소드는
    • 서버에 생성된 세션이 있다면 세션을 반환하고 없다면 새롭게 세션을 생성하여 반환합니다.
    • 기본값은 true
    • 있다면 그 세션을 반환하고 없다면 null을 반환
  • 새롭게 생성된 세션인지는 HttpSession의 isNew()메소드를 통해 알 수 있습니다.
HttpSession session = request.getSession();
HttpSession session = request.getSession(true);
  • request의 getSession()메소드에 파라미터로 false를 전달하면, 이미 생성된 세션이 있다면 반환하고 없으면 null을 반환합니다.
HttpSession session = request.getSession(false);

 

세션에 값 저장

setAttribute(String name, Object value)

  • name과 value의 쌍으로 객체 Object를 저장하는 메소드입니다.
  • 세션이 유지되는 동안 저장할 자료를 저장합니다.
session.setAttribute(이름, 값)

 

세션에 값 조회

getAttribute(String name) 메소드

  • 세션에 저장된 자료는 다시 getAttribute(String name) 메소드를 이용해 조회합니다.
  • 반환 값은 Object 유형이므로 저장된 객체로 자료형 변환이 필요합니다.
  • 메소드 setAttribute()에 이용한 name인 “id”를 알고 있다면 바로 다음과 같이 바로 조회합니다.
String value = (String) session.getAttribute("id");

세션에 값 삭제

  • removeAttribute(String name) 메소드
    - name 값에 해당하는 세션 정보를 삭제합니다.
  • invalidate() 메소드
    - 모든 세션 정보를 삭제합니다.

  javax.servlet.http.HttpSession
javax.servlet.http.HttpSession

 

javax.servlet.http.HttpSession

세션은 클라이언트가 서버에 접속하는 순간 생성

  • 특별히 지정하지 않으면 세션의 유지 시간은 기본 값으로 30분 설정합니다.
  • 세션의 유지 시간이란 서버에 접속한 후 서버에 요청을 하지 않는 최대 시간입니다.
  • 30분 이상 서버에 전혀 반응을 보이지 않으면 세션이 자동으로 끊어집니다.
  • 이 세션 유지 시간은 web.xml파일에서 설정 가능합니다.
<session-config>
  <session-timeout>30</session-timeout>
</session-config>

 

 

실습

  • /guess로 요청을 하면 컴퓨터가 1부터 100 사이의 임의의 값 중의 하나를 맞춰보라는 메시지가 출력합니다.
  • 해당 값은 세션에 저장합니다.
  • 사용자는 1부터 100 사이의 값을 입력합니다.
  • 입력한 값이 세션 값보다 작으면, 입력한 값이 작다고 출력합니다.
  • 입력한 값이 세션 값보다 크면, 입력한 값이 크다고 출력합니다.
  • 입력한 값이 세션 값과 같다면 몇 번째에 맞췄다고 출력합니다.

 

guestbook 프로젝트의

 controller 패키지에

GuessNumberController.java 추가하기

package kr.or.connect.guestbook.controller;

import javax.servlet.http.HttpSession;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class GuessNumberController {
	
	@GetMapping("/guess")
	public String guess(@RequestParam(name="number", required=false) Integer number,
			HttpSession session, // Spring이 대신 생성
			ModelMap model) {  // 값을 저장하기 위 해 ModelMap 객체 생성
		String message = null;
		
		// get방식으로 /guess 를 요청하는데 
		// 파라미터 number가 없을 경우에는 
		// session에 count를 0으로 randomNumber엔 1~100사이의 값을 저장
		if (number == null) { // 처음 요청이 들어왔을 때
			session.setAttribute("count", 0); 
			session.setAttribute("randomNumber", (int)(Math.random()*100)+1);
			message = "내가 생각한 숫자를 맞춰보세요";
		} else { 
			int count = (Integer) session.getAttribute("count"); // Object로 반환되므로 형변환 필수ㅁ
			int randomNumber = (Integer) session.getAttribute("randomNumber");

			// 사용자가 보내준 값과 세션에 저장된 값을 비교
			// number파라미터가 있을 경우 
			// 세션에서 값을 읽어들인 후, number와 세션에 저장된 값을 비교합니다.
			// 값을 비교해서 작거나 크다면 카운트를 1 증가시켜주고
			// 값이 같다면 세션 정보를 삭제합니다.
			// 각 상황에 맞는 메시지를 message변수에 저장을 한 후 jsp에게 전달하기 위해서 ModelMap의 addAttribute메소드를 통해 전달하게 됩니다.
			if(number < randomNumber) {
				message = "입력한 값은 내가 생각하고 있는 숫자보다 작습니다.";
				session.setAttribute("count", ++count);
			}else if(number > randomNumber) {
				message = "입력한 값은 내가 생각하고 있는 숫자보다 큽니다.";
				session.setAttribute("count", ++count);
			}else {
				message = "OK " + ++count + " 번째 맞췄습니다. 내가 생각한 숫자는 " + number + " 입니다.";
				session.removeAttribute("count"); // 세션 삭제
				session.removeAttribute("randomNumber");
			}
		
		}
		
		model.addAttribute("message", message);
		return "guess";
	}
}

 

view 폴더에 guess.jsp 생성

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    
    <!-- JSLT 라이브러리 추가 -->
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 

      
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>숫자 맞추기 게임</title>
</head>
<body>
	<h1> 숫자 맞추기 게임.</h1>
	<hr>
	<h3>${message }</h3>
	
	<c:if test="${sessionScope.count != null}">
		<form method="get" action="guess">
			1부터 100사이의 숫자로 맞춰주세요.<br>
			<input type="text" name="number"><br>
			<input type="submit" value="확인">
		</form>
	</c:if>
	
	<a href="guess">게임 다시 시작하기.</a>
</body>
</html>
 

세션 존재 여부에 따라 form이 출력되도록 했기에 보이지 않음

 

참고

쿠키는 이름과 값이 모두 문자열 이지만,

세션의 키값은 문자열이고 값은 객체로 저장할 수 있다

728x90
반응형

'Web' 카테고리의 다른 글

[Postman] API 테스트 사이트  (0) 2021.11.06