1. 웹에서의 상태 유지 기술
- HTTP 프로토콜은 상태 유지가 되지 않는 프로토콜
- 웹 브라우저(클라이언트)의 요청에 대한 응답을 하고 나면, 해당 클라이언트와의 연결을 지속하지 않음
- 상태 유지를 위해 Cookie와 Session 기술이 등장함
쿠키
- 사용자 컴퓨터에 저장
- 저장된 정보를 다른 사람 또는 시스템이 볼 수 있는 단점
- 유효시간이 지나면 사라짐
세션
- 서버에 저장
- 서버가 종료되거나 유효시간이 지나면 사라짐
쿠키의 동작 이해
1. 클라이언트가 WAS에게 요청
2. WAS는 쿠키 생성
3. WAS는 쿠키를 포함하여 클라이언트에게 응답함
4. 클라이언트는 다음 요청 시 쿠키를 포함하여 요청함
5. WAS는 쿠키를 받아서 검사함
세션의 동작 이해
1. 클라이언트가 WAS에게 요청
2. WAS는 세션 키 생성
3. 세션 키를 이용한 저장소 생성
4. 세션 키를 담은 쿠키 생성
5. WAS는 쿠키를 포함하여 클라이언트에게 응답함
6. 클라이언트는 다음 요청 시 세션 키를 저장하고 있는 쿠키를 포함하여 요청함
7. WAS는 쿠키의 세션 키를 이용해 이전에 생성한 저장소를 활용
2. 쿠키를 이용한 상태 유지
쿠키 정의
정의
- 클라이언트 단에 저장되는 작은 정보의 단위
- 클라이언트에서 생성하고 저장될 수 있고, 서버단에서 전송한 쿠키가 클라이언트에 저장될 수 있음
이용 방법
- 서버에서 클라이언트의 브라우저로 전송되어 사용자의 컴퓨터에 저장
- 저장된 쿠키는 다시 해당하는 웹 페이지에 접속할 때, 브라우저에서 서버로 쿠키를 전송
- 쿠키는 이름(name)과 값(value)으로 구성된 자료를 저장
- 이름 외에도 comment, path, max-age, expire, version, domain과 같은 추가적인 정보를 저장
제한
- 하나의 쿠키는 4KByte 크기로 제한
- 브라우저는 각각의 웹 사이트당 20개의 쿠키를 허용
- 모든 웹 사이트를 합쳐 최대 300개를 허용
- 그러므로 클라이언트당 쿠키의 최대 용량은 1.2MByte
jakarta.servlet.http.Cookie
1. 서버에서 쿠키 객체 생성, Response의 addCookie 메소드를 이용해 클라이언트에게 전송
Cookie cookie = new Cookiew(name, value);
response.addCookie(cookie);
2. 쿠키는 이름, 값의 쌍 정보로 입력하여 생성
- 쿠키의 이름은 알파벳과 숫자로만 구성되고, 쿠키 값은 공백, 괄호, 등호, 콤마, 콜론, 세미콜론 등은 포함 불가능
3. 클라이언트가 보낸 쿠키 정보 읽기
Cookie[] cookies = request.getCookies();
- 쿠키 값이 없으면 null이 반환됨
- 쿠키가 가지고 있는 getName()과 getValue() 메소드를 이용해서 원하는 쿠키 정보를 찾아 사용함
4. 클라이언트에게 쿠키 삭제 요청
- 클라이언트가 가진 쿠키를 서버가 삭제하는 명령은 없고, 같은 이름의 쿠키를 만들고 max-age를 0으로 설정해 쿠키를 전송함
Cookie cookie = new Cookiew(name, null);
cookie.setMaxAge(0);
response.addCookie(cookie);
5. 쿠키의 유효기간 설정
- setMaxAge()
- 인자의 유효기간을 나타내는 초 단위의 정수형
- 유효기간 == 0: 쿠키 삭제, 유효기간 < 0: 브라우저가 종료될 때 쿠키 삭제
6. Spring MVC에서의 Cookie 사용
- @CookieValue 애노테이션 사용
- 컨트롤러 메소드의 파라미터에서 @CookieValue를 사용해 원하는 쿠키 정보를 파라미터 변수에 담아 사용할 수 있음
ControllerMethod(@CookieValue(value = "쿠키이름", required=false, defaultValue="기본값") String 변수명)
3. 쿠키 실습하기
Guestbook 프로젝트 활용
- 방문한 수를 쿠키에 저장해서 보여주기
# GuestbookController.java
package com.example.guestbook.controller;
import com.example.guestbook.dto.Guestbook;
import com.example.guestbook.service.GuestbookService;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.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 java.util.ArrayList;
import java.util.List;
@Controller
public class GuestbookController {
@Autowired // Service를 사용하기 위해 주입
GuestbookService guestbookService;
@GetMapping("/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) {
for(Cookie cookie : cookies) {
if("count".equals(cookie.getName())) {
find = true;
value = cookie.getValue();
break;
}
}
}
if(!find) {
value = "1";
}
else {
try {
int i = Integer.parseInt(value);
value = Integer.toString(++i);
} catch (Exception e) {
value = "1";
}
}
Cookie cookie = new Cookie("count", value);
cookie.setMaxAge(60 * 60 * 24 * 365); // 1년 유지
cookie.setPath("/"); // 경로 이하에 모든 쿠키 적용
response.addCookie(cookie);
// start로 시작하는 방명록 목록 구하기
List<Guestbook> list = guestbookService.getGuestbooks(start);
// 전체 페이지 수 구하기
int count = guestbookService.getCount();
int pageCount = count / GuestbookService.LIMIT;
if(count % GuestbookService.LIMIT > 0) pageCount++;
// 페이지 수만큼 start의 값을 리스트로 저장
// 페이지 수가 3이라면, 0, 5, 10 저장
List<Integer> pageStartList = new ArrayList<>();
for(int i = 0; i < pageCount; i++) {
pageStartList.add(i * GuestbookService.LIMIT);
}
model.addAttribute("list", list);
model.addAttribute("count", count);
model.addAttribute("pageStartList", pageStartList);
model.addAttribute("cookieCount", value);
return "list"; // list.jsp로 넘겨줌
}
@PostMapping("/write")
public String write(@ModelAttribute Guestbook guestbook, HttpServletRequest request) {
String clientIp = request.getRemoteAddr();
// System.out.println("clientIp: " + clientIp);
guestbookService.addGuestbook(guestbook, clientIp);
return "redirect:list";
}
}
Spring MVC의 @CookieValue 이용하여 쿠키 값 읽어오기
# GuestbookController.java
@Controller
public class GuestbookController {
@Autowired // Service를 사용하기 위해 주입
GuestbookService guestbookService;
@GetMapping("/list")
public String list(@RequestParam(name = "start", required = false, defaultValue = "0") int start,
ModelMap model, @CookieValue(value="count", defaultValue = "0", required = true) String value,
HttpServletResponse response) {
// 방문자 수 집계를 위한 쿠키 처리
try {
int i = Integer.parseInt(value);
value = Integer.toString(++i);
} catch (Exception e) {
value = "1";
}
Cookie cookie = new Cookie("count", value);
cookie.setMaxAge(60 * 60 * 24 * 365); // 1년 유지
cookie.setPath("/"); // 경로 이하에 모든 쿠키 적용
response.addCookie(cookie);
4. 세션을 이용한 상태 유지
세션 정의
정의
- 클라이언트 별로 서버에 저장되는 정보
이용 방법
- 웹 클라이언트가 서버 측에 요청을 보내게 되면 서버는 클라이언트를 식별하는 session id를 생성함
- 서버는 session id를 이용해서 key와 value를 이용한 저장소인 HttpSession을 생성함
- 서버는 session id를 저장하고 있는 쿠키를 생성하여 클라이언트에 전송함
- 클라이언트는 서버측에 요청을 보낼때 session id를 가지고 있는 쿠키를 전송함
- 서버는 쿠키에 있는 session id를 이용해서 이전 요청에서 생성한 HttpSession을 찾고 사용함
세션 생성 및 얻기
HttpSession session = request.getSession();
HttpSession session = request.getSession(true);
- request의 getSession() 메소드는 서버에 생성된 세션이 있다면 세션을 반환하고, 없다면 새롭게 세션을 생성하여 반환함
- 새롭게 생성된 세션인지는 HttpSession이 가지고 있는 isNew() 메소드를 통해 알 수 이음
HttpSession session = request.getSession(false);
- request의 getSession() 메소드에 파라미터로 false를 전달하면, 이미 생성된 세션이 있다면 반환하고 없으면 null을 반환함
세션에 값 저장
session.setAttribute(String name, Object value)
- name과 value의 쌍으로 객체 Object를 저장하는 메소드
- 세션이 유지되는 동안 저장할 자료를 저장
세션에 값 조회
String value = (String)session.getAttribute(String name)
- 세션에 저장된 자료는 다시 getAttribute(String name) 메소드를 이용해 조회
- 반환 값은 Object 유형이므로 저장된 객체로 자료 유형 변환이 필요함
세션에 값 삭제
removeAttribute(String name) 메소드
- name 값에 해당하는 세션 정보를 삭제함
invalidate() 메소드
- 모든 세션 정보를 삭제함
jakarta.servlet.http.HttpSession
- 세션은 클라이언트가 서버에 접속하는 순간 생성
- 특별히 지정하지 않으면 세션의 유지 시간은 기본 값으로 30분 설정
- 세션의 유지 시간이란, 서버에 접속한 후 서버에 요청을 하지 않는 최대 시간
- 30분 이상 서버에 전혀 반응을 보이지 않으면 세션이 자동으로 끊어짐
- 세션 유지 시간은 web.xml 파일에서 설정 가능
<session-config>
<session-timeout>30</session-timeout>
</session-config>
5. 세션 실습하기
요구사항
- /guess로 요청을 하면 컴퓨터가 1부터 100 사이의 임의의 값 중의 하나를 맞춰보라는 메시지를 출력함
- 해당 값은 세션에 저장함
- 사용자는 1부터 100 사이의 값을 입력함
- 입력한 값이 세션 값보다 작으면, 입력한 값이 작다고 출력
- 입력한 값이 세션 값보다 크면, 입력한 값이 크다고 출력
- 입력한 값이 세션 값과 같다면 몇 번째에 맞췄다고 출력
# GuessNumberController.java
package com.example.guestbook.controller;
import jakarta.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, ModelMap model) {
String message = null;
if(number == null) {
session.setAttribute("count", 0);
session.setAttribute("randomNumber", (int)(Math.random()) * 100 + 1);
message = "내가 생각한 숫자를 맞춰보세요!";
}
else {
int count = (Integer)session.getAttribute("count");
int randomNumber = (Integer)session.getAttribute("randomNumber");
if(number < randomNumber) {
message = "입력한 값은 내가 생각하고 있는 숫자보다 작습니다.";
}
else if(number > randomNumber) {
message = "입력한 값은 내가 생각하고 있는 숫자보다 큽니다.";
}
else {
message = "OK " + ++count + "번째 맞췄습니다. 내가 생각한 숫자는 " + number + "입니다.";
session.removeAttribute("count");
session.removeAttribute("randomNumber");
}
model.addAttribute("message", message);
}
return "guess";
}
}
# guess.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ 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>
'강의 노트 > 웹 프로그래밍(풀스택)' 카테고리의 다른 글
[boostcourse] 5.6. Interceptor - BE (0) | 2023.07.16 |
---|---|
[boostcourse] 5.5. Spring MVC에서의 Session 사용하기 - BE (0) | 2023.07.11 |
[boostcourse] 3.11. Controller - BE (0) | 2023.07.09 |
[boostcourse] 3.10. Layered Architecture - BE (2) (0) | 2023.06.27 |
[boostcourse] 3.10. Layered Architecture - BE (1) (0) | 2023.06.27 |